diff options
author | Andre Arko <andre@arko.net> | 2011-06-11 21:24:02 -0700 |
---|---|---|
committer | Andre Arko <andre@arko.net> | 2011-06-11 21:24:02 -0700 |
commit | 8b1af1ae85c5e0cef8d752a530f336975163a262 (patch) | |
tree | 3f9bc815a02034ae45871726916d065a0fb39da1 /lib | |
parent | c78d29f4fc68ab2ed09a642b146d66e7dcf5cbdc (diff) | |
download | bundler-8b1af1ae85c5e0cef8d752a530f336975163a262.tar.gz |
Update vendored thor to 7416806146f252c589cfa0f7c154393bc498090a
Diffstat (limited to 'lib')
28 files changed, 913 insertions, 85 deletions
diff --git a/lib/bundler/vendor/thor.rb b/lib/bundler/vendor/thor.rb index d9399f78..dabc2473 100755..100644 --- a/lib/bundler/vendor/thor.rb +++ b/lib/bundler/vendor/thor.rb @@ -18,6 +18,23 @@ class Thor end end + # Registers another Thor subclass as a command. + # + # ==== Parameters + # klass<Class>:: Thor subclass to register + # command<String>:: Subcommand name to use + # usage<String>:: Short usage for the subcommand + # description<String>:: Description for the subcommand + def register(klass, subcommand_name, usage, description, options={}) + if klass <= Thor::Group + desc usage, description, options + define_method(subcommand_name) { invoke klass } + else + desc usage, description, options + subcommand subcommand_name, klass + end + end + # Defines the usage and the description of the next task. # # ==== Parameters @@ -252,8 +269,7 @@ class Thor # the namespace should be displayed as arguments. # def banner(task, namespace = nil, subcommand = false) - base = File.basename($0).split(" ").first - "#{base} #{task.formatted_usage(self, $thor_runner, subcommand)}" + "#{basename} #{task.formatted_usage(self, $thor_runner, subcommand)}" end def baseclass #:nodoc: @@ -305,7 +321,6 @@ class Thor def help(task = nil, subcommand = true); super; end RUBY end - end include Thor::Base diff --git a/lib/bundler/vendor/thor/actions.rb b/lib/bundler/vendor/thor/actions.rb index bfccbf8f..3712c5f4 100755..100644 --- a/lib/bundler/vendor/thor/actions.rb +++ b/lib/bundler/vendor/thor/actions.rb @@ -1,10 +1,12 @@ require 'fileutils' require 'uri' require 'thor/core_ext/file_binary_read' - -Dir[File.join(File.dirname(__FILE__), "actions", "*.rb")].each do |action| - require action -end +require 'thor/actions/create_file' +require 'thor/actions/create_link' +require 'thor/actions/directory' +require 'thor/actions/empty_directory' +require 'thor/actions/file_manipulation' +require 'thor/actions/inject_into_file' class Thor module Actions @@ -158,13 +160,23 @@ class Thor # def inside(dir='', config={}, &block) verbose = config.fetch(:verbose, false) + pretend = options[:pretend] say_status :inside, dir, verbose shell.padding += 1 if verbose @destination_stack.push File.expand_path(dir, destination_root) - FileUtils.mkdir_p(destination_root) unless File.exist?(destination_root) - FileUtils.cd(destination_root) { block.arity == 1 ? yield(destination_root) : yield } + # If the directory doesnt exist and we're not pretending + if !File.exist?(destination_root) && !pretend + FileUtils.mkdir_p(destination_root) + end + + if pretend + # In pretend mode, just yield down to the block + block.arity == 1 ? yield(destination_root) : yield + else + FileUtils.cd(destination_root) { block.arity == 1 ? yield(destination_root) : yield } + end @destination_stack.pop shell.padding -= 1 if verbose @@ -210,7 +222,7 @@ class Thor # # ==== Parameters # command<String>:: the command to be executed. - # config<Hash>:: give :verbose => false to not log the status. Specify :with + # config<Hash>:: give :verbose => false to not log the status, :capture => true to hide to output. Specify :with # to append an executable to command executation. # # ==== Example @@ -231,7 +243,10 @@ class Thor end say_status :run, desc, config.fetch(:verbose, true) - `#{command}` unless options[:pretend] + + unless options[:pretend] + config[:capture] ? `#{command}` : system("#{command}") + end end # Executes a ruby script (taking into account WIN32 platform quirks). @@ -251,8 +266,9 @@ class Thor # ==== Parameters # task<String>:: the task to be invoked # args<Array>:: arguments to the task - # config<Hash>:: give :verbose => false to not log the status. Other options - # are given as parameter to Thor. + # config<Hash>:: give :verbose => false to not log the status, :capture => true to hide to output. + # Other options are given as parameter to Thor. + # # # ==== Examples # @@ -266,12 +282,13 @@ class Thor config = args.last.is_a?(Hash) ? args.pop : {} verbose = config.key?(:verbose) ? config.delete(:verbose) : true pretend = config.key?(:pretend) ? config.delete(:pretend) : false + capture = config.key?(:capture) ? config.delete(:capture) : false args.unshift task args.push Thor::Options.to_switches(config) command = args.join(' ').strip - run command, :with => :thor, :verbose => verbose, :pretend => pretend + run command, :with => :thor, :verbose => verbose, :pretend => pretend, :capture => capture end protected diff --git a/lib/bundler/vendor/thor/actions/create_file.rb b/lib/bundler/vendor/thor/actions/create_file.rb index 5541ad5f..ed5973a4 100755..100644 --- a/lib/bundler/vendor/thor/actions/create_file.rb +++ b/lib/bundler/vendor/thor/actions/create_file.rb @@ -18,7 +18,7 @@ class Thor # "vhost.name = #{hostname}" # end # - # create_file "config/apach.conf", "your apache config" + # create_file "config/apache.conf", "your apache config" # def create_file(destination, *args, &block) config = args.last.is_a?(Hash) ? args.pop : {} @@ -27,7 +27,7 @@ class Thor end alias :add_file :create_file - # AddFile is a subset of Template, which instead of rendering a file with + # CreateFile is a subset of Template, which instead of rendering a file with # ERB, it gets the content from the user. # class CreateFile < EmptyDirectory #:nodoc: diff --git a/lib/bundler/vendor/thor/actions/create_link.rb b/lib/bundler/vendor/thor/actions/create_link.rb new file mode 100644 index 00000000..1975644a --- /dev/null +++ b/lib/bundler/vendor/thor/actions/create_link.rb @@ -0,0 +1,57 @@ +require 'thor/actions/create_file' + +class Thor + module Actions + + # Create a new file relative to the destination root from the given source. + # + # ==== Parameters + # destination<String>:: the relative path to the destination root. + # source<String|NilClass>:: the relative path to the source root. + # config<Hash>:: give :verbose => false to not log the status. + # :: give :symbolic => false for hard link. + # + # ==== Examples + # + # create_link "config/apache.conf", "/etc/apache.conf" + # + def create_link(destination, *args, &block) + config = args.last.is_a?(Hash) ? args.pop : {} + source = args.first + action CreateLink.new(self, destination, source, config) + end + alias :add_link :create_link + + # CreateLink is a subset of CreateFile, which instead of taking a block of + # data, just takes a source string from the user. + # + class CreateLink < CreateFile #:nodoc: + attr_reader :data + + # Checks if the content of the file at the destination is identical to the rendered result. + # + # ==== Returns + # Boolean:: true if it is identical, false otherwise. + # + def identical? + exists? && File.identical?(render, destination) + end + + def invoke! + invoke_with_conflict_check do + FileUtils.mkdir_p(File.dirname(destination)) + # Create a symlink by default + config[:symbolic] ||= true + File.unlink(destination) if exists? + if config[:symbolic] + File.symlink(render, destination) + else + File.link(render, destination) + end + end + given_destination + end + + end + end +end diff --git a/lib/bundler/vendor/thor/actions/directory.rb b/lib/bundler/vendor/thor/actions/directory.rb index 717508eb..dc238939 100755..100644 --- a/lib/bundler/vendor/thor/actions/directory.rb +++ b/lib/bundler/vendor/thor/actions/directory.rb @@ -21,7 +21,7 @@ class Thor # directory "doc" # # It will create a doc directory in the destination with the following - # files (assuming that the app_name is "blog"): + # files (assuming that the `app_name` method returns the value "blog"): # # doc/ # components/ @@ -70,7 +70,7 @@ class Thor lookup = config[:recursive] ? File.join(source, '**') : source lookup = File.join(lookup, '{*,.[a-z]*}') - Dir[lookup].each do |file_source| + Dir[lookup].sort.each do |file_source| next if File.directory?(file_source) file_destination = File.join(given_destination, file_source.gsub(source, '.')) file_destination.gsub!('/./', '/') diff --git a/lib/bundler/vendor/thor/actions/empty_directory.rb b/lib/bundler/vendor/thor/actions/empty_directory.rb index 484cb820..484cb820 100755..100644 --- a/lib/bundler/vendor/thor/actions/empty_directory.rb +++ b/lib/bundler/vendor/thor/actions/empty_directory.rb diff --git a/lib/bundler/vendor/thor/actions/file_manipulation.rb b/lib/bundler/vendor/thor/actions/file_manipulation.rb index e9494626..ad049b3c 100755..100644 --- a/lib/bundler/vendor/thor/actions/file_manipulation.rb +++ b/lib/bundler/vendor/thor/actions/file_manipulation.rb @@ -30,6 +30,28 @@ class Thor end end + # Links the file from the relative source to the relative destination. If + # the destination is not given it's assumed to be equal to the source. + # + # ==== Parameters + # source<String>:: the relative path to the source root. + # destination<String>:: the relative path to the destination root. + # config<Hash>:: give :verbose => false to not log the status. + # + # ==== Examples + # + # link_file "README", "doc/README" + # + # link_file "doc/README" + # + def link_file(source, *args, &block) + config = args.last.is_a?(Hash) ? args.pop : {} + destination = args.first || source + source = File.expand_path(find_in_source_paths(source.to_s)) + + create_link destination, source, config + end + # Gets the content at the given address and places it at the given relative # destination. If a block is given instead of destination, the content of # the url is yielded and used as location. @@ -51,7 +73,7 @@ class Thor config = args.last.is_a?(Hash) ? args.pop : {} destination = args.first - source = File.expand_path(find_in_source_paths(source.to_s)) unless source =~ /^http\:\/\// + source = File.expand_path(find_in_source_paths(source.to_s)) unless source =~ /^https?\:\/\// render = open(source) {|input| input.binmode.read } destination ||= if block_given? @@ -80,13 +102,13 @@ class Thor # def template(source, *args, &block) config = args.last.is_a?(Hash) ? args.pop : {} - destination = args.first || source + destination = args.first || source.sub(/\.tt$/, '') source = File.expand_path(find_in_source_paths(source.to_s)) context = instance_eval('binding') create_file destination, nil, config do - content = ERB.new(::File.binread(source), nil, '-').result(context) + content = ERB.new(::File.binread(source), nil, '-', '@output_buffer').result(context) content = block.call(content) if block content end @@ -110,7 +132,7 @@ class Thor FileUtils.chmod_R(mode, path) unless options[:pretend] end - # Prepend text to a file. Since it depends on inject_into_file, it's reversible. + # Prepend text to a file. Since it depends on insert_into_file, it's reversible. # # ==== Parameters # path<String>:: path of the file to be changed @@ -119,19 +141,20 @@ class Thor # # ==== Example # - # prepend_file 'config/environments/test.rb', 'config.gem "rspec"' + # prepend_to_file 'config/environments/test.rb', 'config.gem "rspec"' # - # prepend_file 'config/environments/test.rb' do + # prepend_to_file 'config/environments/test.rb' do # 'config.gem "rspec"' # end # - def prepend_file(path, *args, &block) + def prepend_to_file(path, *args, &block) config = args.last.is_a?(Hash) ? args.pop : {} config.merge!(:after => /\A/) - inject_into_file(path, *(args << config), &block) + insert_into_file(path, *(args << config), &block) end + alias_method :prepend_file, :prepend_to_file - # Append text to a file. Since it depends on inject_into_file, it's reversible. + # Append text to a file. Since it depends on insert_into_file, it's reversible. # # ==== Parameters # path<String>:: path of the file to be changed @@ -140,20 +163,21 @@ class Thor # # ==== Example # - # append_file 'config/environments/test.rb', 'config.gem "rspec"' + # append_to_file 'config/environments/test.rb', 'config.gem "rspec"' # - # append_file 'config/environments/test.rb' do + # append_to_file 'config/environments/test.rb' do # 'config.gem "rspec"' # end # - def append_file(path, *args, &block) + def append_to_file(path, *args, &block) config = args.last.is_a?(Hash) ? args.pop : {} config.merge!(:before => /\z/) - inject_into_file(path, *(args << config), &block) + insert_into_file(path, *(args << config), &block) end + alias_method :append_file, :append_to_file # Injects text right after the class definition. Since it depends on - # inject_into_file, it's reversible. + # insert_into_file, it's reversible. # # ==== Parameters # path<String>:: path of the file to be changed @@ -172,7 +196,7 @@ class Thor def inject_into_class(path, klass, *args, &block) config = args.last.is_a?(Hash) ? args.pop : {} config.merge!(:after => /class #{klass}\n|class #{klass} .*\n/) - inject_into_file(path, *(args << config), &block) + insert_into_file(path, *(args << config), &block) end # Run a regular expression replacement on a file. @@ -225,5 +249,22 @@ class Thor end alias :remove_dir :remove_file + private + attr_accessor :output_buffer + def concat(string) + @output_buffer.concat(string) + end + + def capture(*args, &block) + with_output_buffer { block.call(*args) } + end + + def with_output_buffer(buf = '') #:nodoc: + self.output_buffer, old_buffer = buf, output_buffer + yield + output_buffer + ensure + self.output_buffer = old_buffer + end end end diff --git a/lib/bundler/vendor/thor/actions/inject_into_file.rb b/lib/bundler/vendor/thor/actions/inject_into_file.rb index 812c2e7d..c48cfab5 100755..100644 --- a/lib/bundler/vendor/thor/actions/inject_into_file.rb +++ b/lib/bundler/vendor/thor/actions/inject_into_file.rb @@ -10,19 +10,19 @@ class Thor # destination<String>:: Relative path to the destination root # data<String>:: Data to add to the file. Can be given as a block. # config<Hash>:: give :verbose => false to not log the status and the flag - # for injection (:after or :before) or :force => true for + # for injection (:after or :before) or :force => true for # insert two or more times the same content. - # + # # ==== Examples # - # inject_into_file "config/environment.rb", "config.gem :thor", :after => "Rails::Initializer.run do |config|\n" + # insert_into_file "config/environment.rb", "config.gem :thor", :after => "Rails::Initializer.run do |config|\n" # - # inject_into_file "config/environment.rb", :after => "Rails::Initializer.run do |config|\n" do + # insert_into_file "config/environment.rb", :after => "Rails::Initializer.run do |config|\n" do # gems = ask "Which gems would you like to add?" # gems.split(" ").map{ |gem| " config.gem :#{gem}" }.join("\n") # end # - def inject_into_file(destination, *args, &block) + def insert_into_file(destination, *args, &block) if block_given? data, config = block, args.shift else @@ -30,6 +30,7 @@ class Thor end action InjectIntoFile.new(self, destination, data, config) end + alias_method :inject_into_file, :insert_into_file class InjectIntoFile < EmptyDirectory #:nodoc: attr_reader :replacement, :flag, :behavior @@ -76,12 +77,16 @@ class Thor protected def say_status(behavior) - status = if flag == /\A/ - behavior == :invoke ? :prepend : :unprepend - elsif flag == /\z/ - behavior == :invoke ? :append : :unappend + status = if behavior == :invoke + if flag == /\A/ + :prepend + elsif flag == /\z/ + :append + else + :insert + end else - behavior == :invoke ? :inject : :deinject + :subtract end super(status, config[:verbose]) diff --git a/lib/bundler/vendor/thor/base.rb b/lib/bundler/vendor/thor/base.rb index 6412ace6..65399ffb 100755..100644 --- a/lib/bundler/vendor/thor/base.rb +++ b/lib/bundler/vendor/thor/base.rb @@ -94,8 +94,6 @@ class Thor end module ClassMethods - attr_accessor :debugging - def attr_reader(*) #:nodoc: no_tasks { super } end @@ -384,14 +382,29 @@ class Thor # script.invoke(:task, first_arg, second_arg, third_arg) # def start(given_args=ARGV, config={}) - self.debugging = given_args.delete("--debug") config[:shell] ||= Thor::Base.shell.new dispatch(nil, given_args.dup, nil, config) rescue Thor::Error => e - debugging ? (raise e) : config[:shell].error(e.message) + ENV["THOR_DEBUG"] == "1" ? (raise e) : config[:shell].error(e.message) exit(1) if exit_on_failure? end + # Allows to use private methods from parent in child classes as tasks. + # + # ==== Paremeters + # names<Array>:: Method names to be used as tasks + # + # ==== Examples + # + # public_task :foo + # public_task :foo, :bar, :baz + # + def public_task(*names) + names.each do |name| + class_eval "def #{name}(*); super end" + end + end + def handle_no_task_error(task) #:nodoc: if $thor_runner raise UndefinedTaskError, "Could not find task #{task.inspect} in #{namespace.inspect} namespace." @@ -531,6 +544,13 @@ class Thor false end + # + # The basename of the program invoking the thor class. + # + def basename + File.basename($0).split(' ').first + end + # SIGNATURE: Sets the baseclass. This is where the superclass lookup # finishes. def baseclass #:nodoc: diff --git a/lib/bundler/vendor/thor/core_ext/file_binary_read.rb b/lib/bundler/vendor/thor/core_ext/file_binary_read.rb index d6af7e44..d6af7e44 100755..100644 --- a/lib/bundler/vendor/thor/core_ext/file_binary_read.rb +++ b/lib/bundler/vendor/thor/core_ext/file_binary_read.rb diff --git a/lib/bundler/vendor/thor/core_ext/hash_with_indifferent_access.rb b/lib/bundler/vendor/thor/core_ext/hash_with_indifferent_access.rb index 40d201d9..40d201d9 100755..100644 --- a/lib/bundler/vendor/thor/core_ext/hash_with_indifferent_access.rb +++ b/lib/bundler/vendor/thor/core_ext/hash_with_indifferent_access.rb diff --git a/lib/bundler/vendor/thor/core_ext/ordered_hash.rb b/lib/bundler/vendor/thor/core_ext/ordered_hash.rb index 27fea5bb..27fea5bb 100755..100644 --- a/lib/bundler/vendor/thor/core_ext/ordered_hash.rb +++ b/lib/bundler/vendor/thor/core_ext/ordered_hash.rb diff --git a/lib/bundler/vendor/thor/error.rb b/lib/bundler/vendor/thor/error.rb index 9746b882..9746b882 100755..100644 --- a/lib/bundler/vendor/thor/error.rb +++ b/lib/bundler/vendor/thor/error.rb diff --git a/lib/bundler/vendor/thor/group.rb b/lib/bundler/vendor/thor/group.rb new file mode 100644 index 00000000..3dbab98a --- /dev/null +++ b/lib/bundler/vendor/thor/group.rb @@ -0,0 +1,273 @@ +require 'thor/base' + +# Thor has a special class called Thor::Group. The main difference to Thor class +# is that it invokes all tasks at once. It also include some methods that allows +# invocations to be done at the class method, which are not available to Thor +# tasks. +class Thor::Group + class << self + # The description for this Thor::Group. If none is provided, but a source root + # exists, tries to find the USAGE one folder above it, otherwise searches + # in the superclass. + # + # ==== Parameters + # description<String>:: The description for this Thor::Group. + # + def desc(description=nil) + case description + when nil + @desc ||= from_superclass(:desc, nil) + else + @desc = description + end + end + + # Prints help information. + # + # ==== Options + # short:: When true, shows only usage. + # + def help(shell) + shell.say "Usage:" + shell.say " #{banner}\n" + shell.say + class_options_help(shell) + shell.say self.desc if self.desc + end + + # Stores invocations for this class merging with superclass values. + # + def invocations #:nodoc: + @invocations ||= from_superclass(:invocations, {}) + end + + # Stores invocation blocks used on invoke_from_option. + # + def invocation_blocks #:nodoc: + @invocation_blocks ||= from_superclass(:invocation_blocks, {}) + end + + # Invoke the given namespace or class given. It adds an instance + # method that will invoke the klass and task. You can give a block to + # configure how it will be invoked. + # + # The namespace/class given will have its options showed on the help + # usage. Check invoke_from_option for more information. + # + def invoke(*names, &block) + options = names.last.is_a?(Hash) ? names.pop : {} + verbose = options.fetch(:verbose, true) + + names.each do |name| + invocations[name] = false + invocation_blocks[name] = block if block_given? + + class_eval <<-METHOD, __FILE__, __LINE__ + def _invoke_#{name.to_s.gsub(/\W/, '_')} + klass, task = self.class.prepare_for_invocation(nil, #{name.inspect}) + + if klass + say_status :invoke, #{name.inspect}, #{verbose.inspect} + block = self.class.invocation_blocks[#{name.inspect}] + _invoke_for_class_method klass, task, &block + else + say_status :error, %(#{name.inspect} [not found]), :red + end + end + METHOD + end + end + + # Invoke a thor class based on the value supplied by the user to the + # given option named "name". A class option must be created before this + # method is invoked for each name given. + # + # ==== Examples + # + # class GemGenerator < Thor::Group + # class_option :test_framework, :type => :string + # invoke_from_option :test_framework + # end + # + # ==== Boolean options + # + # In some cases, you want to invoke a thor class if some option is true or + # false. This is automatically handled by invoke_from_option. Then the + # option name is used to invoke the generator. + # + # ==== Preparing for invocation + # + # In some cases you want to customize how a specified hook is going to be + # invoked. You can do that by overwriting the class method + # prepare_for_invocation. The class method must necessarily return a klass + # and an optional task. + # + # ==== Custom invocations + # + # You can also supply a block to customize how the option is giong to be + # invoked. The block receives two parameters, an instance of the current + # class and the klass to be invoked. + # + def invoke_from_option(*names, &block) + options = names.last.is_a?(Hash) ? names.pop : {} + verbose = options.fetch(:verbose, :white) + + names.each do |name| + unless class_options.key?(name) + raise ArgumentError, "You have to define the option #{name.inspect} " << + "before setting invoke_from_option." + end + + invocations[name] = true + invocation_blocks[name] = block if block_given? + + class_eval <<-METHOD, __FILE__, __LINE__ + def _invoke_from_option_#{name.to_s.gsub(/\W/, '_')} + return unless options[#{name.inspect}] + + value = options[#{name.inspect}] + value = #{name.inspect} if TrueClass === value + klass, task = self.class.prepare_for_invocation(#{name.inspect}, value) + + if klass + say_status :invoke, value, #{verbose.inspect} + block = self.class.invocation_blocks[#{name.inspect}] + _invoke_for_class_method klass, task, &block + else + say_status :error, %(\#{value} [not found]), :red + end + end + METHOD + end + end + + # Remove a previously added invocation. + # + # ==== Examples + # + # remove_invocation :test_framework + # + def remove_invocation(*names) + names.each do |name| + remove_task(name) + remove_class_option(name) + invocations.delete(name) + invocation_blocks.delete(name) + end + end + + # Overwrite class options help to allow invoked generators options to be + # shown recursively when invoking a generator. + # + def class_options_help(shell, groups={}) #:nodoc: + get_options_from_invocations(groups, class_options) do |klass| + klass.send(:get_options_from_invocations, groups, class_options) + end + super(shell, groups) + end + + # Get invocations array and merge options from invocations. Those + # options are added to group_options hash. Options that already exists + # in base_options are not added twice. + # + def get_options_from_invocations(group_options, base_options) #:nodoc: + invocations.each do |name, from_option| + value = if from_option + option = class_options[name] + option.type == :boolean ? name : option.default + else + name + end + next unless value + + klass, task = prepare_for_invocation(name, value) + next unless klass && klass.respond_to?(:class_options) + + value = value.to_s + human_name = value.respond_to?(:classify) ? value.classify : value + + group_options[human_name] ||= [] + group_options[human_name] += klass.class_options.values.select do |option| + base_options[option.name.to_sym].nil? && option.group.nil? && + !group_options.values.flatten.any? { |i| i.name == option.name } + end + + yield klass if block_given? + end + end + + # Returns tasks ready to be printed. + def printable_tasks(*) + item = [] + item << banner + item << (desc ? "# #{desc.gsub(/\s+/m,' ')}" : "") + [item] + end + + def handle_argument_error(task, error) #:nodoc: + raise error, "#{task.name.inspect} was called incorrectly. Are you sure it has arity equals to 0?" + end + + protected + + # The method responsible for dispatching given the args. + def dispatch(task, given_args, given_opts, config) #:nodoc: + if Thor::HELP_MAPPINGS.include?(given_args.first) + help(config[:shell]) + return + end + + args, opts = Thor::Options.split(given_args) + opts = given_opts || opts + + if task + new(args, opts, config).invoke_task(all_tasks[task]) + else + new(args, opts, config).invoke_all + end + end + + # The banner for this class. You can customize it if you are invoking the + # thor class by another ways which is not the Thor::Runner. + def banner + "#{basename} #{self_task.formatted_usage(self, false)}" + end + + # Represents the whole class as a task. + def self_task #:nodoc: + Thor::DynamicTask.new(self.namespace, class_options) + end + + def baseclass #:nodoc: + Thor::Group + end + + def create_task(meth) #:nodoc: + tasks[meth.to_s] = Thor::Task.new(meth, nil, nil, nil, nil) + true + end + end + + include Thor::Base + + protected + + # Shortcut to invoke with padding and block handling. Use internally by + # invoke and invoke_from_option class methods. + def _invoke_for_class_method(klass, task=nil, *args, &block) #:nodoc: + with_padding do + if block + case block.arity + when 3 + block.call(self, klass, task) + when 2 + block.call(self, klass) + when 1 + instance_exec(klass, &block) + end + else + invoke klass, task, *args + end + end + end +end diff --git a/lib/bundler/vendor/thor/invocation.rb b/lib/bundler/vendor/thor/invocation.rb index 6315dd42..6315dd42 100755..100644 --- a/lib/bundler/vendor/thor/invocation.rb +++ b/lib/bundler/vendor/thor/invocation.rb diff --git a/lib/bundler/vendor/thor/parser.rb b/lib/bundler/vendor/thor/parser.rb index 57a3f6e1..57a3f6e1 100755..100644 --- a/lib/bundler/vendor/thor/parser.rb +++ b/lib/bundler/vendor/thor/parser.rb diff --git a/lib/bundler/vendor/thor/parser/argument.rb b/lib/bundler/vendor/thor/parser/argument.rb index 68c2e086..68c2e086 100755..100644 --- a/lib/bundler/vendor/thor/parser/argument.rb +++ b/lib/bundler/vendor/thor/parser/argument.rb diff --git a/lib/bundler/vendor/thor/parser/arguments.rb b/lib/bundler/vendor/thor/parser/arguments.rb index 07850836..888ef692 100755..100644 --- a/lib/bundler/vendor/thor/parser/arguments.rb +++ b/lib/bundler/vendor/thor/parser/arguments.rb @@ -28,7 +28,7 @@ class Thor @switches = arguments arguments.each do |argument| - if argument.default + if argument.default != nil @assigns[argument.human_name] = argument.default elsif argument.required? @non_assigned_required << argument @@ -94,7 +94,7 @@ class Thor hash = {} while current_is_value? && peek.include?(?:) - key, value = shift.split(':') + key, value = shift.split(':',2) hash[key] = value end hash diff --git a/lib/bundler/vendor/thor/parser/option.rb b/lib/bundler/vendor/thor/parser/option.rb index 496756db..c8f20b13 100755..100644 --- a/lib/bundler/vendor/thor/parser/option.rb +++ b/lib/bundler/vendor/thor/parser/option.rb @@ -37,7 +37,7 @@ class Thor # string (--foo=value) or booleans (just --foo). # # By default all options are optional, unless :required is given. - # + # def self.parse(key, value) if key.is_a?(Array) name, *aliases = key diff --git a/lib/bundler/vendor/thor/parser/options.rb b/lib/bundler/vendor/thor/parser/options.rb index 2315b136..37062d70 100755..100644 --- a/lib/bundler/vendor/thor/parser/options.rb +++ b/lib/bundler/vendor/thor/parser/options.rb @@ -53,7 +53,9 @@ class Thor @pile = args.dup while peek - if current_is_switch? + match, is_switch = current_is_switch? + + if is_switch case shift when SHORT_SQ_RE unshift($1.split('').map { |f| "-#{f}" }) @@ -68,7 +70,7 @@ class Thor switch = normalize_switch(switch) option = switch_option(switch) @assigns[option.human_name] = parse_peek(switch, option) - elsif current_is_switch_formatted? + elsif match @unknown << shift else shift @@ -92,15 +94,17 @@ class Thor # def current_is_switch? case peek - when LONG_RE, SHORT_RE, EQ_RE, SHORT_NUM - switch?($1) - when SHORT_SQ_RE - $1.split('').any? { |f| switch?("-#{f}") } + when LONG_RE, SHORT_RE, EQ_RE, SHORT_NUM + [true, switch?($1)] + when SHORT_SQ_RE + [true, $1.split('').any? { |f| switch?("-#{f}") }] + else + [false, false] end end - def switch_formatted?(arg) - case arg + def current_is_switch_formatted? + case peek when LONG_RE, SHORT_RE, EQ_RE, SHORT_NUM, SHORT_SQ_RE true else @@ -108,12 +112,8 @@ class Thor end end - def current_is_switch_formatted? - switch_formatted? peek - end - def switch?(arg) - switch_option(arg) || @shorts.key?(arg) + switch_option(normalize_switch(arg)) end def switch_option(arg) @@ -127,7 +127,7 @@ class Thor # Check if the given argument is actually a shortcut. # def normalize_switch(arg) - @shorts.key?(arg) ? @shorts[arg] : arg + (@shorts[arg] || arg).tr('_', '-') end # Parse boolean values which can be given as --foo=true, --foo or --no-foo. @@ -169,6 +169,5 @@ class Thor @non_assigned_required.delete(option) send(:"parse_#{option.type}", switch) end - end end diff --git a/lib/bundler/vendor/thor/rake_compat.rb b/lib/bundler/vendor/thor/rake_compat.rb new file mode 100644 index 00000000..0d0757fd --- /dev/null +++ b/lib/bundler/vendor/thor/rake_compat.rb @@ -0,0 +1,66 @@ +require 'rake' + +class Thor + # Adds a compatibility layer to your Thor classes which allows you to use + # rake package tasks. For example, to use rspec rake tasks, one can do: + # + # require 'thor/rake_compat' + # + # class Default < Thor + # include Thor::RakeCompat + # + # Spec::Rake::SpecTask.new(:spec) do |t| + # t.spec_opts = ['--options', "spec/spec.opts"] + # t.spec_files = FileList['spec/**/*_spec.rb'] + # end + # end + # + module RakeCompat + def self.rake_classes + @rake_classes ||= [] + end + + def self.included(base) + # Hack. Make rakefile point to invoker, so rdoc task is generated properly. + rakefile = File.basename(caller[0].match(/(.*):\d+/)[1]) + Rake.application.instance_variable_set(:@rakefile, rakefile) + self.rake_classes << base + end + end +end + +class Object #:nodoc: + alias :rake_task :task + alias :rake_namespace :namespace + + def task(*args, &block) + task = rake_task(*args, &block) + + if klass = Thor::RakeCompat.rake_classes.last + non_namespaced_name = task.name.split(':').last + + description = non_namespaced_name + description << task.arg_names.map{ |n| n.to_s.upcase }.join(' ') + description.strip! + + klass.desc description, task.comment || non_namespaced_name + klass.send :define_method, non_namespaced_name do |*args| + Rake::Task[task.name.to_sym].invoke(*args) + end + end + + task + end + + def namespace(name, &block) + if klass = Thor::RakeCompat.rake_classes.last + const_name = Thor::Util.camel_case(name.to_s).to_sym + klass.const_set(const_name, Class.new(Thor)) + new_klass = klass.const_get(const_name) + Thor::RakeCompat.rake_classes << new_klass + end + + rake_namespace(name, &block) + Thor::RakeCompat.rake_classes.pop + end +end diff --git a/lib/bundler/vendor/thor/runner.rb b/lib/bundler/vendor/thor/runner.rb new file mode 100644 index 00000000..0d9e3c05 --- /dev/null +++ b/lib/bundler/vendor/thor/runner.rb @@ -0,0 +1,309 @@ +require 'thor' +require 'thor/group' +require 'thor/core_ext/file_binary_read' + +require 'fileutils' +require 'open-uri' +require 'yaml' +require 'digest/md5' +require 'pathname' + +class Thor::Runner < Thor #:nodoc: + map "-T" => :list, "-i" => :install, "-u" => :update, "-v" => :version + + # 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.find_class_and_task_by_namespace(meth) + klass.start(["-h", task].compact, :shell => self.shell) + else + super + end + end + + # If a task is not found on Thor::Runner, method missing is invoked and + # Thor::Runner is then responsable for finding the task in all classes. + # + def method_missing(meth, *args) + meth = meth.to_s + initialize_thorfiles(meth) + klass, task = Thor::Util.find_class_and_task_by_namespace(meth) + args.unshift(task) if task + klass.start(args, :shell => self.shell) + end + + desc "install NAME", "Install an optionally named Thor file into your system tasks" + method_options :as => :string, :relative => :boolean, :force => :boolean + 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) {|input| input.read } + else + base, package = name, :file + contents = open(name) {|input| input.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 =~ /^https?:\/\// + 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 + + desc "version", "Show Thor version" + def version + require 'thor/version' + say "Thor #{Thor::VERSION}" + end + + desc "uninstall NAME", "Uninstall a named Thor module" + 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 + + desc "update NAME", "Update a Thor file from its original location" + 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 + + desc "installed", "List the installed Thor modules and tasks" + method_options :internal => :boolean + def installed + initialize_thorfiles(nil, true) + display_klasses(true, options["internal"]) + end + + desc "list [SEARCH]", "List the available thor tasks (--substring means .*SEARCH)" + method_options :substring => :boolean, :group => :string, :all => :boolean, :debug => :boolean + 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 + + private + + def self.banner(task, all = false, subcommand = false) + "thor " + task.formatted_usage(self, all, subcommand) + end + + def thor_root + Thor::Util.thor_root + end + + 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 + + # 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 self.exit_on_failure? + true + end + + # Load the thorfiles. If relevant_to is supplied, looks for specific files + # in the thor_root instead of loading them all. + # + # By default, it also traverses the current path until find Thor files, as + # described in thorfiles. This look up can be skipped by suppliying + # skip_lookup true. + # + def initialize_thorfiles(relevant_to=nil, skip_lookup=false) + thorfiles(relevant_to, skip_lookup).each do |f| + Thor::Util.load_thorfile(f, nil, options[:debug]) unless Thor::Base.subclass_files.keys.include?(File.expand_path(f)) + end + end + + # Finds Thorfiles by traversing from your current directory down to the root + # directory of your system. If at any time we find a Thor file, we stop. + # + # We also ensure that system-wide Thorfiles are loaded first, so local + # Thorfiles can override them. + # + # ==== Example + # + # If we start at /Users/wycats/dev/thor ... + # + # 1. /Users/wycats/dev/thor + # 2. /Users/wycats/dev + # 3. /Users/wycats <-- we find a Thorfile here, so we stop + # + # Suppose we start at c:\Documents and Settings\james\dev\thor ... + # + # 1. c:\Documents and Settings\james\dev\thor + # 2. c:\Documents and Settings\james\dev + # 3. c:\Documents and Settings\james + # 4. c:\Documents and Settings + # 5. c:\ <-- no Thorfiles found! + # + 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 + + # Load thorfiles relevant to the given method. If you provide "foo:bar" it + # will load all thor files in the thor.yaml that has "foo" e "foo:bar" + # namespaces registered. + # + 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 + + # Display information about the given klasses. If with_module is given, + # it shows a table with information extracted from the yaml file. + # + 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? + + 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.split(":").first] += 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: + 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 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 +end diff --git a/lib/bundler/vendor/thor/shell.rb b/lib/bundler/vendor/thor/shell.rb index b52c9da2..b52c9da2 100755..100644 --- a/lib/bundler/vendor/thor/shell.rb +++ b/lib/bundler/vendor/thor/shell.rb diff --git a/lib/bundler/vendor/thor/shell/basic.rb b/lib/bundler/vendor/thor/shell/basic.rb index b227b5f4..c8411d3d 100755..100644 --- a/lib/bundler/vendor/thor/shell/basic.rb +++ b/lib/bundler/vendor/thor/shell/basic.rb @@ -11,6 +11,21 @@ class Thor @base, @padding = nil, 0 end + # Mute everything that's inside given block + # + def mute + @mute = true + yield + ensure + @mute = false + end + + # Check if base is muted + # + def mute? + @mute + end + # Sets the output padding, not allowing less than zero values. # def padding=(value) @@ -24,7 +39,7 @@ class Thor # def ask(statement, color=nil) say("#{statement} ", color) - $stdin.gets.strip + stdin.gets.strip end # Say (print) something to the user. If the sentence ends with a whitespace @@ -41,11 +56,11 @@ class Thor spaces = " " * padding if force_new_line - $stdout.puts(spaces + message) + stdout.puts(spaces + message) else - $stdout.print(spaces + message) + stdout.print(spaces + message) end - $stdout.flush + stdout.flush end # Say a status with the given color and appends the message. Since this @@ -61,15 +76,15 @@ class Thor status = status.to_s.rjust(12) status = set_color status, color, true if color - $stdout.puts "#{status}#{spaces}#{message}" - $stdout.flush + stdout.puts "#{status}#{spaces}#{message}" + stdout.flush end # Make a question the to user and returns true if the user replies "y" or # "yes". # def yes?(statement, color=nil) - ask(statement, color) =~ is?(:yes) + !!(ask(statement, color) =~ is?(:yes)) end # Make a question the to user and returns true if the user replies "n" or @@ -113,7 +128,7 @@ class Thor end sentence = truncate(sentence, options[:truncate]) if options[:truncate] - $stdout.puts sentence + stdout.puts sentence end end @@ -139,9 +154,9 @@ class Thor paras.each do |para| para.split("\n").each do |line| - $stdout.puts line.insert(0, " " * ident) + stdout.puts line.insert(0, " " * ident) end - $stdout.puts unless para == paras.last + stdout.puts unless para == paras.last end end @@ -180,12 +195,12 @@ class Thor end # Called if something goes wrong during the execution. This is used by Thor - # internally and should not be used inside your scripts. If someone went + # internally and should not be used inside your scripts. If something went # wrong, you can always raise an exception. If you raise a Thor::Error, it # will be rescued and wrapped in the method below. # def error(statement) - $stderr.puts statement + stderr.puts statement end # Apply color to the given string with optional bold. Disabled in the @@ -197,6 +212,18 @@ class Thor protected + def stdout + $stdout + end + + def stdin + $stdin + end + + def stderr + $stderr + end + def is?(value) #:nodoc: value = value.to_s @@ -229,7 +256,7 @@ HELP end def quiet? #:nodoc: - base && base.options[:quiet] + mute? || (base && base.options[:quiet]) end # This code was copied from Rake, available under MIT-LICENSE diff --git a/lib/bundler/vendor/thor/shell/color.rb b/lib/bundler/vendor/thor/shell/color.rb index b2bc66df..b2bc66df 100755..100644 --- a/lib/bundler/vendor/thor/shell/color.rb +++ b/lib/bundler/vendor/thor/shell/color.rb diff --git a/lib/bundler/vendor/thor/task.rb b/lib/bundler/vendor/thor/task.rb index a4355831..6db3b608 100755..100644 --- a/lib/bundler/vendor/thor/task.rb +++ b/lib/bundler/vendor/thor/task.rb @@ -65,10 +65,9 @@ class Thor @required_options ||= options.map{ |_, o| o.usage if o.required? }.compact.sort.join(" ") end - # Given a target, checks if this class name is not a private/protected method. + # Given a target, checks if this class name is a public method. def public_method?(instance) #:nodoc: - collection = instance.private_methods + instance.protected_methods - (collection & [name.to_s, name.to_sym]).empty? + !(instance.public_methods & [name.to_s, name.to_sym]).empty? end def sans_backtrace(backtrace, caller) #:nodoc: @@ -111,4 +110,4 @@ class Thor end end end -end
\ No newline at end of file +end diff --git a/lib/bundler/vendor/thor/util.rb b/lib/bundler/vendor/thor/util.rb index 7f2142b4..275a30bf 100755..100644 --- a/lib/bundler/vendor/thor/util.rb +++ b/lib/bundler/vendor/thor/util.rb @@ -8,11 +8,11 @@ class Thor # # 1) Methods to convert thor namespaces to constants and vice-versa. # - # Thor::Utils.namespace_from_thor_class(Foo::Bar::Baz) #=> "foo:bar:baz" + # Thor::Util.namespace_from_thor_class(Foo::Bar::Baz) #=> "foo:bar:baz" # # 2) Loading thor files and sandboxing: # - # Thor::Utils.load_thorfile("~/.thor/foo") + # Thor::Util.load_thorfile("~/.thor/foo") # module Util diff --git a/lib/bundler/vendor/thor/version.rb b/lib/bundler/vendor/thor/version.rb index b3efbb53..7de92f16 100755..100644 --- a/lib/bundler/vendor/thor/version.rb +++ b/lib/bundler/vendor/thor/version.rb @@ -1,3 +1,3 @@ class Thor - VERSION = "0.14.0".freeze + VERSION = "0.14.6".freeze end |