thor 0.20.3 → 1.3.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -9
  3. data/lib/thor/actions/create_file.rb +4 -3
  4. data/lib/thor/actions/create_link.rb +3 -2
  5. data/lib/thor/actions/directory.rb +8 -18
  6. data/lib/thor/actions/empty_directory.rb +1 -1
  7. data/lib/thor/actions/file_manipulation.rb +22 -24
  8. data/lib/thor/actions/inject_into_file.rb +34 -13
  9. data/lib/thor/actions.rb +39 -30
  10. data/lib/thor/base.rb +196 -49
  11. data/lib/thor/command.rb +34 -18
  12. data/lib/thor/core_ext/hash_with_indifferent_access.rb +10 -0
  13. data/lib/thor/error.rb +14 -22
  14. data/lib/thor/group.rb +13 -2
  15. data/lib/thor/invocation.rb +2 -1
  16. data/lib/thor/line_editor/basic.rb +1 -1
  17. data/lib/thor/line_editor/readline.rb +6 -6
  18. data/lib/thor/line_editor.rb +2 -2
  19. data/lib/thor/nested_context.rb +29 -0
  20. data/lib/thor/parser/argument.rb +17 -1
  21. data/lib/thor/parser/arguments.rb +35 -15
  22. data/lib/thor/parser/option.rb +45 -13
  23. data/lib/thor/parser/options.rb +79 -11
  24. data/lib/thor/parser.rb +4 -4
  25. data/lib/thor/rake_compat.rb +3 -2
  26. data/lib/thor/runner.rb +43 -32
  27. data/lib/thor/shell/basic.rb +68 -162
  28. data/lib/thor/shell/color.rb +9 -43
  29. data/lib/thor/shell/column_printer.rb +29 -0
  30. data/lib/thor/shell/html.rb +7 -49
  31. data/lib/thor/shell/lcs_diff.rb +49 -0
  32. data/lib/thor/shell/table_printer.rb +118 -0
  33. data/lib/thor/shell/terminal.rb +42 -0
  34. data/lib/thor/shell/wrapped_printer.rb +38 -0
  35. data/lib/thor/shell.rb +5 -5
  36. data/lib/thor/util.rb +25 -8
  37. data/lib/thor/version.rb +1 -1
  38. data/lib/thor.rb +182 -17
  39. data/thor.gemspec +22 -10
  40. metadata +25 -11
  41. data/CHANGELOG.md +0 -204
  42. data/lib/thor/core_ext/io_binary_read.rb +0 -12
  43. data/lib/thor/core_ext/ordered_hash.rb +0 -129
data/lib/thor/base.rb CHANGED
@@ -1,17 +1,17 @@
1
- require "thor/command"
2
- require "thor/core_ext/hash_with_indifferent_access"
3
- require "thor/core_ext/ordered_hash"
4
- require "thor/error"
5
- require "thor/invocation"
6
- require "thor/parser"
7
- require "thor/shell"
8
- require "thor/line_editor"
9
- require "thor/util"
1
+ require_relative "command"
2
+ require_relative "core_ext/hash_with_indifferent_access"
3
+ require_relative "error"
4
+ require_relative "invocation"
5
+ require_relative "nested_context"
6
+ require_relative "parser"
7
+ require_relative "shell"
8
+ require_relative "line_editor"
9
+ require_relative "util"
10
10
 
11
11
  class Thor
12
- autoload :Actions, "thor/actions"
13
- autoload :RakeCompat, "thor/rake_compat"
14
- autoload :Group, "thor/group"
12
+ autoload :Actions, File.expand_path("actions", __dir__)
13
+ autoload :RakeCompat, File.expand_path("rake_compat", __dir__)
14
+ autoload :Group, File.expand_path("group", __dir__)
15
15
 
16
16
  # Shortcuts for help.
17
17
  HELP_MAPPINGS = %w(-h -? --help -D)
@@ -22,6 +22,15 @@ class Thor
22
22
 
23
23
  TEMPLATE_EXTNAME = ".tt"
24
24
 
