thor 0.19.4 → 1.0.1
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 +5 -5
- data/CHANGELOG.md +57 -0
- data/README.md +6 -2
- data/lib/thor.rb +37 -12
- data/lib/thor/actions.rb +31 -13
- 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 +58 -12
- data/lib/thor/actions/inject_into_file.rb +27 -10
- data/lib/thor/base.rb +84 -41
- data/lib/thor/command.rb +30 -21
- data/lib/thor/core_ext/hash_with_indifferent_access.rb +12 -0
- data/lib/thor/error.rb +78 -0
- data/lib/thor/group.rb +4 -4
- data/lib/thor/invocation.rb +1 -0
- data/lib/thor/line_editor.rb +2 -2
- data/lib/thor/line_editor/basic.rb +2 -0
- data/lib/thor/line_editor/readline.rb +6 -6
- data/lib/thor/nested_context.rb +29 -0
- data/lib/thor/parser.rb +4 -4
- data/lib/thor/parser/arguments.rb +2 -2
- data/lib/thor/parser/option.rb +22 -9
- data/lib/thor/parser/options.rb +25 -9
- data/lib/thor/rake_compat.rb +1 -0
- data/lib/thor/runner.rb +9 -6
- data/lib/thor/shell.rb +4 -4
- data/lib/thor/shell/basic.rb +72 -17
- data/lib/thor/shell/color.rb +6 -2
- data/lib/thor/shell/html.rb +3 -3
- data/lib/thor/util.rb +17 -1
- data/lib/thor/version.rb +1 -1
- data/thor.gemspec +9 -2
- metadata +19 -10
- data/lib/thor/core_ext/io_binary_read.rb +0 -12
- data/lib/thor/core_ext/ordered_hash.rb +0 -129
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
require_relative "empty_directory"
|
2
2
|
|
3
3
|
class Thor
|
4
4
|
module Actions
|
@@ -21,9 +21,14 @@ class Thor
|
|
21
21
|
# gems.split(" ").map{ |gem| " config.gem :#{gem}" }.join("\n")
|
22
22
|
# end
|
23
23
|
#
|
24
|
+
WARNINGS = { unchanged_no_flag: 'File unchanged! The supplied flag value not found!' }
|
25
|
+
|
24
26
|
def insert_into_file(destination, *args, &block)
|
25
27
|
data = block_given? ? block : args.shift
|
26
|
-
|
28
|
+
|
29
|
+
config = args.shift || {}
|
30
|
+
config[:after] = /\z/ unless config.key?(:before) || config.key?(:after)
|
31
|
+
|
27
32
|
action InjectIntoFile.new(self, destination, data, config)
|
28
33
|
end
|
29
34
|
alias_method :inject_into_file, :insert_into_file
|
@@ -45,15 +50,23 @@ class Thor
|
|
45
50
|
end
|
46
51
|
|
47
52
|
def invoke!
|
48
|
-
say_status :invoke
|
49
|
-
|
50
53
|
content = if @behavior == :after
|
51
54
|
'\0' + replacement
|
52
55
|
else
|
53
56
|
replacement + '\0'
|
54
57
|
end
|
55
58
|
|
56
|
-
|
59
|
+
if exists?
|
60
|
+
if replace!(/#{flag}/, content, config[:force])
|
61
|
+
say_status(:invoke)
|
62
|
+
else
|
63
|
+
say_status(:unchanged, warning: WARNINGS[:unchanged_no_flag], color: :red)
|
64
|
+
end
|
65
|
+
else
|
66
|
+
unless pretend?
|
67
|
+
raise Thor::Error, "The file #{ destination } does not appear to exist"
|
68
|
+
end
|
69
|
+
end
|
57
70
|
end
|
58
71
|
|
59
72
|
def revoke!
|
@@ -72,7 +85,7 @@ class Thor
|
|
72
85
|
|
73
86
|
protected
|
74
87
|
|
75
|
-
def say_status(behavior)
|
88
|
+
def say_status(behavior, warning: nil, color: nil)
|
76
89
|
status = if behavior == :invoke
|
77
90
|
if flag == /\A/
|
78
91
|
:prepend
|
@@ -81,21 +94,25 @@ class Thor
|
|
81
94
|
else
|
82
95
|
:insert
|
83
96
|
end
|
97
|
+
elsif warning
|
98
|
+
warning
|
84
99
|
else
|
85
100
|
:subtract
|
86
101
|
end
|
87
102
|
|
88
|
-
super(status, config[:verbose])
|
103
|
+
super(status, (color || config[:verbose]))
|
89
104
|
end
|
90
105
|
|
91
106
|
# Adds the content to the file.
|
92
107
|
#
|
93
108
|
def replace!(regexp, string, force)
|
94
|
-
return if
|
95
|
-
content = File.
|
109
|
+
return if pretend?
|
110
|
+
content = File.read(destination)
|
96
111
|
if force || !content.include?(replacement)
|
97
|
-
content.gsub!(regexp, string)
|
112
|
+
success = content.gsub!(regexp, string)
|
113
|
+
|
98
114
|
File.open(destination, "wb") { |file| file.write(content) }
|
115
|
+
success
|
99
116
|
end
|
100
117
|
end
|
101
118
|
end
|
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
|
|
@@ -51,6 +51,18 @@ class Thor
|
|
51
51
|
self
|
52
52
|
end
|
53
53
|
|
54
|
+
def reverse_merge(other)
|
55
|
+
self.class.new(other).merge(self)
|
56
|
+
end
|
57
|
+
|
58
|
+
def reverse_merge!(other_hash)
|
59
|
+
replace(reverse_merge(other_hash))
|
60
|
+
end
|
61
|
+
|
62
|
+
def replace(other_hash)
|
63
|
+
super(other_hash)
|
64
|
+
end
|
65
|
+
|
54
66
|
# Convert to a Hash with String keys.
|
55
67
|
def to_hash
|
56
68
|
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)
|
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,11 @@ class Thor
|
|
29
100
|
|
30
101
|
class MalformattedArgumentError < InvocationError
|
31
102
|
end
|
103
|
+
|
104
|
+
if Correctable
|
105
|
+
DidYouMean::SPELL_CHECKERS.merge!(
|
106
|
+
'Thor::UndefinedCommandError' => UndefinedCommandError::SpellChecker,
|
107
|
+
'Thor::UnknownArgumentError' => UnknownArgumentError::SpellChecker
|
108
|
+
)
|
109
|
+
end
|
32
110
|
end
|