thor 0.20.0 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of thor might be problematic. Click here for more details.
- checksums.yaml +5 -5
- data/README.md +7 -9
- data/lib/thor/actions/create_file.rb +1 -1
- data/lib/thor/actions/create_link.rb +3 -2
- data/lib/thor/actions/directory.rb +7 -17
- data/lib/thor/actions/file_manipulation.rb +25 -14
- data/lib/thor/actions/inject_into_file.rb +20 -10
- data/lib/thor/actions.rb +34 -15
- data/lib/thor/base.rb +63 -43
- data/lib/thor/command.rb +21 -14
- data/lib/thor/core_ext/hash_with_indifferent_access.rb +6 -0
- data/lib/thor/error.rb +83 -0
- data/lib/thor/group.rb +3 -3
- data/lib/thor/invocation.rb +1 -0
- data/lib/thor/line_editor/basic.rb +1 -1
- data/lib/thor/line_editor/readline.rb +6 -6
- data/lib/thor/line_editor.rb +2 -2
- data/lib/thor/nested_context.rb +29 -0
- data/lib/thor/parser/arguments.rb +7 -3
- data/lib/thor/parser/option.rb +20 -7
- data/lib/thor/parser/options.rb +40 -6
- data/lib/thor/parser.rb +4 -4
- data/lib/thor/rake_compat.rb +1 -0
- data/lib/thor/runner.rb +5 -4
- data/lib/thor/shell/basic.rb +87 -12
- data/lib/thor/shell/color.rb +10 -2
- data/lib/thor/shell/html.rb +3 -3
- data/lib/thor/shell.rb +5 -5
- data/lib/thor/util.rb +18 -2
- data/lib/thor/version.rb +1 -1
- data/lib/thor.rb +16 -9
- data/thor.gemspec +10 -2
- metadata +20 -11
- data/CHANGELOG.md +0 -193
- data/lib/thor/core_ext/io_binary_read.rb +0 -12
- data/lib/thor/core_ext/ordered_hash.rb +0 -129
    
        data/lib/thor/command.rb
    CHANGED
    
    | @@ -49,24 +49,32 @@ class Thor | |
| 49 49 |  | 
| 50 50 | 
             
                  formatted ||= "".dup
         | 
| 51 51 |  | 
| 52 | 
            -
                   | 
| 53 | 
            -
             | 