25
+ class << self
26
+ def deprecation_warning(message) #:nodoc:
27
+ unless ENV["THOR_SILENCE_DEPRECATION"]
28
+ warn "Deprecation warning: #{message}\n" +
29
+ "You can silence deprecations warning by setting the environment variable THOR_SILENCE_DEPRECATION."
30
+ end
31
+ end
32
+ end
33
+
25
34
  module Base
26
35
  attr_accessor :options, :parent_options, :args
27
36
 
@@ -51,6 +60,7 @@ class Thor
51
60
 
52
61
  command_options = config.delete(:command_options) # hook for start
53
62
  parse_options = parse_options.merge(command_options) if command_options
63
+
54
64
  if local_options.is_a?(Array)
55
65
  array_options = local_options
56
66
  hash_options = {}
@@ -64,9 +74,24 @@ class Thor
64
74
  # Let Thor::Options parse the options first, so it can remove
65
75
  # declared options from the array. This will leave us with
66
76
  # a list of arguments that weren't declared.
67
- stop_on_unknown = self.class.stop_on_unknown_option? config[:current_command]
68
- disable_required_check = self.class.disable_required_check? config[:current_command]
69
- opts = Thor::Options.new(parse_options, hash_options, stop_on_unknown, disable_required_check)
77
+ current_command = config[:current_command]
78
+ stop_on_unknown = self.class.stop_on_unknown_option? current_command
79
+
80
+ # Give a relation of options.
81
+ # After parsing, Thor::Options check whether right relations are kept
82
+ relations = if current_command.nil?
83
+ {exclusive_option_names: [], at_least_one_option_names: []}
84
+ else
85
+ current_command.options_relation
86
+ end
87
+
88
+ self.class.class_exclusive_option_names.map { |n| relations[:exclusive_option_names] << n }
89
+ self.class.class_at_least_one_option_names.map { |n| relations[:at_least_one_option_names] << n }
90
+
91
+ disable_required_check = self.class.disable_required_check? current_command
92
+
93
+ opts = Thor::Options.new(parse_options, hash_options, stop_on_unknown, disable_required_check, relations)
94
+
70
95
  self.options = opts.parse(array_options)
71
96
  self.options = config[:class_options].merge(options) if config[:class_options]
72
97
 
@@ -89,6 +114,7 @@ class Thor
89
114
 
90
115
  class << self
91
116
  def included(base) #:nodoc:
117
+ super(base)
92
118
  base.extend ClassMethods
93
119
  base.send :include, Invocation
94
120
  base.send :include, Shell
@@ -153,17 +179,20 @@ class Thor
153
179
 
154
180
  # If you want to raise an error when the default value of an option does not match
155
181
  # the type call check_default_type!
156
- # This is disabled by default for compatibility.
182
+ # This will be the default; for compatibility a deprecation warning is issued if necessary.
157
183
  def check_default_type!
158
184
  @check_default_type = true
159
185
  end
160
186
 
161
- def check_default_type #:nodoc:
162
- @check_default_type ||= from_superclass(:check_default_type, false)
187
+ # If you want to use defaults that don't match the type of an option,
188
+ # either specify `check_default_type: false` or call `allow_incompatible_default_type!`
189
+ def allow_incompatible_default_type!
190
+ @check_default_type = false
163
191
  end
164
192
 
165
- def check_default_type? #:nodoc:
166
- !!check_default_type
193
+ def check_default_type #:nodoc:
194
+ @check_default_type = from_superclass(:check_default_type, nil) unless defined?(@check_default_type)
195
+ @check_default_type
167
196
  end
168
197
 
169
198
  # If true, option parsing is suspended as soon as an unknown option or a
@@ -297,9 +326,92 @@ class Thor
297
326
  # :hide:: -- If you want to hide this option from the help.
298
327
  #
299
328
  def class_option(name, options = {})
329
+ unless [ Symbol, String ].any? { |klass| name.is_a?(klass) }
330
+ raise ArgumentError, "Expected a Symbol or String, got #{name.inspect}"
331
+ end
300
332
  build_option(name, options, class_options)
301
333
  end
302
334
 
