thor 0.19.4 → 1.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/README.md +7 -9
- data/lib/thor/actions/create_file.rb +2 -1
- data/lib/thor/actions/create_link.rb +4 -2
- data/lib/thor/actions/directory.rb +7 -17
- data/lib/thor/actions/empty_directory.rb +9 -1
- data/lib/thor/actions/file_manipulation.rb +64 -16
- data/lib/thor/actions/inject_into_file.rb +27 -11
- data/lib/thor/actions.rb +38 -16
- data/lib/thor/base.rb +84 -41
- data/lib/thor/command.rb +30 -21
- data/lib/thor/core_ext/hash_with_indifferent_access.rb +18 -0
- data/lib/thor/error.rb +83 -0
- data/lib/thor/group.rb +4 -4
- data/lib/thor/invocation.rb +1 -0
- data/lib/thor/line_editor/basic.rb +2 -0
- data/lib/thor/line_editor/readline.rb +6 -6
- data/lib/thor/line_editor.rb +2 -2
- data/lib/thor/nested_context.rb +29 -0
- data/lib/thor/parser/arguments.rb +7 -3
- data/lib/thor/parser/option.rb +22 -9
- data/lib/thor/parser/options.rb +45 -10
- data/lib/thor/parser.rb +4 -4
- data/lib/thor/rake_compat.rb +1 -0
- data/lib/thor/runner.rb +9 -6
- data/lib/thor/shell/basic.rb +96 -20
- data/lib/thor/shell/color.rb +10 -2
- data/lib/thor/shell/html.rb +3 -3
- data/lib/thor/shell.rb +5 -5
- data/lib/thor/util.rb +18 -2
- data/lib/thor/version.rb +1 -1
- data/lib/thor.rb +39 -15
- data/thor.gemspec +10 -2
- metadata +20 -11
- data/CHANGELOG.md +0 -163
- 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
|
|
@@ -42,7 +51,7 @@ class Thor
|
|
42
51
|
# config<Hash>:: Configuration for this Thor class.
|
43
52
|
#
|
44
53
|
def initialize(args = [], local_options = {}, config = {})
|
45
|
-
parse_options =
|
54
|
+
parse_options = self.class.class_options
|
46
55
|
|
47
56
|
# The start method splits inbound arguments at the first argument
|
48
57
|
# that looks like an option (starts with - or --). It then calls
|
@@ -65,7 +74,8 @@ class Thor
|
|
65
74
|
# declared options from the array. This will leave us with
|
66
75
|
# a list of arguments that weren't declared.
|
67
76
|
stop_on_unknown = self.class.stop_on_unknown_option? config[:current_command]
|
68
|
-
|
77
|
+
disable_required_check = self.class.disable_required_check? config[:current_command]
|
78
|
+
opts = Thor::Options.new(parse_options, hash_options, stop_on_unknown, disable_required_check)
|
69
79
|
self.options = opts.parse(array_options)
|
70
80
|
self.options = config[:class_options].merge(options) if config[:class_options]
|
71
81
|
|
@@ -88,6 +98,7 @@ class Thor
|
|
88
98
|
|
89
99
|
class << self
|
90
100
|
def included(base) #:nodoc:
|
101
|
+
super(base)
|
91
102
|
base.extend ClassMethods
|
92
103
|
base.send :include, Invocation
|
93
104
|
base.send :include, Shell
|
@@ -112,7 +123,7 @@ class Thor
|
|
112
123
|
end
|
113
124
|
|
114
125
|
# Whenever a class inherits from Thor or Thor::Group, we should track the
|
115
|
-
# class and the file on Thor::Base. This is the method
|
126
|
+
# class and the file on Thor::Base. This is the method responsible for it.
|
116
127
|
#
|
117
128
|
def register_klass_file(klass) #:nodoc:
|
118
129
|
file = caller[1].match(/(.*):\d+/)[1]
|
@@ -150,6 +161,24 @@ class Thor
|
|
150
161
|
!!check_unknown_options
|
151
162
|
end
|
152
163
|
|
164
|
+
# If you want to raise an error when the default value of an option does not match
|
165
|
+
# the type call check_default_type!
|
166
|
+
# This will be the default; for compatibility a deprecation warning is issued if necessary.
|
167
|
+
def check_default_type!
|
168
|
+
@check_default_type = true
|
169
|
+
end
|
170
|
+
|
171
|
+
# If you want to use defaults that don't match the type of an option,
|
172
|
+
# either specify `check_default_type: false` or call `allow_incompatible_default_type!`
|
173
|
+
def allow_incompatible_default_type!
|
174
|
+
@check_default_type = false
|
175
|
+
end
|
176
|
+
|
177
|
+
def check_default_type #:nodoc:
|
178
|
+
@check_default_type = from_superclass(:check_default_type, nil) unless defined?(@check_default_type)
|
179
|
+
@check_default_type
|
180
|
+
end
|
181
|
+
|
153
182
|
# If true, option parsing is suspended as soon as an unknown option or a
|
154
183
|
# regular argument is encountered. All remaining arguments are passed to
|
155
184
|
# the command as regular arguments.
|
@@ -157,6 +186,12 @@ class Thor
|
|
157
186
|
false
|
158
187
|
end
|
159
188
|
|
189
|
+
# If true, option set will not suspend the execution of the command when
|
190
|
+
# a required option is not provided.
|
191
|
+
def disable_required_check?(command_name) #:nodoc:
|
192
|
+
false
|
193
|
+
end
|
194
|
+
|
160
195
|
# If you want only strict string args (useful when cascading thor classes),
|
161
196
|
# call strict_args_position! This is disabled by default to allow dynamic
|
162
197
|
# invocations.
|
@@ -331,22 +366,22 @@ class Thor
|
|
331
366
|
# Returns the commands for this Thor class.
|
332
367
|
#
|
333
368
|
# ==== Returns
|
334
|
-
#
|
335
|
-
#
|
369
|
+
# Hash:: An ordered hash with commands names as keys and Thor::Command
|
370
|
+
# objects as values.
|
336
371
|
#
|
337
372
|
def commands
|
338
|
-
@commands ||=
|
373
|
+
@commands ||= Hash.new
|
339
374
|
end
|
340
375
|
alias_method :tasks, :commands
|
341
376
|
|
342
377
|
# Returns the commands for this Thor class and all subclasses.
|
343
378
|
#
|
344
379
|
# ==== Returns
|
345
|
-
#
|
346
|
-
#
|
380
|
+
# Hash:: An ordered hash with commands names as keys and Thor::Command
|
381
|
+
# objects as values.
|
347
382
|
#
|
348
383
|
def all_commands
|
349
|
-
@all_commands ||= from_superclass(:all_commands,
|
384
|
+
@all_commands ||= from_superclass(:all_commands, Hash.new)
|
350
385
|
@all_commands.merge!(commands)
|
351
386
|
end
|
352
387
|
alias_method :all_tasks, :all_commands
|
@@ -393,14 +428,20 @@ class Thor
|
|
393
428
|
# remove_command :this_is_not_a_command
|
394
429
|
# end
|
395
430
|
#
|
396
|
-
def no_commands
|
397
|
-
|
398
|
-
yield
|
399
|
-
ensure
|
400
|
-
@no_commands = false
|
431
|
+
def no_commands(&block)
|
432
|
+
no_commands_context.enter(&block)
|
401
433
|
end
|
434
|
+
|
402
435
|
alias_method :no_tasks, :no_commands
|
403
436
|
|
437
|
+
def no_commands_context
|
438
|
+
@no_commands_context ||= NestedContext.new
|
439
|
+
end
|
440
|
+
|
441
|
+
def no_commands?
|
442
|
+
no_commands_context.entered?
|
443
|
+
end
|
444
|
+
|
404
445
|
# Sets the namespace for the Thor or Thor::Group class. By default the
|
405
446
|
# namespace is retrieved from the class name. If your Thor class is named
|
406
447
|
# Scripts::MyScript, the help method, for example, will be called as:
|
@@ -444,13 +485,13 @@ class Thor
|
|
444
485
|
dispatch(nil, given_args.dup, nil, config)
|
445
486
|
rescue Thor::Error => e
|
446
487
|
config[:debug] || ENV["THOR_DEBUG"] == "1" ? (raise e) : config[:shell].error(e.message)
|
447
|
-
exit(
|
488
|
+
exit(false) if exit_on_failure?
|
448
489
|
rescue Errno::EPIPE
|
449
490
|
# This happens if a thor command is piped to something like `head`,
|
450
491
|
# which closes the pipe when it's done reading. This will also
|
451
492
|
# mean that if the pipe is closed, further unnecessary
|
452
493
|
# computation will not occur.
|
453
|
-
exit(
|
494
|
+
exit(true)
|
454
495
|
end
|
455
496
|
|
456
497
|
# Allows to use private methods from parent in child classes as commands.
|
@@ -471,19 +512,25 @@ class Thor
|
|
471
512
|
alias_method :public_task, :public_command
|
472
513
|
|
473
514
|
def handle_no_command_error(command, has_namespace = $thor_runner) #:nodoc:
|
474
|
-
raise UndefinedCommandError,
|
475
|
-
raise UndefinedCommandError, "Could not find command #{command.inspect}."
|
515
|
+
raise UndefinedCommandError.new(command, all_commands.keys, (namespace if has_namespace))
|
476
516
|
end
|
477
517
|
alias_method :handle_no_task_error, :handle_no_command_error
|
478
518
|
|
479
519
|
def handle_argument_error(command, error, args, arity) #:nodoc:
|
480
|
-
|
520
|
+
name = [command.ancestor_name, command.name].compact.join(" ")
|
521
|
+
msg = "ERROR: \"#{basename} #{name}\" was called with ".dup
|
481
522
|
msg << "no arguments" if args.empty?
|
482
523
|
msg << "arguments " << args.inspect unless args.empty?
|
483
|
-
msg << "\nUsage: #{banner(command).
|
524
|
+
msg << "\nUsage: \"#{banner(command).split("\n").join("\"\n \"")}\""
|
484
525
|
raise InvocationError, msg
|
485
526
|
end
|
486
527
|
|
528
|
+
# A flag that makes the process exit with status 1 if any error happens.
|
529
|
+
def exit_on_failure?
|
530
|
+
Thor.deprecation_warning "Thor exit with status 0 on errors. To keep this behavior, you must define `exit_on_failure?` in `#{self.name}`"
|
531
|
+
false
|
532
|
+
end
|
533
|
+
|
487
534
|
protected
|
488
535
|
|
489
536
|
# Prints the class options per group. If an option does not belong to
|
@@ -541,7 +588,7 @@ class Thor
|
|
541
588
|
# options<Hash>:: Described in both class_option and method_option.
|
542
589
|
# scope<Hash>:: Options hash that is being built up
|
543
590
|
def build_option(name, options, scope) #:nodoc:
|
544
|
-
scope[name] = Thor::Option.new(name, options)
|
591
|
+
scope[name] = Thor::Option.new(name, {:check_default_type => check_default_type}.merge!(options))
|
545
592
|
end
|
546
593
|
|
547
594
|
# Receives a hash of options, parse them and add to the scope. This is a
|
@@ -574,13 +621,15 @@ class Thor
|
|
574
621
|
# Everytime someone inherits from a Thor class, register the klass
|
575
622
|
# and file into baseclass.
|
576
623
|
def inherited(klass)
|
624
|
+
super(klass)
|
577
625
|
Thor::Base.register_klass_file(klass)
|
578
|
-
klass.instance_variable_set(:@no_commands,
|
626
|
+
klass.instance_variable_set(:@no_commands, 0)
|
579
627
|
end
|
580
628
|
|
581
629
|
# Fire this callback whenever a method is added. Added methods are
|
582
630
|
# tracked as commands by invoking the create_command method.
|
583
631
|
def method_added(meth)
|
632
|
+
super(meth)
|
584
633
|
meth = meth.to_s
|
585
634
|
|
586
635
|
if meth == "initialize"
|
@@ -591,8 +640,7 @@ class Thor
|
|
591
640
|
# Return if it's not a public instance method
|
592
641
|
return unless public_method_defined?(meth.to_sym)
|
593
642
|
|
594
|
-
|
595
|
-
return if @no_commands || !create_command(meth)
|
643
|
+
return if no_commands? || !create_command(meth)
|
596
644
|
|
597
645
|
is_thor_reserved_word?(meth, :command)
|
598
646
|
Thor::Base.register_klass_file(self)
|
@@ -619,11 +667,6 @@ class Thor
|
|
619
667
|
end
|
620
668
|
end
|
621
669
|
|
622
|
-
# A flag that makes the process exit with status 1 if any error happens.
|
623
|
-
def exit_on_failure?
|
624
|
-
false
|
625
|
-
end
|
626
|
-
|
627
670
|
#
|
628
671
|
# The basename of the program invoking the thor class.
|
629
672
|
#
|
data/lib/thor/command.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
class Thor
|
2
|
-
class Command < Struct.new(:name, :description, :long_description, :usage, :options, :
|
2
|
+
class Command < Struct.new(:name, :description, :long_description, :usage, :options, :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, usage, options = nil)
|
6
|
+
super(name.to_s, description, long_description, usage, options || {})
|
7
7
|
end
|
8
8
|
|
9
9
|
def initialize_copy(other) #:nodoc:
|
@@ -39,32 +39,42 @@ class Thor
|
|
39
39
|
# Returns the formatted usage by injecting given required arguments
|
40
40
|
# and required options into the given usage.
|
41
41
|
def formatted_usage(klass, namespace = true, subcommand = false)
|
42
|
-
if
|
42
|
+
if ancestor_name
|
43
|
+
formatted = "#{ancestor_name} ".dup # add space
|
44
|
+
elsif namespace
|
43
45
|
namespace = klass.namespace
|
44
|
-
formatted = "#{namespace.gsub(/^(default)/, '')}:"
|
46
|
+
formatted = "#{namespace.gsub(/^(default)/, '')}:".dup
|
45
47
|
end
|
46
|
-
formatted
|
48
|
+
formatted ||= "#{klass.namespace.split(':').last} ".dup if subcommand
|
47
49
|
|
48
|
-
formatted ||= ""
|
50
|
+
formatted ||= "".dup
|
49
51
|
|
50
|
-
|
51
|
-
|
52
|
-
usage.to_s.gsub(/^#{name}/) do |match|
|
53
|
-
match << " " << klass.arguments.map(&:usage).compact.join(" ")
|
54
|
-
end
|
55
|
-
else
|
56
|
-
usage.to_s
|
57
|
-
end
|
52
|
+
Array(usage).map do |specific_usage|
|
53
|
+
formatted_specific_usage = formatted
|
58
54
|
|
59
|
-
|
60
|
-
formatted << " #{required_options}"
|
55
|
+
formatted_specific_usage += required_arguments_for(klass, specific_usage)
|
61
56
|
|
62
|
-
|
63
|
-
|
57
|
+
# Add required options
|
58
|
+
formatted_specific_usage += " #{required_options}"
|
59
|
+
|
60
|
+
# Strip and go!
|
61
|
+
formatted_specific_usage.strip
|
62
|
+
end.join("\n")
|
64
63
|
end
|
65
64
|
|
66
65
|
protected
|
67
66
|
|
67
|
+
# Add usage with required arguments
|
68
|
+
def required_arguments_for(klass, usage)
|
69
|
+
if klass && !klass.arguments.empty?
|
70
|
+
usage.to_s.gsub(/^#{name}/) do |match|
|
71
|
+
match << " " << klass.arguments.map(&:usage).compact.join(" ")
|
72
|
+
end
|
73
|
+
else
|
74
|
+
usage.to_s
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
68
78
|
def not_debugging?(instance)
|
69
79
|
!(instance.class.respond_to?(:debugging) && instance.class.debugging)
|
70
80
|
end
|
@@ -95,8 +105,7 @@ class Thor
|
|
95
105
|
def handle_argument_error?(instance, error, caller)
|
96
106
|
not_debugging?(instance) && (error.message =~ /wrong number of arguments/ || error.message =~ /given \d*, expected \d*/) && begin
|
97
107
|
saned = sans_backtrace(error.backtrace, caller)
|
98
|
-
|
99
|
-
saned.empty? || (saned.size == 1 && RUBY_VERSION >= "1.9")
|
108
|
+
saned.empty? || saned.size == 1
|
100
109
|
end
|
101
110
|
end
|
102
111
|
|
@@ -28,6 +28,12 @@ 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
|
@@ -51,6 +57,18 @@ class Thor
|
|
51
57
|
self
|
52
58
|
end
|
53
59
|
|
60
|
+
def reverse_merge(other)
|
61
|
+
self.class.new(other).merge(self)
|
62
|
+
end
|
63
|
+
|
64
|
+
def reverse_merge!(other_hash)
|
65
|
+
replace(reverse_merge(other_hash))
|
66
|
+
end
|
67
|
+
|
68
|
+
def replace(other_hash)
|
69
|
+
super(other_hash)
|
70
|
+
end
|
71
|
+
|
54
72
|
# Convert to a Hash with String keys.
|
55
73
|
def to_hash
|
56
74
|
Hash.new(default).merge!(self)
|
data/lib/thor/error.rb
CHANGED
@@ -1,4 +1,19 @@
|
|
1
1
|
class Thor
|
2
|
+
Correctable = if defined?(DidYouMean::SpellChecker) && defined?(DidYouMean::Correctable) # rubocop:disable Naming/ConstantName
|
3
|
+
# In order to support versions of Ruby that don't have keyword
|
4
|
+
# arguments, we need our own spell checker class that doesn't take key
|
5
|
+
# words. Even though this code wouldn't be hit because of the check
|
6
|
+
# above, it's still necessary because the interpreter would otherwise be
|
7
|
+
# unable to parse the file.
|
8
|
+
class NoKwargSpellChecker < DidYouMean::SpellChecker # :nodoc:
|
9
|
+
def initialize(dictionary)
|
10
|
+
@dictionary = dictionary
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
DidYouMean::Correctable
|
15
|
+
end
|
16
|
+
|
2
17
|
# Thor::Error is raised when it's caused by wrong usage of thor classes. Those
|
3
18
|
# errors have their backtrace suppressed and are nicely shown to the user.
|
4
19
|
#
|
@@ -10,6 +25,35 @@ class Thor
|
|
10
25
|
|
11
26
|
# Raised when a command was not found.
|
12
27
|
class UndefinedCommandError < Error
|
28
|
+
class SpellChecker
|
29
|
+
attr_reader :error
|
30
|
+
|
31
|
+
def initialize(error)
|
32
|
+
@error = error
|
33
|
+
end
|
34
|
+
|
35
|
+
def corrections
|
36
|
+
@corrections ||= spell_checker.correct(error.command).map(&:inspect)
|
37
|
+
end
|
38
|
+
|
39
|
+
def spell_checker
|
40
|
+
NoKwargSpellChecker.new(error.all_commands)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
attr_reader :command, :all_commands
|
45
|
+
|
46
|
+
def initialize(command, all_commands, namespace)
|
47
|
+
@command = command
|
48
|
+
@all_commands = all_commands
|
49
|
+
|
50
|
+
message = "Could not find command #{command.inspect}"
|
51
|
+
message = namespace ? "#{message} in #{namespace.inspect} namespace." : "#{message}."
|
52
|
+
|
53
|
+
super(message)
|
54
|
+
end
|
55
|
+
|
56
|
+
prepend Correctable if Correctable
|
13
57
|
end
|
14
58
|
UndefinedTaskError = UndefinedCommandError
|
15
59
|
|
@@ -22,6 +66,33 @@ class Thor
|
|
22
66
|
end
|
23
67
|
|
24
68
|
class UnknownArgumentError < Error
|
69
|
+
class SpellChecker
|
70
|
+
attr_reader :error
|
71
|
+
|
72
|
+
def initialize(error)
|
73
|
+
@error = error
|
74
|
+
end
|
75
|
+
|
76
|
+
def corrections
|
77
|
+
@corrections ||=
|
78
|
+
error.unknown.flat_map { |unknown| spell_checker.correct(unknown) }.uniq.map(&:inspect)
|
79
|
+
end
|
80
|
+
|
81
|
+
def spell_checker
|
82
|
+
@spell_checker ||= NoKwargSpellChecker.new(error.switches)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
attr_reader :switches, :unknown
|
87
|
+
|
88
|
+
def initialize(switches, unknown)
|
89
|
+
@switches = switches
|
90
|
+
@unknown = unknown
|
91
|
+
|
92
|
+
super("Unknown switches #{unknown.map(&:inspect).join(', ')}")
|
93
|
+
end
|
94
|
+
|
95
|
+
prepend Correctable if Correctable
|
25
96
|
end
|
26
97
|
|
27
98
|
class RequiredArgumentMissingError < InvocationError
|
@@ -29,4 +100,16 @@ class Thor
|
|
29
100
|
|
30
101
|
class MalformattedArgumentError < InvocationError
|
31
102
|
end
|
103
|
+
|
104
|
+
if Correctable
|
105
|
+
if DidYouMean.respond_to?(:correct_error)
|
106
|
+
DidYouMean.correct_error(Thor::UndefinedCommandError, UndefinedCommandError::SpellChecker)
|
107
|
+
DidYouMean.correct_error(Thor::UnknownArgumentError, UnknownArgumentError::SpellChecker)
|
108
|
+
else
|
109
|
+
DidYouMean::SPELL_CHECKERS.merge!(
|
110
|
+
'Thor::UndefinedCommandError' => UndefinedCommandError::SpellChecker,
|
111
|
+
'Thor::UnknownArgumentError' => UnknownArgumentError::SpellChecker
|
112
|
+
)
|
113
|
+
end
|
114
|
+
end
|
32
115
|
end
|
data/lib/thor/group.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
require_relative "base"
|
2
2
|
|
3
3
|
# Thor has a special class called Thor::Group. The main difference to Thor class
|
4
4
|
# is that it invokes all commands at once. It also include some methods that allows
|
@@ -61,7 +61,7 @@ class Thor::Group
|
|
61
61
|
invocations[name] = false
|
62
62
|
invocation_blocks[name] = block if block_given?
|
63
63
|
|
64
|
-
class_eval <<-METHOD, __FILE__, __LINE__
|
64
|
+
class_eval <<-METHOD, __FILE__, __LINE__ + 1
|
65
65
|
def _invoke_#{name.to_s.gsub(/\W/, '_')}
|
66
66
|
klass, command = self.class.prepare_for_invocation(nil, #{name.inspect})
|
67
67
|
|
@@ -120,7 +120,7 @@ class Thor::Group
|
|
120
120
|
invocations[name] = true
|
121
121
|
invocation_blocks[name] = block if block_given?
|
122
122
|
|
123
|
-
class_eval <<-METHOD, __FILE__, __LINE__
|
123
|
+
class_eval <<-METHOD, __FILE__, __LINE__ + 1
|
124
124
|
def _invoke_from_option_#{name.to_s.gsub(/\W/, '_')}
|
125
125
|
return unless options[#{name.inspect}]
|
126
126
|
|
@@ -205,7 +205,7 @@ class Thor::Group
|
|
205
205
|
alias_method :printable_tasks, :printable_commands
|
206
206
|
|
207
207
|
def handle_argument_error(command, error, _args, arity) #:nodoc:
|
208
|
-
msg = "#{basename} #{command.name} takes #{arity} argument"
|
208
|
+
msg = "#{basename} #{command.name} takes #{arity} argument".dup
|
209
209
|
msg << "s" if arity > 1
|
210
210
|
msg << ", but it should not."
|
211
211
|
raise error, msg
|
data/lib/thor/invocation.rb
CHANGED
@@ -1,19 +1,19 @@
|
|
1
|
-
begin
|
2
|
-
require "readline"
|
3
|
-
rescue LoadError
|
4
|
-
end
|
5
|
-
|
6
1
|
class Thor
|
7
2
|
module LineEditor
|
8
3
|
class Readline < Basic
|
9
4
|
def self.available?
|
5
|
+
begin
|
6
|
+
require "readline"
|
7
|
+
rescue LoadError
|
8
|
+
end
|
9
|
+
|
10
10
|
Object.const_defined?(:Readline)
|
11
11
|
end
|
12
12
|
|
13
13
|
def readline
|
14
14
|
if echo?
|
15
15
|
::Readline.completion_append_character = nil
|
16
|
-
#
|
16
|
+
# rb-readline does not allow Readline.completion_proc= to receive nil.
|
17
17
|
if complete = completion_proc
|
18
18
|
::Readline.completion_proc = complete
|
19
19
|
end
|
data/lib/thor/line_editor.rb
CHANGED
@@ -0,0 +1,29 @@
|
|
1
|
+
class Thor
|
2
|
+
class NestedContext
|
3
|
+
def initialize
|
4
|
+
@depth = 0
|
5
|
+
end
|
6
|
+
|
7
|
+
def enter
|
8
|
+
push
|
9
|
+
|
10
|
+
yield
|
11
|
+
ensure
|
12
|
+
pop
|
13
|
+
end
|
14
|
+
|
15
|
+
def entered?
|
16
|
+
@depth > 0
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def push
|
22
|
+
@depth += 1
|
23
|
+
end
|
24
|
+
|
25
|
+
def pop
|
26
|
+
@depth -= 1
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -9,7 +9,7 @@ class Thor
|
|
9
9
|
arguments = []
|
10
10
|
|
11
11
|
args.each do |item|
|
12
|
-
break if item =~ /^-/
|
12
|
+
break if item.is_a?(String) && item =~ /^-/
|
13
13
|
arguments << item
|
14
14
|
end
|
15
15
|
|
@@ -30,7 +30,11 @@ class Thor
|
|
30
30
|
|
31
31
|
arguments.each do |argument|
|
32
32
|
if !argument.default.nil?
|
33
|
-
|
33
|
+
begin
|
34
|
+
@assigns[argument.human_name] = argument.default.dup
|
35
|
+
rescue TypeError # Compatibility shim for un-dup-able Fixnum in Ruby < 2.4
|
36
|
+
@assigns[argument.human_name] = argument.default
|
37
|
+
end
|
34
38
|
elsif argument.required?
|
35
39
|
@non_assigned_required << argument
|
36
40
|
end
|
@@ -82,7 +86,7 @@ class Thor
|
|
82
86
|
end
|
83
87
|
|
84
88
|
def current_is_value?
|
85
|
-
peek && peek.to_s !~
|
89
|
+
peek && peek.to_s !~ /^-{1,2}\S+/
|
86
90
|
end
|
87
91
|
|
88
92
|
# Runs through the argument array getting strings that contains ":" and
|