| 54 | 
            -
                                 usage.to_s.gsub(/^#{name}/) do |match|
         | 
| 55 | 
            -
                                   match << " " << klass.arguments.map(&:usage).compact.join(" ")
         | 
| 56 | 
            -
                                 end
         | 
| 57 | 
            -
                               else
         | 
| 58 | 
            -
                                 usage.to_s
         | 
| 59 | 
            -
                               end
         | 
| 52 | 
            +
                  Array(usage).map do |specific_usage|
         | 
| 53 | 
            +
                    formatted_specific_usage = formatted
         | 
| 60 54 |  | 
| 61 | 
            -
             | 
| 62 | 
            -
                  formatted << " #{required_options}"
         | 
| 55 | 
            +
                    formatted_specific_usage += required_arguments_for(klass, specific_usage)
         | 
| 63 56 |  | 
| 64 | 
            -
             | 
| 65 | 
            -
             | 
| 57 | 
            +
                    # Add required options
         | 
| 58 | 
            +
                    formatted_specific_usage += " #{required_options}"
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                    # Strip and go!
         | 
| 61 | 
            +
                    formatted_specific_usage.strip
         | 
| 62 | 
            +
                  end.join("\n")
         | 
| 66 63 | 
             
                end
         | 
| 67 64 |  | 
| 68 65 | 
             
              protected
         | 
| 69 66 |  | 
| 67 | 
            +
                # Add usage with required arguments
         | 
| 68 | 
            +
                def required_arguments_for(klass, usage)
         | 
| 69 | 
            +
                  if klass && !klass.arguments.empty?
         | 
| 70 | 
            +
                    usage.to_s.gsub(/^#{name}/) do |match|
         | 
| 71 | 
            +
                      match << " " << klass.arguments.map(&:usage).compact.join(" ")
         | 
| 72 | 
            +
                    end
         | 
| 73 | 
            +
                  else
         | 
| 74 | 
            +
                    usage.to_s
         | 
| 75 | 
            +
                  end
         | 
| 76 | 
            +
                end
         | 
| 77 | 
            +
             | 
| 70 78 | 
             
                def not_debugging?(instance)
         | 
| 71 79 | 
             
                  !(instance.class.respond_to?(:debugging) && instance.class.debugging)
         | 
| 72 80 | 
             
                end
         | 
| @@ -97,8 +105,7 @@ class Thor | |
| 97 105 | 
             
                def handle_argument_error?(instance, error, caller)
         | 
| 98 106 | 
             
                  not_debugging?(instance) && (error.message =~ /wrong number of arguments/ || error.message =~ /given \d*, expected \d*/) && begin
         | 
| 99 107 | 
             
                    saned = sans_backtrace(error.backtrace, caller)
         | 
| 100 | 
            -
                     | 
| 101 | 
            -
                    saned.empty? || (saned.size == 1 && RUBY_VERSION >= "1.9")
         | 
| 108 | 
            +
                    saned.empty? || saned.size == 1
         | 
| 102 109 | 
             
                  end
         | 
| 103 110 | 
             
                end
         | 
| 104 111 |  | 
    
        data/lib/thor/error.rb
    CHANGED
    
    | @@ -1,4 +1,19 @@ | |
| 1 1 | 
             
            class Thor
         | 
| 2 | 
            +
              Correctable = if defined?(DidYouMean::SpellChecker) && defined?(DidYouMean::Correctable) # rubocop:disable Naming/ConstantName
         | 
| 3 | 
            +
                              # In order to support versions of Ruby that don't have keyword
         | 
| 4 | 
            +
                              # arguments, we need our own spell checker class that doesn't take key
         | 
| 5 | 
            +
                              # words. Even though this code wouldn't be hit because of the check
         | 
| 6 | 
            +
                              # above, it's still necessary because the interpreter would otherwise be
         | 
| 7 | 
            +
                              # unable to parse the file.
         | 
| 8 | 
            +
                              class NoKwargSpellChecker < DidYouMean::SpellChecker # :nodoc:
         | 
| 9 | 
            +
                                def initialize(dictionary)
         | 
| 10 | 
            +
                                  @dictionary = dictionary
         | 
| 11 | 
            +
                                end
         | 
| 12 | 
            +
                              end
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                              DidYouMean::Correctable
         | 
| 15 | 
            +
                            end
         | 
| 16 | 
            +
             | 
| 2 17 | 
             
              # Thor::Error is raised when it's caused by wrong usage of thor classes. Those
         | 
| 3 18 | 
             
              # errors have their backtrace suppressed and are nicely shown to the user.
         | 
| 4 19 | 
             
              #
         | 
| @@ -10,6 +25,35 @@ class Thor | |
| 10 25 |  | 
| 11 26 | 
             
              # Raised when a command was not found.
         | 
| 12 27 | 
             
              class UndefinedCommandError < Error
         | 
| 28 | 
            +
                class SpellChecker
         | 
| 29 | 
            +
                  attr_reader :error
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                  def initialize(error)
         | 
| 32 | 
            +
                    @error = error
         | 
| 33 | 
            +
                  end
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                  def corrections
         | 
| 36 | 
            +
                    @corrections ||= spell_checker.correct(error.command).map(&:inspect)
         | 
| 37 | 
            +
                  end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                  def spell_checker
         | 
| 40 | 
            +
                    NoKwargSpellChecker.new(error.all_commands)
         | 
| 41 | 
            +
                  end
         | 
| 42 | 
            +
                end
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                attr_reader :command, :all_commands
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                def initialize(command, all_commands, namespace)
         | 
| 47 | 
            +
                  @command = command
         | 
| 48 | 
            +
                  @all_commands = all_commands
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                  message = "Could not find command #{command.inspect}"
         | 
| 51 | 
            +
                  message = namespace ? "#{message} in #{namespace.inspect} namespace." : "#{message}."
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                  super(message)
         | 
| 54 | 
            +
                end
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                prepend Correctable if Correctable
         | 
| 13 57 | 
             
              end
         | 
| 14 58 | 
             
              UndefinedTaskError = UndefinedCommandError
         | 
| 15 59 |  | 
| @@ -22,6 +66,33 @@ class Thor | |
| 22 66 | 
             
              end
         | 
| 23 67 |  | 
| 24 68 | 
             
              class UnknownArgumentError < Error
         | 
| 69 | 
            +
                class SpellChecker
         | 
| 70 | 
            +
                  attr_reader :error
         | 
| 71 | 
            +
             | 
| 72 | 
            +
                  def initialize(error)
         | 
| 73 | 
            +
                    @error = error
         | 
| 74 | 
            +
                  end
         | 
| 75 | 
            +
             | 
| 76 | 
            +
                  def corrections
         | 
| 77 | 
            +
                    @corrections ||=
         | 
| 78 | 
            +
                      error.unknown.flat_map { |unknown| spell_checker.correct(unknown) }.uniq.map(&:inspect)
         | 
| 79 | 
            +
                  end
         | 
| 80 | 
            +
             | 
| 81 | 
            +
                  def spell_checker
         | 
| 82 | 
            +
                    @spell_checker ||= NoKwargSpellChecker.new(error.switches)
         | 
| 83 | 
            +
                  end
         | 
| 84 | 
            +
                end
         | 
| 85 | 
            +
             | 
| 86 | 
            +
                attr_reader :switches, :unknown
         | 
| 87 | 
            +
             | 
| 88 | 
            +
                def initialize(switches, unknown)
         | 
| 89 | 
            +
                  @switches = switches
         | 
| 90 | 
            +
                  @unknown = unknown
         | 
| 91 | 
            +
             | 
| 92 | 
            +
                  super("Unknown switches #{unknown.map(&:inspect).join(', ')}")
         | 
| 93 | 
            +
                end
         | 
| 94 | 
            +
             | 
| 95 | 
            +
                prepend Correctable if Correctable
         | 
| 25 96 | 
             
              end
         | 
| 26 97 |  | 
| 27 98 | 
             
              class RequiredArgumentMissingError < InvocationError
         | 
| @@ -29,4 +100,16 @@ class Thor | |
| 29 100 |  | 
| 30 101 | 
             
              class MalformattedArgumentError < InvocationError
         | 
| 31 102 | 
             
              end
         | 
| 103 | 
            +
             | 
| 104 | 
            +
              if Correctable
         | 
| 105 | 
            +
                if DidYouMean.respond_to?(:correct_error)
         | 
| 106 | 
            +
                  DidYouMean.correct_error(Thor::UndefinedCommandError, UndefinedCommandError::SpellChecker)
         | 
| 107 | 
            +
                  DidYouMean.correct_error(Thor::UnknownArgumentError, UnknownArgumentError::SpellChecker)
         | 
| 108 | 
            +
                else
         | 
| 109 | 
            +
                  DidYouMean::SPELL_CHECKERS.merge!(
         | 
| 110 | 
            +
                    'Thor::UndefinedCommandError' => UndefinedCommandError::SpellChecker,
         | 
| 111 | 
            +
                    'Thor::UnknownArgumentError' => UnknownArgumentError::SpellChecker
         | 
| 112 | 
            +
                  )
         | 
| 113 | 
            +
                end
         | 
| 114 | 
            +
              end
         | 
| 32 115 | 
             
            end
         | 
    
        data/lib/thor/group.rb
    CHANGED
    
    | @@ -1,4 +1,4 @@ | |
| 1 | 
            -
             | 
| 1 | 
            +
            require_relative "base"
         | 
| 2 2 |  | 
| 3 3 | 
             
            # Thor has a special class called Thor::Group. The main difference to Thor class
         | 
| 4 4 | 
             
            # is that it invokes all commands at once. It also include some methods that allows
         | 
| @@ -61,7 +61,7 @@ class Thor::Group | |
| 61 61 | 
             
                    invocations[name] = false
         | 
| 62 62 | 
             
                    invocation_blocks[name] = block if block_given?
         | 
| 63 63 |  | 
| 64 | 
            -
                    class_eval <<-METHOD, __FILE__, __LINE__
         | 
| 64 | 
            +
                    class_eval <<-METHOD, __FILE__, __LINE__ + 1
         | 
| 65 65 | 
             
                      def _invoke_#{name.to_s.gsub(/\W/, '_')}
         | 
| 66 66 | 
             
                        klass, command = self.class.prepare_for_invocation(nil, #{name.inspect})
         | 
| 67 67 |  | 
| @@ -120,7 +120,7 @@ class Thor::Group | |
| 120 120 | 
             
                    invocations[name] = true
         | 
| 121 121 | 
             
                    invocation_blocks[name] = block if block_given?
         | 
| 122 122 |  | 
| 123 | 
            -
                    class_eval <<-METHOD, __FILE__, __LINE__
         | 
| 123 | 
            +
                    class_eval <<-METHOD, __FILE__, __LINE__ + 1
         | 
| 124 124 | 
             
                      def _invoke_from_option_#{name.to_s.gsub(/\W/, '_')}
         | 
| 125 125 | 
             
                        return unless options[#{name.inspect}]
         | 
| 126 126 |  | 
    
        data/lib/thor/invocation.rb
    CHANGED
    
    
| @@ -1,19 +1,19 @@ | |
| 1 | 
            -
            begin
         | 
| 2 | 
            -
              require "readline"
         | 
| 3 | 
            -
            rescue LoadError
         | 
| 4 | 
            -
            end
         | 
| 5 | 
            -
             | 
| 6 1 | 
             
            class Thor
         | 
| 7 2 | 
             
              module LineEditor
         | 
| 8 3 | 
             
                class Readline < Basic
         | 
| 9 4 | 
             
                  def self.available?
         | 
| 5 | 
            +
                    begin
         | 
| 6 | 
            +
                      require "readline"
         | 
| 7 | 
            +
                    rescue LoadError
         | 
| 8 | 
            +
                    end
         | 
| 9 | 
            +
             | 
| 10 10 | 
             
                    Object.const_defined?(:Readline)
         | 
| 11 11 | 
             
                  end
         | 
| 12 12 |  | 
| 13 13 | 
             
                  def readline
         | 
| 14 14 | 
             
                    if echo?
         | 
| 15 15 | 
             
                      ::Readline.completion_append_character = nil
         | 
| 16 | 
            -
                      #  | 
| 16 | 
            +
                      # rb-readline does not allow Readline.completion_proc= to receive nil.
         | 
| 17 17 | 
             
                      if complete = completion_proc
         | 
| 18 18 | 
             
                        ::Readline.completion_proc = complete
         | 
| 19 19 | 
             
                      end
         | 
    
        data/lib/thor/line_editor.rb
    CHANGED
    
    
| @@ -0,0 +1,29 @@ | |
| 1 | 
            +
            class Thor
         | 
| 2 | 
            +
              class NestedContext
         | 
| 3 | 
            +
                def initialize
         | 
| 4 | 
            +
                  @depth = 0
         | 
| 5 | 
            +
                end
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                def enter
         | 
| 8 | 
            +
                  push
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                  yield
         | 
| 11 | 
            +
                ensure
         | 
| 12 | 
            +
                  pop
         | 
| 13 | 
            +
                end
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                def entered?
         | 
| 16 | 
            +
                  @depth > 0
         | 
| 17 | 
            +
                end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                private
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                def push
         | 
| 22 | 
            +
                  @depth += 1
         | 
| 23 | 
            +
                end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                def pop
         | 
| 26 | 
            +
                  @depth -= 1
         | 
| 27 | 
            +
                end
         | 
| 28 | 
            +
              end
         | 
| 29 | 
            +
            end
         | 
| @@ -9,7 +9,7 @@ class Thor | |
| 9 9 | 
             
                  arguments = []
         | 
| 10 10 |  | 
| 11 11 | 
             
                  args.each do |item|
         | 
| 12 | 
            -
                    break if item =~ /^-/
         | 
| 12 | 
            +
                    break if item.is_a?(String) && item =~ /^-/
         | 
| 13 13 | 
             
                    arguments << item
         | 
| 14 14 | 
             
                  end
         | 
| 15 15 |  | 
| @@ -30,7 +30,11 @@ class Thor | |
| 30 30 |  | 
| 31 31 | 
             
                  arguments.each do |argument|
         | 
| 32 32 | 
             
                    if !argument.default.nil?
         | 
| 33 | 
            -
                       | 
| 33 | 
            +
                      begin
         | 
| 34 | 
            +
                        @assigns[argument.human_name] = argument.default.dup
         | 
| 35 | 
            +
                      rescue TypeError  # Compatibility shim for un-dup-able Fixnum in Ruby < 2.4
         | 
| 36 | 
            +
                        @assigns[argument.human_name] = argument.default
         | 
| 37 | 
            +
                      end
         | 
| 34 38 | 
             
                    elsif argument.required?
         | 
| 35 39 | 
             
                      @non_assigned_required << argument
         | 
| 36 40 | 
             
                    end
         | 
| @@ -82,7 +86,7 @@ class Thor | |
| 82 86 | 
             
                end
         | 
| 83 87 |  | 
| 84 88 | 
             
                def current_is_value?
         | 
| 85 | 
            -
                  peek && peek.to_s !~  | 
| 89 | 
            +
                  peek && peek.to_s !~ /^-{1,2}\S+/
         | 
| 86 90 | 
             
                end
         | 
| 87 91 |  | 
| 88 92 | 
             
                # Runs through the argument array getting strings that contains ":" and
         | 
    
        data/lib/thor/parser/option.rb
    CHANGED
    
    | @@ -1,17 +1,18 @@ | |
| 1 1 | 
             
            class Thor
         | 
| 2 2 | 
             
              class Option < Argument #:nodoc:
         | 
| 3 | 
            -
                attr_reader :aliases, :group, :lazy_default, :hide
         | 
| 3 | 
            +
                attr_reader :aliases, :group, :lazy_default, :hide, :repeatable
         | 
| 4 4 |  | 
| 5 5 | 
             
                VALID_TYPES = [:boolean, :numeric, :hash, :array, :string]
         | 
| 6 6 |  | 
| 7 7 | 
             
                def initialize(name, options = {})
         | 
| 8 8 | 
             
                  @check_default_type = options[:check_default_type]
         | 
| 9 9 | 
             
                  options[:required] = false unless options.key?(:required)
         | 
| 10 | 
            +
                  @repeatable     = options.fetch(:repeatable, false)
         | 
| 10 11 | 
             
                  super
         | 
| 11 | 
            -
                  @lazy_default | 
| 12 | 
            -
                  @group | 
| 13 | 
            -
                  @aliases | 
| 14 | 
            -
                  @hide | 
| 12 | 
            +
                  @lazy_default   = options[:lazy_default]
         | 
| 13 | 
            +
                  @group          = options[:group].to_s.capitalize if options[:group]
         | 
| 14 | 
            +
                  @aliases        = Array(options[:aliases])
         | 
| 15 | 
            +
                  @hide           = options[:hide]
         | 
| 15 16 | 
             
                end
         | 
| 16 17 |  | 
| 17 18 | 
             
                # This parse quick options given as method_options. It makes several
         | 
| @@ -111,7 +112,7 @@ class Thor | |
| 111 112 |  | 
| 112 113 | 
             
                def validate!
         | 
| 113 114 | 
             
                  raise ArgumentError, "An option cannot be boolean and required." if boolean? && required?
         | 
| 114 | 
            -
                  validate_default_type! | 
| 115 | 
            +
                  validate_default_type!
         | 
| 115 116 | 
             
                end
         | 
| 116 117 |  | 
| 117 118 | 
             
                def validate_default_type!
         | 
| @@ -128,7 +129,19 @@ class Thor | |
| 128 129 | 
             
                    @default.class.name.downcase.to_sym
         | 
| 129 130 | 
             
                  end
         | 
| 130 131 |  | 
| 131 | 
            -
                   | 
| 132 | 
            +
                  expected_type = (@repeatable && @type != :hash) ? :array : @type
         | 
| 133 | 
            +
             | 
| 134 | 
            +
                  if default_type != expected_type
         | 
| 135 | 
            +
                    err = "Expected #{expected_type} default value for '#{switch_name}'; got #{@default.inspect} (#{default_type})"
         | 
| 136 | 
            +
             | 
| 137 | 
            +
                    if @check_default_type
         | 
| 138 | 
            +
                      raise ArgumentError, err
         | 
| 139 | 
            +
                    elsif @check_default_type == nil
         | 
| 140 | 
            +
                      Thor.deprecation_warning "#{err}.\n" +
         | 
| 141 | 
            +
                        'This will be rejected in the future unless you explicitly pass the options `check_default_type: false`' +
         | 
| 142 | 
            +
                        ' or call `allow_incompatible_default_type!` in your code'
         | 
| 143 | 
            +
                    end
         | 
| 144 | 
            +
                  end
         | 
| 132 145 | 
             
                end
         | 
| 133 146 |  | 
| 134 147 | 
             
                def dasherized?
         | 
    
        data/lib/thor/parser/options.rb
    CHANGED
    
    | @@ -44,6 +44,8 @@ class Thor | |
| 44 44 | 
             
                  @shorts = {}
         | 
| 45 45 | 
             
                  @switches = {}
         | 
| 46 46 | 
             
                  @extra = []
         | 
| 47 | 
            +
                  @stopped_parsing_after_extra_index = nil
         | 
| 48 | 
            +
                  @is_treated_as_value = false
         | 
| 47 49 |  | 
| 48 50 | 
             
                  options.each do |option|
         | 
| 49 51 | 
             
                    @switches[option.switch_name] = option
         | 
| @@ -66,14 +68,26 @@ class Thor | |
| 66 68 | 
             
                  if result == OPTS_END
         | 
| 67 69 | 
             
                    shift
         | 
| 68 70 | 
             
                    @parsing_options = false
         | 
| 71 | 
            +
                    @stopped_parsing_after_extra_index ||= @extra.size
         | 
| 69 72 | 
             
                    super
         | 
| 70 73 | 
             
                  else
         | 
| 71 74 | 
             
                    result
         | 
| 72 75 | 
             
                  end
         | 
| 73 76 | 
             
                end
         | 
| 74 77 |  | 
| 78 | 
            +
                def shift
         | 
| 79 | 
            +
                  @is_treated_as_value = false
         | 
| 80 | 
            +
                  super
         | 
| 81 | 
            +
                end
         | 
| 82 | 
            +
             | 
| 83 | 
            +
                def unshift(arg, is_value: false)
         | 
| 84 | 
            +
                  @is_treated_as_value = is_value
         | 
| 85 | 
            +
                  super(arg)
         | 
| 86 | 
            +
                end
         | 
| 87 | 
            +
             | 
| 75 88 | 
             
                def parse(args) # rubocop:disable MethodLength
         | 
| 76 89 | 
             
                  @pile = args.dup
         | 
| 90 | 
            +
                  @is_treated_as_value = false
         | 
| 77 91 | 
             
                  @parsing_options = true
         | 
| 78 92 |  | 
| 79 93 | 
             
                  while peek
         | 
| @@ -86,7 +100,10 @@ class Thor | |
| 86 100 | 
             
                        when SHORT_SQ_RE
         | 
| 87 101 | 
             
                          unshift($1.split("").map { |f| "-#{f}" })
         | 
| 88 102 | 
             
                          next
         | 
| 89 | 
            -
                        when EQ_RE | 
| 103 | 
            +
                        when EQ_RE
         | 
| 104 | 
            +
                          unshift($2, is_value: true)
         | 
| 105 | 
            +
                          switch = $1
         | 
| 106 | 
            +
                        when SHORT_NUM
         | 
| 90 107 | 
             
                          unshift($2)
         | 
| 91 108 | 
             
                          switch = $1
         | 
| 92 109 | 
             
                        when LONG_RE, SHORT_RE
         | 
| @@ -95,10 +112,12 @@ class Thor | |
| 95 112 |  | 
| 96 113 | 
             
                        switch = normalize_switch(switch)
         | 
| 97 114 | 
             
                        option = switch_option(switch)
         | 
| 98 | 
            -
                         | 
| 115 | 
            +
                        result = parse_peek(switch, option)
         | 
| 116 | 
            +
                        assign_result!(option, result)
         | 
| 99 117 | 
             
                      elsif @stop_on_unknown
         | 
| 100 118 | 
             
                        @parsing_options = false
         | 
| 101 119 | 
             
                        @extra << shifted
         | 
| 120 | 
            +
                        @stopped_parsing_after_extra_index ||= @extra.size
         | 
| 102 121 | 
             
                        @extra << shift while peek
         | 
| 103 122 | 
             
                        break
         | 
| 104 123 | 
             
                      elsif match
         | 
| @@ -120,18 +139,31 @@ class Thor | |
| 120 139 | 
             
                end
         | 
| 121 140 |  | 
| 122 141 | 
             
                def check_unknown!
         | 
| 142 | 
            +
                  to_check = @stopped_parsing_after_extra_index ? @extra[0...@stopped_parsing_after_extra_index] : @extra
         | 
| 143 | 
            +
             | 
| 123 144 | 
             
                  # an unknown option starts with - or -- and has no more --'s afterward.
         | 
| 124 | 
            -
                  unknown =  | 
| 125 | 
            -
                  raise UnknownArgumentError | 
| 145 | 
            +
                  unknown = to_check.select { |str| str =~ /^--?(?:(?!--).)*$/ }
         | 
| 146 | 
            +
                  raise UnknownArgumentError.new(@switches.keys, unknown) unless unknown.empty?
         | 
| 126 147 | 
             
                end
         | 
| 127 148 |  | 
| 128 149 | 
             
              protected
         | 
| 129 150 |  | 
| 151 | 
            +
                def assign_result!(option, result)
         | 
| 152 | 
            +
                  if option.repeatable && option.type == :hash
         | 
| 153 | 
            +
                    (@assigns[option.human_name] ||= {}).merge!(result)
         | 
| 154 | 
            +
                  elsif option.repeatable
         | 
| 155 | 
            +
                    (@assigns[option.human_name] ||= []) << result
         | 
| 156 | 
            +
                  else
         | 
| 157 | 
            +
                    @assigns[option.human_name] = result
         | 
| 158 | 
            +
                  end
         | 
| 159 | 
            +
                end
         | 
| 160 | 
            +
             | 
| 130 161 | 
             
                # Check if the current value in peek is a registered switch.
         | 
| 131 162 | 
             
                #
         | 
| 132 163 | 
             
                # Two booleans are returned.  The first is true if the current value
         | 
| 133 164 | 
             
                # starts with a hyphen; the second is true if it is a registered switch.
         | 
| 134 165 | 
             
                def current_is_switch?
         | 
| 166 | 
            +
                  return [false, false] if @is_treated_as_value
         | 
| 135 167 | 
             
                  case peek
         | 
| 136 168 | 
             
                  when LONG_RE, SHORT_RE, EQ_RE, SHORT_NUM
         | 
| 137 169 | 
             
                    [true, switch?($1)]
         | 
| @@ -143,6 +175,7 @@ class Thor | |
| 143 175 | 
             
                end
         | 
| 144 176 |  | 
| 145 177 | 
             
                def current_is_switch_formatted?
         | 
| 178 | 
            +
                  return false if @is_treated_as_value
         | 
| 146 179 | 
             
                  case peek
         | 
| 147 180 | 
             
                  when LONG_RE, SHORT_RE, EQ_RE, SHORT_NUM, SHORT_SQ_RE
         | 
| 148 181 | 
             
                    true
         | 
| @@ -152,11 +185,12 @@ class Thor | |
| 152 185 | 
             
                end
         | 
| 153 186 |  | 
| 154 187 | 
             
                def current_is_value?
         | 
| 188 | 
            +
                  return true if @is_treated_as_value
         | 
| 155 189 | 
             
                  peek && (!parsing_options? || super)
         | 
| 156 190 | 
             
                end
         | 
| 157 191 |  | 
| 158 192 | 
             
                def switch?(arg)
         | 
| 159 | 
            -
                  switch_option(normalize_switch(arg))
         | 
| 193 | 
            +
                  !switch_option(normalize_switch(arg)).nil?
         | 
| 160 194 | 
             
                end
         | 
| 161 195 |  | 
| 162 196 | 
             
                def switch_option(arg)
         | 
| @@ -189,7 +223,7 @@ class Thor | |
| 189 223 | 
             
                      shift
         | 
| 190 224 | 
             
                      false
         | 
| 191 225 | 
             
                    else
         | 
| 192 | 
            -
                      !no_or_skip?(switch)
         | 
| 226 | 
            +
                      @switches.key?(switch) || !no_or_skip?(switch)
         | 
| 193 227 | 
             
                    end
         | 
| 194 228 | 
             
                  else
         | 
| 195 229 | 
             
                    @switches.key?(switch) || !no_or_skip?(switch)
         | 
    
        data/lib/thor/parser.rb
    CHANGED
    
    | @@ -1,4 +1,4 @@ | |
| 1 | 
            -
             | 
| 2 | 
            -
             | 
| 3 | 
            -
             | 
| 4 | 
            -
             | 
| 1 | 
            +
            require_relative "parser/argument"
         | 
| 2 | 
            +
            require_relative "parser/arguments"
         | 
| 3 | 
            +
            require_relative "parser/option"
         | 
| 4 | 
            +
            require_relative "parser/options"
         | 
    
        data/lib/thor/rake_compat.rb
    CHANGED
    
    | @@ -25,6 +25,7 @@ class Thor | |
| 25 25 | 
             
                end
         | 
| 26 26 |  | 
| 27 27 | 
             
                def self.included(base)
         | 
| 28 | 
            +
                  super(base)
         | 
| 28 29 | 
             
                  # Hack. Make rakefile point to invoker, so rdoc task is generated properly.
         | 
| 29 30 | 
             
                  rakefile = File.basename(caller[0].match(/(.*):\d+/)[1])
         | 
| 30 31 | 
             
                  Rake.application.instance_variable_set(:@rakefile, rakefile)
         | 
    
        data/lib/thor/runner.rb
    CHANGED
    
    | @@ -1,12 +1,13 @@ | |
| 1 | 
            -
             | 
| 2 | 
            -
             | 
| 3 | 
            -
            require "thor/core_ext/io_binary_read"
         | 
| 1 | 
            +
            require_relative "../thor"
         | 
| 2 | 
            +
            require_relative "group"
         | 
| 4 3 |  | 
| 5 4 | 
             
            require "yaml"
         | 
| 6 5 | 
             
            require "digest/md5"
         | 
| 7 6 | 
             
            require "pathname"
         | 
| 8 7 |  | 
| 9 8 | 
             
            class Thor::Runner < Thor #:nodoc: # rubocop:disable ClassLength
         | 
| 9 | 
            +
              autoload :OpenURI, "open-uri"
         | 
| 10 | 
            +
             | 
| 10 11 | 
             
              map "-T" => :list, "-i" => :install, "-u" => :update, "-v" => :version
         | 
| 11 12 |  | 
| 12 13 | 
             
              def self.banner(command, all = false, subcommand = false)
         | 
| @@ -111,7 +112,7 @@ class Thor::Runner < Thor #:nodoc: # rubocop:disable ClassLength | |
| 111 112 |  | 
| 112 113 | 
             
              desc "version", "Show Thor version"
         | 
| 113 114 | 
             
              def version
         | 
| 114 | 
            -
                 | 
| 115 | 
            +
                require_relative "version"
         | 
| 115 116 | 
             
                say "Thor #{Thor::VERSION}"
         | 
| 116 117 | 
             
              end
         | 
| 117 118 |  |