thor 0.20.3 → 1.3.2

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.
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