335
+ # Adds and declares option group for exclusive options in the
336
+ # block and arguments. You can declare options as the outside of the block.
337
+ #
338
+ # ==== Parameters
339
+ # Array[Thor::Option.name]
340
+ #
341
+ # ==== Examples
342
+ #
343
+ # class_exclusive do
344
+ # class_option :one
345
+ # class_option :two
346
+ # end
347
+ #
348
+ # Or
349
+ #
350
+ # class_option :one
351
+ # class_option :two
352
+ # class_exclusive :one, :two
353
+ #
354
+ # If you give "--one" and "--two" at the same time ExclusiveArgumentsError
355
+ # will be raised.
356
+ #
357
+ def class_exclusive(*args, &block)
358
+ register_options_relation_for(:class_options,
359
+ :class_exclusive_option_names, *args, &block)
360
+ end
361
+
362
+ # Adds and declares option group for required at least one of options in the
363
+ # block and arguments. You can declare options as the outside of the block.
364
+ #
365
+ # ==== Examples
366
+ #
367
+ # class_at_least_one do
368
+ # class_option :one
369
+ # class_option :two
370
+ # end
371
+ #
372
+ # Or
373
+ #
374
+ # class_option :one
375
+ # class_option :two
376
+ # class_at_least_one :one, :two
377
+ #
378
+ # If you do not give "--one" and "--two" AtLeastOneRequiredArgumentError
379
+ # will be raised.
380
+ #
381
+ # You can use class_at_least_one and class_exclusive at the same time.
382
+ #
383
+ # class_exclusive do
384
+ # class_at_least_one do
385
+ # class_option :one
386
+ # class_option :two
387
+ # end
388
+ # end
389
+ #
390
+ # Then it is required either only one of "--one" or "--two".
391
+ #
392
+ def class_at_least_one(*args, &block)
393
+ register_options_relation_for(:class_options,
394
+ :class_at_least_one_option_names, *args, &block)
395
+ end
396
+
397
+ # Returns this class exclusive options array set, looking up in the ancestors chain.
398
+ #
399
+ # ==== Returns
400
+ # Array[Array[Thor::Option.name]]
401
+ #
402
+ def class_exclusive_option_names
403
+ @class_exclusive_option_names ||= from_superclass(:class_exclusive_option_names, [])
404
+ end
405
+
406
+ # Returns this class at least one of required options array set, looking up in the ancestors chain.
407
+ #
408
+ # ==== Returns
409
+ # Array[Array[Thor::Option.name]]
410
+ #
411
+ def class_at_least_one_option_names
412
+ @class_at_least_one_option_names ||= from_superclass(:class_at_least_one_option_names, [])
413
+ end
414
+
303
415
  # Removes a previous defined argument. If :undefine is given, undefine
304
416
  # accessors as well.
305
417
  #
@@ -353,22 +465,22 @@ class Thor
353
465
  # Returns the commands for this Thor class.
354
466
  #
355
467
  # ==== Returns
356
- # OrderedHash:: An ordered hash with commands names as keys and Thor::Command
357
- # objects as values.
468
+ # Hash:: An ordered hash with commands names as keys and Thor::Command
469
+ # objects as values.
358
470
  #
359
471
  def commands
360
- @commands ||= Thor::CoreExt::OrderedHash.new
472
+ @commands ||= Hash.new
361
473
  end
362
474
  alias_method :tasks, :commands
363
475
 
364
476
  # Returns the commands for this Thor class and all subclasses.
365
477
  #
366
478
  # ==== Returns
367
- # OrderedHash:: An ordered hash with commands names as keys and Thor::Command
368
- # objects as values.
479
+ # Hash:: An ordered hash with commands names as keys and Thor::Command
480
+ # objects as values.
369
481
  #
370
482
  def all_commands
371
- @all_commands ||= from_superclass(:all_commands, Thor::CoreExt::OrderedHash.new)
483
+ @all_commands ||= from_superclass(:all_commands, Hash.new)
372
484
  @all_commands.merge!(commands)
373
485
  end
374
486
  alias_method :all_tasks, :all_commands
@@ -415,14 +527,20 @@ class Thor
415
527
  # remove_command :this_is_not_a_command
416
528
  # end
417
529
  #
418
- def no_commands
419
- @no_commands = true
420
- yield
421
- ensure
422
- @no_commands = false
530
+ def no_commands(&block)
531
+ no_commands_context.enter(&block)
423
532
  end
