thor 0.20.3 → 1.3.2
Sign up to get free protection for your applications and to get access to all the features.
- 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