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.
- checksums.yaml +4 -4
- data/README.md +3 -9
- data/lib/thor/actions/create_file.rb +4 -3
- data/lib/thor/actions/create_link.rb +3 -2
- data/lib/thor/actions/directory.rb +8 -18
- data/lib/thor/actions/empty_directory.rb +1 -1
- data/lib/thor/actions/file_manipulation.rb +22 -24
- data/lib/thor/actions/inject_into_file.rb +34 -13
- data/lib/thor/actions.rb +39 -30
- data/lib/thor/base.rb +196 -49
- data/lib/thor/command.rb +34 -18
- data/lib/thor/core_ext/hash_with_indifferent_access.rb +10 -0
- data/lib/thor/error.rb +14 -22
- data/lib/thor/group.rb +13 -2
- data/lib/thor/invocation.rb +2 -1
- 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/argument.rb +17 -1
- data/lib/thor/parser/arguments.rb +35 -15
- data/lib/thor/parser/option.rb +45 -13
- data/lib/thor/parser/options.rb +79 -11
- data/lib/thor/parser.rb +4 -4
- data/lib/thor/rake_compat.rb +3 -2
- data/lib/thor/runner.rb +43 -32
- data/lib/thor/shell/basic.rb +68 -162
- data/lib/thor/shell/color.rb +9 -43
- data/lib/thor/shell/column_printer.rb +29 -0
- data/lib/thor/shell/html.rb +7 -49
- data/lib/thor/shell/lcs_diff.rb +49 -0
- data/lib/thor/shell/table_printer.rb +118 -0
- data/lib/thor/shell/terminal.rb +42 -0
- data/lib/thor/shell/wrapped_printer.rb +38 -0
- data/lib/thor/shell.rb +5 -5
- data/lib/thor/util.rb +25 -8
- data/lib/thor/version.rb +1 -1
- data/lib/thor.rb +182 -17
- data/thor.gemspec +22 -10
- metadata +25 -11
- data/CHANGELOG.md +0 -204
- data/lib/thor/core_ext/io_binary_read.rb +0 -12
- data/lib/thor/core_ext/ordered_hash.rb +0 -129
data/lib/thor/base.rb
CHANGED
@@ -1,17 +1,17 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
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, "
|
13
|
-
autoload :RakeCompat, "
|
14
|
-
autoload :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
|
-
|
68
|
-
|
69
|
-
|
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
|
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
|
-
|
162
|
-
|
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
|
166
|
-
|
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
|
-
#
|
357
|
-
#
|
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 ||=
|
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
|
-
#
|
368
|
-
#
|
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,
|
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
|
-
|
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).
|
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.
|
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.
|
545
|
-
list << ["", "# Possible values: #{option.
|
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, :
|
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,
|
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
|
-
#
|
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,
|
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
|
-
|
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
|
-
|
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
|
53
|
+
Array(usage).map do |specific_usage|
|
54
|
+
formatted_specific_usage = formatted
|
60
55
|
|
61
|
-
|
62
|
-
formatted << " #{required_options}"
|
56
|
+
formatted_specific_usage += required_arguments_for(klass, specific_usage)
|
63
57
|
|
64
|
-
|
65
|
-
|
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
|
-
|
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
|
-
|
4
|
-
|
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
|
-
|
18
|
-
|
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
|
-
|
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 ||=
|
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
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
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
|
-
|
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:
|
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.
|
data/lib/thor/invocation.rb
CHANGED
@@ -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
|
-
{:
|
146
|
+
{invocations: @_invocations}
|
146
147
|
end
|
147
148
|
|
148
149
|
# This method simply retrieves the class and command to be invoked.
|
@@ -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