533
+
424
534
  alias_method :no_tasks, :no_commands
425
535
 
536
+ def no_commands_context
537
+ @no_commands_context ||= NestedContext.new
538
+ end
539
+
540
+ def no_commands?
541
+ no_commands_context.entered?
542
+ end
543
+
426
544
  # Sets the namespace for the Thor or Thor::Group class. By default the
427
545
  # namespace is retrieved from the class name. If your Thor class is named
428
546
  # Scripts::MyScript, the help method, for example, will be called as:
@@ -487,7 +605,7 @@ class Thor
487
605
  #
488
606
  def public_command(*names)
489
607
  names.each do |name|
490
- class_eval "def #{name}(*); super end"
608
+ class_eval "def #{name}(*); super end", __FILE__, __LINE__
491
609
  end
492
610
  end
493
611
  alias_method :public_task, :public_command
@@ -502,10 +620,16 @@ class Thor
502
620
  msg = "ERROR: \"#{basename} #{name}\" was called with ".dup
503
621
  msg << "no arguments" if args.empty?
504
622
  msg << "arguments " << args.inspect unless args.empty?
505
- msg << "\nUsage: #{banner(command).inspect}"
623
+ msg << "\nUsage: \"#{banner(command).split("\n").join("\"\n \"")}\""
506
624
  raise InvocationError, msg
507
625
  end
508
626
 
627
+ # A flag that makes the process exit with status 1 if any error happens.
628
+ def exit_on_failure?
629
+ Thor.deprecation_warning "Thor exit with status 0 on errors. To keep this behavior, you must define `exit_on_failure?` in `#{self.name}`"
630
+ false
631
+ end
632
+
509
633
  protected
510
634
 
511
635
  # Prints the class options per group. If an option does not belong to
@@ -533,20 +657,19 @@ class Thor
533
657
  return if options.empty?
534
658
 
535
659
  list = []
536
- padding = options.map { |o| o.aliases.size }.max.to_i * 4
537
-
660
+ padding = options.map { |o| o.aliases_for_usage.size }.max.to_i
538
661
  options.each do |option|
539
662
  next if option.hide
540
663
  item = [option.usage(padding)]
541
664
  item.push(option.description ? "# #{option.description}" : "")
542
665
 
543
666
  list << item
544
- list << ["", "# Default: #{option.default}"] if option.show_default?
545
- list << ["", "# Possible values: #{option.enum.join(', ')}"] if option.enum
667
+ list << ["", "# Default: #{option.print_default}"] if option.show_default?
668
+ list << ["", "# Possible values: #{option.enum_to_s}"] if option.enum
546
669
  end
547
670
 
548
671
  shell.say(group_name ? "#{group_name} options:" : "Options:")
549
- shell.print_table(list, :indent => 2)
672
+ shell.print_table(list, indent: 2)
550
673
  shell.say ""
551
674
  end
552
675
 
@@ -563,7 +686,7 @@ class Thor
563
686
  # options<Hash>:: Described in both class_option and method_option.
564
687
  # scope<Hash>:: Options hash that is being built up
565
688
  def build_option(name, options, scope) #:nodoc:
566
- scope[name] = Thor::Option.new(name, options.merge(:check_default_type => check_default_type?))
689
+ scope[name] = Thor::Option.new(name, {check_default_type: check_default_type}.merge!(options))
567
690
  end
568
691
 
569
692
  # Receives a hash of options, parse them and add to the scope. This is a
@@ -585,7 +708,7 @@ class Thor
585
708
  def find_and_refresh_command(name) #:nodoc:
586
709
  if commands[name.to_s]
587
710
  commands[name.to_s]
588
- elsif command = all_commands[name.to_s] # rubocop:disable AssignmentInCondition
711
+ elsif command = all_commands[name.to_s] # rubocop:disable Lint/AssignmentInCondition
589
712
  commands[name.to_s] = command.clone
590
713
  else
591
714
  raise ArgumentError, "You supplied :for => #{name.inspect}, but the command #{name.inspect} could not be found."
@@ -593,16 +716,18 @@ class Thor
593
716
  end
594
717
  alias_method :find_and_refresh_task, :find_and_refresh_command
