class Thor::Runner
:nodoc:
def self.banner(task)
def self.banner(task) "thor " + task.formatted_usage(self, false) end
def self.exit_on_failure?
def self.exit_on_failure? true end
def display_klasses(with_modules=false, show_internal=false, klasses=Thor::Base.subclasses)
it shows a table with information extracted from the yaml file.
Display information about the given klasses. If with_module is given,
def display_klasses(with_modules=false, show_internal=false, klasses=Thor::Base.subclasses) klasses -= [Thor, Thor::Runner, Thor::Group] unless show_internal raise Error, "No Thor tasks available" if klasses.empty? show_modules if with_modules && !thor_yaml.empty? # Remove subclasses klasses.dup.each do |klass| klasses -= Thor::Util.thor_classes_in(klass) end list = Hash.new { |h,k| h[k] = [] } groups = klasses.select { |k| k.ancestors.include?(Thor::Group) } # Get classes which inherit from Thor (klasses - groups).each { |k| list[k.namespace] += k.printable_tasks(false) } # Get classes which inherit from Thor::Base groups.map! { |k| k.printable_tasks(false).first } list["root"] = groups # Order namespaces with default coming first list = list.sort{ |a,b| a[0].sub(/^default/, '') <=> b[0].sub(/^default/, '') } list.each { |n, tasks| display_tasks(n, tasks) unless tasks.empty? } end
def display_tasks(namespace, list) #:nodoc:
def display_tasks(namespace, list) #:nodoc: list.sort!{ |a,b| a[0] <=> b[0] } say shell.set_color(namespace, :blue, true) say "-" * namespace.size print_table(list, :truncate => true) say end
def help(meth=nil)
Override Thor#help so it can give information about any class and any method.
def help(meth=nil) if meth && !self.respond_to?(meth) initialize_thorfiles(meth) klass, task = Thor::Util.namespace_to_thor_class_and_task(meth) # Send mapping -h because it works with Thor::Group too klass.start(["-h", task].compact, :shell => self.shell) else super end end
def initialize_thorfiles(relevant_to=nil, skip_lookup=false)
skip_lookup true.
described in thorfiles. This look up can be skipped by suppliying
By default, it also traverses the current path until find Thor files, as
in the thor_root instead of loading them all.
Load the thorfiles. If relevant_to is supplied, looks for specific files
def initialize_thorfiles(relevant_to=nil, skip_lookup=false) thorfiles(relevant_to, skip_lookup).each do |f| Thor::Util.load_thorfile(f) unless Thor::Base.subclass_files.keys.include?(File.expand_path(f)) end end
def install(name)
def install(name) initialize_thorfiles # If a directory name is provided as the argument, look for a 'main.thor' # task in said directory. begin if File.directory?(File.expand_path(name)) base, package = File.join(name, "main.thor"), :directory contents = open(base).read else base, package = name, :file contents = open(name).read end rescue OpenURI::HTTPError raise Error, "Error opening URI '#{name}'" rescue Errno::ENOENT raise Error, "Error opening file '#{name}'" end say "Your Thorfile contains:" say contents unless options["force"] return false if no?("Do you wish to continue [y/N]?") end as = options["as"] || begin first_line = contents.split("\n")[0] (match = first_line.match(/\s*#\s*module:\s*([^\n]*)/)) ? match[1].strip : nil end unless as basename = File.basename(name) as = ask("Please specify a name for #{name} in the system repository [#{basename}]:") as = basename if as.empty? end location = if options[:relative] || name =~ /^http:\/\// name else File.expand_path(name) end thor_yaml[as] = { :filename => Digest::MD5.hexdigest(name + as), :location => location, :namespaces => Thor::Util.namespaces_in_content(contents, base) } save_yaml(thor_yaml) say "Storing thor file in your system repository" destination = File.join(thor_root, thor_yaml[as][:filename]) if package == :file File.open(destination, "w") { |f| f.puts contents } else FileUtils.cp_r(name, destination) end thor_yaml[as][:filename] # Indicate success end
def installed
def installed initialize_thorfiles(nil, true) display_klasses(true, options["internal"]) end
def list(search="")
def list(search="") initialize_thorfiles search = ".*#{search}" if options["substring"] search = /^#{search}.*/i group = options[:group] || "standard" klasses = Thor::Base.subclasses.select do |k| (options[:all] || k.group == group) && k.namespace =~ search end display_klasses(false, false, klasses) end
def method_missing(meth, *args)
Thor::Runner is then responsable for finding the task in all classes.
If a task is not found on Thor::Runner, method missing is invoked and
def method_missing(meth, *args) meth = meth.to_s initialize_thorfiles(meth) klass, task = Thor::Util.namespace_to_thor_class_and_task(meth) args.unshift(task) if task klass.start(args, :shell => shell) end
def save_yaml(yaml)
Save the yaml file. If none exists in thor root, creates one.
def save_yaml(yaml) yaml_file = File.join(thor_root, "thor.yml") unless File.exists?(yaml_file) FileUtils.mkdir_p(thor_root) yaml_file = File.join(thor_root, "thor.yml") FileUtils.touch(yaml_file) end File.open(yaml_file, "w") { |f| f.puts yaml.to_yaml } end
def show_modules #:nodoc:
def show_modules #:nodoc: info = [] labels = ["Modules", "Namespaces"] info << labels info << [ "-" * labels[0].size, "-" * labels[1].size ] thor_yaml.each do |name, hash| info << [ name, hash[:namespaces].join(", ") ] end print_table info say "" end
def thor_root
def thor_root Thor::Util.thor_root end
def thor_yaml
def thor_yaml @thor_yaml ||= begin yaml_file = File.join(thor_root, "thor.yml") yaml = YAML.load_file(yaml_file) if File.exists?(yaml_file) yaml || {} end end
def thorfiles(relevant_to=nil, skip_lookup=false)
5. c:\ <-- no Thorfiles found!
4. c:\Documents and Settings
3. c:\Documents and Settings\james
2. c:\Documents and Settings\james\dev
1. c:\Documents and Settings\james\dev\thor
Suppose we start at c:\Documents and Settings\james\dev\thor ...
3. /Users/wycats <-- we find a Thorfile here, so we stop
2. /Users/wycats/dev
1. /Users/wycats/dev/thor
If we start at /Users/wycats/dev/thor ...
==== Example
Thorfiles can override them.
We also ensure that system-wide Thorfiles are loaded first, so local
directory of your system. If at any time we find a Thor file, we stop.
Finds Thorfiles by traversing from your current directory down to the root
def thorfiles(relevant_to=nil, skip_lookup=false) thorfiles = [] unless skip_lookup Pathname.pwd.ascend do |path| thorfiles = Thor::Util.globs_for(path).map { |g| Dir[g] }.flatten break unless thorfiles.empty? end end files = (relevant_to ? thorfiles_relevant_to(relevant_to) : Thor::Util.thor_root_glob) files += thorfiles files -= ["#{thor_root}/thor.yml"] files.map! do |file| File.directory?(file) ? File.join(file, "main.thor") : file end end
def thorfiles_relevant_to(meth)
namespaces registered.
will load all thor files in the thor.yaml that has "foo" e "foo:bar"
Load thorfiles relevant to the given method. If you provide "foo:bar" it
def thorfiles_relevant_to(meth) lookup = [ meth, meth.split(":")[0...-1].join(":") ] files = thor_yaml.select do |k, v| v[:namespaces] && !(v[:namespaces] & lookup).empty? end files.map { |k, v| File.join(thor_root, "#{v[:filename]}") } end
def uninstall(name)
def uninstall(name) raise Error, "Can't find module '#{name}'" unless thor_yaml[name] say "Uninstalling #{name}." FileUtils.rm_rf(File.join(thor_root, "#{thor_yaml[name][:filename]}")) thor_yaml.delete(name) save_yaml(thor_yaml) puts "Done." end
def update(name)
def update(name) raise Error, "Can't find module '#{name}'" if !thor_yaml[name] || !thor_yaml[name][:location] say "Updating '#{name}' from #{thor_yaml[name][:location]}" old_filename = thor_yaml[name][:filename] self.options = self.options.merge("as" => name) filename = install(thor_yaml[name][:location]) unless filename == old_filename File.delete(File.join(thor_root, old_filename)) end end
def version
def version require 'thor/version' say "Thor #{Thor::VERSION}" end