595
718
 
596
- # Everytime someone inherits from a Thor class, register the klass
719
+ # Every time someone inherits from a Thor class, register the klass
597
720
  # and file into baseclass.
598
721
  def inherited(klass)
722
+ super(klass)
599
723
  Thor::Base.register_klass_file(klass)
600
- klass.instance_variable_set(:@no_commands, false)
724
+ klass.instance_variable_set(:@no_commands, 0)
601
725
  end
602
726
 
603
727
  # Fire this callback whenever a method is added. Added methods are
604
728
  # tracked as commands by invoking the create_command method.
605
729
  def method_added(meth)
730
+ super(meth)
606
731
  meth = meth.to_s
607
732
 
608
733
  if meth == "initialize"
@@ -613,8 +738,7 @@ class Thor
613
738
  # Return if it's not a public instance method
614
739
  return unless public_method_defined?(meth.to_sym)
615
740
 
616
- @no_commands ||= false
617
- return if @no_commands || !create_command(meth)
741
+ return if no_commands? || !create_command(meth)
618
742
 
619
743
  is_thor_reserved_word?(meth, :command)
620
744
  Thor::Base.register_klass_file(self)
@@ -641,11 +765,6 @@ class Thor
641
765
  end
642
766
  end
643
767
 
644
- # A flag that makes the process exit with status 1 if any error happens.
645
- def exit_on_failure?
646
- false
647
- end
648
-
649
768
  #
650
769
  # The basename of the program invoking the thor class.
651
770
  #
@@ -673,6 +792,34 @@ class Thor
673
792
  def dispatch(command, given_args, given_opts, config) #:nodoc:
674
793
  raise NotImplementedError
675
794
  end
795
+
796
+ # Register a relation of options for target(method_option/class_option)
797
+ # by args and block.
798
+ def register_options_relation_for(target, relation, *args, &block) # :nodoc:
799
+ opt = args.pop if args.last.is_a? Hash
800
+ opt ||= {}
801
+ names = args.map{ |arg| arg.to_s }
802
+ names += built_option_names(target, opt, &block) if block_given?
803
+ command_scope_member(relation, opt) << names
804
+ end
805
+
806
+ # Get target(method_options or class_options) options
807
+ # of before and after by block evaluation.
808
+ def built_option_names(target, opt = {}, &block) # :nodoc:
809
+ before = command_scope_member(target, opt).map{ |k,v| v.name }
810
+ instance_eval(&block)
811
+ after = command_scope_member(target, opt).map{ |k,v| v.name }
812
+ after - before
813
+ end
814
+
815
+ # Get command scope member by name.
816
+ def command_scope_member(name, options = {}) # :nodoc:
817
+ if options[:for]
818
+ find_and_refresh_command(options[:for]).send(name)
819
+ else
820
+ send(name)
821
+ end
822
+ end
676
823
  end
677
824
  end
678
825
  end
data/lib/thor/command.rb CHANGED
@@ -1,14 +1,15 @@
1
1
  class Thor
2
- class Command < Struct.new(:name, :description, :long_description, :usage, :options, :ancestor_name)
2
+ class Command < Struct.new(:name, :description, :long_description, :wrap_long_description, :usage, :options, :options_relation, :ancestor_name)
3
3
  FILE_REGEXP = /^#{Regexp.escape(File.dirname(__FILE__))}/
4
4
 
5
- def initialize(name, description, long_description, usage, options = nil)
6
- super(name.to_s, description, long_description, usage, options || {})
5
+ def initialize(name, description, long_description, wrap_long_description, usage, options = nil, options_relation = nil)
6
+ super(name.to_s, description, long_description, wrap_long_description, usage, options || {}, options_relation || {})
7
7
  end
8
8
 
9
9
  def initialize_copy(other) #:nodoc:
10
10
  super(other)
11
11
  self.options = other.options.dup if other.options
12
+ self.options_relation = other.options_relation.dup if other.options_relation
12
13
  end
13
14
 
14
15
  def hidden?
@@ -49,24 +50,40 @@ class Thor
49
50
 
50
51
  formatted ||= "".dup
51
52
 
52
- # Add usage with required arguments
53
- formatted << if klass && !klass.arguments.empty?
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
53
+ Array(usage).map do |specific_usage|
54
+ formatted_specific_usage = formatted
60
55
 
61
- # Add required options
62
- formatted << " #{required_options}"
56
+ formatted_specific_usage += required_arguments_for(klass, specific_usage)
63
57
 
64
- # Strip and go!
65
- formatted.strip
58
+ # Add required options
59
+ formatted_specific_usage += " #{required_options}"
60
+
61
+ # Strip and go!
62
+ formatted_specific_usage.strip
63
+ end.join("\n")
64
+ end
65
+
66
+ def method_exclusive_option_names #:nodoc:
67
+ self.options_relation[:exclusive_option_names] || []
68
+ end
69
+
70
+ def method_at_least_one_option_names #:nodoc:
71
+ self.options_relation[:at_least_one_option_names] || []
66
72
  end
67
73
 
68
74
  protected
69
75
 
76
+ # Add usage with required arguments
77
+ def required_arguments_for(klass, usage)
78
+ if klass && !klass.arguments.empty?
79
+ usage.to_s.gsub(/^#{name}/) do |match|
80
+ match << " " << klass.arguments.map(&:usage).compact.join(" ")
81
+ end
82
+ else
83
+ usage.to_s
84
+ end
85
+ end
86
+
70
87
  def not_debugging?(instance)
71
88
  !(instance.class.respond_to?(:debugging) && instance.class.debugging)
72
89
  end
@@ -97,8 +114,7 @@ class Thor
97
114
  def handle_argument_error?(instance, error, caller)
98
115
  not_debugging?(instance) && (error.message =~ /wrong number of arguments/ || error.message =~ /given \d*, expected \d*/) && begin
99
116
  saned = sans_backtrace(error.backtrace, caller)
100
- # Ruby 1.9 always include the called method in the backtrace
101
- saned.empty? || (saned.size == 1 && RUBY_VERSION >= "1.9")
117
+ saned.empty? || saned.size == 1
102
118
  end
103
119
  end
104
120
 
@@ -120,7 +136,7 @@ class Thor
120
136
  # A dynamic command that handles method missing scenarios.
121
137
  class DynamicCommand < Command
122
138
  def initialize(name, options = nil)
123
- super(name.to_s, "A dynamically-generated command", name.to_s, name.to_s, options)
139
+ super(name.to_s, "A dynamically-generated command", name.to_s, nil, name.to_s, options)
124
140
  end
125
141
 
126
142
  def run(instance, args = [])
@@ -28,10 +28,20 @@ class Thor
28
28
  super(convert_key(key))
29
29
  end
30
30
 
31
+ def except(*keys)
32
+ dup.tap do |hash|
33
+ keys.each { |key| hash.delete(convert_key(key)) }
34
+ end
35
+ end
36
+
31
37
  def fetch(key, *args)
32
38
  super(convert_key(key), *args)
33
39
  end
34
40
 
41
+ def slice(*keys)
42
+ super(*keys.map{ |key| convert_key(key) })
43
+ end
44
+
35
45
  def key?(key)
36
46
  super(convert_key(key))
37
47
  end
data/lib/thor/error.rb CHANGED
@@ -1,22 +1,15 @@
1
1
  class Thor
2
- Correctable =
3
- begin
4
- require 'did_you_mean'
5
-
6
- # In order to support versions of Ruby that don't have keyword
7
- # arguments, we need our own spell checker class that doesn't take key
8
- # words. Even though this code wouldn't be hit because of the check
9
- # above, it's still necessary because the interpreter would otherwise be
10
- # unable to parse the file.
11
- class NoKwargSpellChecker < DidYouMean::SpellChecker # :nodoc:
12
- def initialize(dictionary)
13
- @dictionary = dictionary
14
- end
2
+ Correctable = if defined?(DidYouMean::SpellChecker) && defined?(DidYouMean::Correctable) # rubocop:disable Naming/ConstantName
3
+ Module.new do
4
+ def to_s
5
+ super + DidYouMean.formatter.message_for(corrections)
15
6
  end
16
7
 
17
- DidYouMean::Correctable
18
- rescue LoadError, NameError
8
+ def corrections
9
+ @corrections ||= self.class.const_get(:SpellChecker).new(self).corrections
10
+ end
19
11
  end
12
+ end
20
13
 
21
14
  # Thor::Error is raised when it's caused by wrong usage of thor classes. Those
22
15
  # errors have their backtrace suppressed and are nicely shown to the user.
@@ -41,7 +34,7 @@ class Thor
41
34
  end
42
35
 
43
36
  def spell_checker
44
- NoKwargSpellChecker.new(error.all_commands)
37
+ DidYouMean::SpellChecker.new(dictionary: error.all_commands)
45
38
  end
46
39
  end
47
40
 
@@ -83,7 +76,7 @@ class Thor
83
76
  end
84
77
 
85
78
  def spell_checker
86
- @spell_checker ||= NoKwargSpellChecker.new(error.switches)
79
+ @spell_checker ||= DidYouMean::SpellChecker.new(dictionary: error.switches)
87
80
  end
88
81
  end
89
82
 
@@ -105,10 +98,9 @@ class Thor
105
98
  class MalformattedArgumentError < InvocationError
106
99
  end
107
100
 
108
- if Correctable
109
- DidYouMean::SPELL_CHECKERS.merge!(
110
- 'Thor::UndefinedCommandError' => UndefinedCommandError::SpellChecker,
111
- 'Thor::UnknownArgumentError' => UnknownArgumentError::SpellChecker
112
- )
101
+ class ExclusiveArgumentError < InvocationError
102
+ end
103
+
104
+ class AtLeastOneRequiredArgumentError < InvocationError
113
105
  end
114
106
  end
data/lib/thor/group.rb CHANGED
@@ -1,4 +1,4 @@
1
- require "thor/base"
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
@@ -169,7 +169,7 @@ class Thor::Group
169
169
  # options are added to group_options hash. Options that already exists
170
170
  # in base_options are not added twice.
171
171
  #
172
- def get_options_from_invocations(group_options, base_options) #:nodoc: # rubocop:disable MethodLength
172
+ def get_options_from_invocations(group_options, base_options) #:nodoc:
173
173
  invocations.each do |name, from_option|
174
174
  value = if from_option
175
175
  option = class_options[name]
@@ -211,6 +211,17 @@ class Thor::Group
211
211
  raise error, msg
212
212
  end
213
213
 
214
+ # Checks if a specified command exists.
215
+ #
216
+ # ==== Parameters
217
+ # command_name<String>:: The name of the command to check for existence.
218
+ #
219
+ # ==== Returns
220
+ # Boolean:: +true+ if the command exists, +false+ otherwise.
221
+ def command_exists?(command_name) #:nodoc:
222
+ commands.keys.include?(command_name)
223
+ end
224
+
214
225
  protected
215
226
 
216
227
  # The method responsible for dispatching given the args.
@@ -1,6 +1,7 @@
1
1
  class Thor
2
2
  module Invocation
3
3
  def self.included(base) #:nodoc:
4
+ super(base)
4
5
  base.extend ClassMethods
5
6
  end
6
7
 
@@ -142,7 +143,7 @@ class Thor
142
143
 
143
144
  # Configuration values that are shared between invocations.
144
145
  def _shared_configuration #:nodoc:
145
- {:invocations => @_invocations}
146
+ {invocations: @_invocations}
146
147
  end
147
148
 
148
149
  # This method simply retrieves the class and command to be invoked.
@@ -24,7 +24,7 @@ class Thor
24
24
  $stdin.gets
25
25
  else
26
26
  # Lazy-load io/console since it is gem-ified as of 2.3
27
- require "io/console" if RUBY_VERSION > "1.9.2"
27
+ require "io/console"
28
28
  $stdin.noecho(&:gets)
29
29
  end
30
30
  end
@@ -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
- # Ruby 1.8.7 does not allow Readline.completion_proc= to receive nil.
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
@@ -1,5 +1,5 @@
1
- require "thor/line_editor/basic"
2
- require "thor/line_editor/readline"
1
+ require_relative "line_editor/basic"
2
+ require_relative "line_editor/readline"
3
3
 
4
4
  class Thor
5
5
  module LineEditor