thor 0.16.0 → 1.2.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 +7 -0
- data/CONTRIBUTING.md +15 -0
- data/README.md +23 -6
- data/bin/thor +1 -1
- data/lib/thor/actions/create_file.rb +34 -35
- data/lib/thor/actions/create_link.rb +9 -5
- data/lib/thor/actions/directory.rb +33 -23
- data/lib/thor/actions/empty_directory.rb +75 -85
- data/lib/thor/actions/file_manipulation.rb +103 -36
- data/lib/thor/actions/inject_into_file.rb +46 -36
- data/lib/thor/actions.rb +90 -68
- data/lib/thor/base.rb +302 -244
- data/lib/thor/command.rb +142 -0
- data/lib/thor/core_ext/hash_with_indifferent_access.rb +52 -24
- data/lib/thor/error.rb +90 -10
- data/lib/thor/group.rb +70 -74
- data/lib/thor/invocation.rb +63 -55
- data/lib/thor/line_editor/basic.rb +37 -0
- data/lib/thor/line_editor/readline.rb +88 -0
- data/lib/thor/line_editor.rb +17 -0
- data/lib/thor/nested_context.rb +29 -0
- data/lib/thor/parser/argument.rb +24 -28
- data/lib/thor/parser/arguments.rb +110 -102
- data/lib/thor/parser/option.rb +53 -15
- data/lib/thor/parser/options.rb +174 -97
- data/lib/thor/parser.rb +4 -4
- data/lib/thor/rake_compat.rb +12 -11
- data/lib/thor/runner.rb +159 -155
- data/lib/thor/shell/basic.rb +216 -93
- data/lib/thor/shell/color.rb +53 -40
- data/lib/thor/shell/html.rb +61 -58
- data/lib/thor/shell.rb +29 -36
- data/lib/thor/util.rb +231 -213
- data/lib/thor/version.rb +1 -1
- data/lib/thor.rb +303 -166
- data/thor.gemspec +27 -24
- metadata +36 -226
- data/.gitignore +0 -44
- data/.rspec +0 -2
- data/.travis.yml +0 -7
- data/CHANGELOG.rdoc +0 -134
- data/Gemfile +0 -15
- data/Thorfile +0 -30
- data/bin/rake2thor +0 -86
- data/lib/thor/core_ext/dir_escape.rb +0 -0
- data/lib/thor/core_ext/file_binary_read.rb +0 -9
- data/lib/thor/core_ext/ordered_hash.rb +0 -100
- data/lib/thor/task.rb +0 -132
- data/spec/actions/create_file_spec.rb +0 -170
- data/spec/actions/create_link_spec.rb +0 -81
- data/spec/actions/directory_spec.rb +0 -149
- data/spec/actions/empty_directory_spec.rb +0 -130
- data/spec/actions/file_manipulation_spec.rb +0 -370
- data/spec/actions/inject_into_file_spec.rb +0 -135
- data/spec/actions_spec.rb +0 -331
- data/spec/base_spec.rb +0 -279
- data/spec/core_ext/hash_with_indifferent_access_spec.rb +0 -43
- data/spec/core_ext/ordered_hash_spec.rb +0 -115
- data/spec/exit_condition_spec.rb +0 -19
- data/spec/fixtures/application.rb +0 -2
- data/spec/fixtures/app{1}/README +0 -3
- data/spec/fixtures/bundle/execute.rb +0 -6
- data/spec/fixtures/bundle/main.thor +0 -1
- data/spec/fixtures/doc/%file_name%.rb.tt +0 -1
- data/spec/fixtures/doc/COMMENTER +0 -10
- data/spec/fixtures/doc/README +0 -3
- data/spec/fixtures/doc/block_helper.rb +0 -3
- data/spec/fixtures/doc/components/.empty_directory +0 -0
- data/spec/fixtures/doc/config.rb +0 -1
- data/spec/fixtures/doc/config.yaml.tt +0 -1
- data/spec/fixtures/enum.thor +0 -10
- data/spec/fixtures/group.thor +0 -114
- data/spec/fixtures/invoke.thor +0 -112
- data/spec/fixtures/path with spaces +0 -0
- data/spec/fixtures/script.thor +0 -190
- data/spec/fixtures/task.thor +0 -10
- data/spec/group_spec.rb +0 -216
- data/spec/invocation_spec.rb +0 -100
- data/spec/parser/argument_spec.rb +0 -53
- data/spec/parser/arguments_spec.rb +0 -66
- data/spec/parser/option_spec.rb +0 -202
- data/spec/parser/options_spec.rb +0 -330
- data/spec/rake_compat_spec.rb +0 -72
- data/spec/register_spec.rb +0 -135
- data/spec/runner_spec.rb +0 -241
- data/spec/shell/basic_spec.rb +0 -300
- data/spec/shell/color_spec.rb +0 -81
- data/spec/shell/html_spec.rb +0 -32
- data/spec/shell_spec.rb +0 -47
- data/spec/spec_helper.rb +0 -59
- data/spec/task_spec.rb +0 -80
- data/spec/thor_spec.rb +0 -418
- data/spec/util_spec.rb +0 -196
data/lib/thor.rb
CHANGED
@@ -1,22 +1,31 @@
|
|
1
|
-
|
1
|
+
require_relative "thor/base"
|
2
2
|
|
3
3
|
class Thor
|
4
|
+
$thor_runner ||= false
|
4
5
|
class << self
|
5
|
-
#
|
6
|
+
# Allows for custom "Command" package naming.
|
7
|
+
#
|
8
|
+
# === Parameters
|
9
|
+
# name<String>
|
10
|
+
# options<Hash>
|
11
|
+
#
|
12
|
+
def package_name(name, _ = {})
|
13
|
+
@package_name = name.nil? || name == "" ? nil : name
|
14
|
+
end
|
15
|
+
|
16
|
+
# Sets the default command when thor is executed without an explicit command to be called.
|
6
17
|
#
|
7
18
|
# ==== Parameters
|
8
|
-
# meth<Symbol>:: name of the default
|
9
|
-
#
|
10
|
-
def
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
@default_task ||= from_superclass(:default_task, 'help')
|
16
|
-
else
|
17
|
-
@default_task = meth.to_s
|
19
|
+
# meth<Symbol>:: name of the default command
|
20
|
+
#
|
21
|
+
def default_command(meth = nil)
|
22
|
+
if meth
|
23
|
+
@default_command = meth == :none ? "help" : meth.to_s
|
24
|
+
else
|
25
|
+
@default_command ||= from_superclass(:default_command, "help")
|
18
26
|
end
|
19
27
|
end
|
28
|
+
alias_method :default_task, :default_command
|
20
29
|
|
21
30
|
# Registers another Thor subclass as a command.
|
22
31
|
#
|
@@ -25,7 +34,7 @@ class Thor
|
|
25
34
|
# command<String>:: Subcommand name to use
|
26
35
|
# usage<String>:: Short usage for the subcommand
|
27
36
|
# description<String>:: Description for the subcommand
|
28
|
-
def register(klass, subcommand_name, usage, description, options={})
|
37
|
+
def register(klass, subcommand_name, usage, description, options = {})
|
29
38
|
if klass <= Thor::Group
|
30
39
|
desc usage, description, options
|
31
40
|
define_method(subcommand_name) { |*args| invoke(klass, args) }
|
@@ -35,38 +44,40 @@ class Thor
|
|
35
44
|
end
|
36
45
|
end
|
37
46
|
|
38
|
-
# Defines the usage and the description of the next
|
47
|
+
# Defines the usage and the description of the next command.
|
39
48
|
#
|
40
49
|
# ==== Parameters
|
41
50
|
# usage<String>
|
42
51
|
# description<String>
|
43
52
|
# options<String>
|
44
53
|
#
|
45
|
-
def desc(usage, description, options={})
|
54
|
+
def desc(usage, description, options = {})
|
46
55
|
if options[:for]
|
47
|
-
|
48
|
-
|
49
|
-
|
56
|
+
command = find_and_refresh_command(options[:for])
|
57
|
+
command.usage = usage if usage
|
58
|
+
command.description = description if description
|
50
59
|
else
|
51
|
-
@usage
|
60
|
+
@usage = usage
|
61
|
+
@desc = description
|
62
|
+
@hide = options[:hide] || false
|
52
63
|
end
|
53
64
|
end
|
54
65
|
|
55
|
-
# Defines the long description of the next
|
66
|
+
# Defines the long description of the next command.
|
56
67
|
#
|
57
68
|
# ==== Parameters
|
58
69
|
# long description<String>
|
59
70
|
#
|
60
|
-
def long_desc(long_description, options={})
|
71
|
+
def long_desc(long_description, options = {})
|
61
72
|
if options[:for]
|
62
|
-
|
63
|
-
|
73
|
+
command = find_and_refresh_command(options[:for])
|
74
|
+
command.long_description = long_description if long_description
|
64
75
|
else
|
65
76
|
@long_desc = long_description
|
66
77
|
end
|
67
78
|
end
|
68
79
|
|
69
|
-
# Maps an input to a
|
80
|
+
# Maps an input to a command. If you define:
|
70
81
|
#
|
71
82
|
# map "-T" => "list"
|
72
83
|
#
|
@@ -74,18 +85,23 @@ class Thor
|
|
74
85
|
#
|
75
86
|
# thor -T
|
76
87
|
#
|
77
|
-
# Will invoke the list
|
88
|
+
# Will invoke the list command.
|
78
89
|
#
|
79
90
|
# ==== Parameters
|
80
|
-
# Hash[String|Array => Symbol]:: Maps the string or the strings in the array to the given
|
91
|
+
# Hash[String|Array => Symbol]:: Maps the string or the strings in the array to the given command.
|
81
92
|
#
|
82
|
-
def map(mappings=nil)
|
93
|
+
def map(mappings = nil, **kw)
|
83
94
|
@map ||= from_superclass(:map, {})
|
84
95
|
|
96
|
+
if mappings && !kw.empty?
|
97
|
+
mappings = kw.merge!(mappings)
|
98
|
+
else
|
99
|
+
mappings ||= kw
|
100
|
+
end
|
85
101
|
if mappings
|
86
102
|
mappings.each do |key, value|
|
87
103
|
if key.respond_to?(:each)
|
88
|
-
key.each {|subkey| @map[subkey] = value}
|
104
|
+
key.each { |subkey| @map[subkey] = value }
|
89
105
|
else
|
90
106
|
@map[key] = value
|
91
107
|
end
|
@@ -95,31 +111,31 @@ class Thor
|
|
95
111
|
@map
|
96
112
|
end
|
97
113
|
|
98
|
-
# Declares the options for the next
|
114
|
+
# Declares the options for the next command to be declared.
|
99
115
|
#
|
100
116
|
# ==== Parameters
|
101
117
|
# Hash[Symbol => Object]:: The hash key is the name of the option and the value
|
102
118
|
# is the type of the option. Can be :string, :array, :hash, :boolean, :numeric
|
103
119
|
# or :required (string). If you give a value, the type of the value is used.
|
104
120
|
#
|
105
|
-
def method_options(options=nil)
|
121
|
+
def method_options(options = nil)
|
106
122
|
@method_options ||= {}
|
107
123
|
build_options(options, @method_options) if options
|
108
124
|
@method_options
|
109
125
|
end
|
110
126
|
|
111
|
-
|
127
|
+
alias_method :options, :method_options
|
112
128
|
|
113
129
|
# Adds an option to the set of method options. If :for is given as option,
|
114
|
-
# it allows you to change the options from a previous defined
|
130
|
+
# it allows you to change the options from a previous defined command.
|
115
131
|
#
|
116
|
-
# def
|
132
|
+
# def previous_command
|
117
133
|
# # magic
|
118
134
|
# end
|
119
135
|
#
|
120
|
-
# method_option :foo => :bar, :for => :
|
136
|
+
# method_option :foo => :bar, :for => :previous_command
|
121
137
|
#
|
122
|
-
# def
|
138
|
+
# def next_command
|
123
139
|
# # magic
|
124
140
|
# end
|
125
141
|
#
|
@@ -136,40 +152,40 @@ class Thor
|
|
136
152
|
# :banner - String to show on usage notes.
|
137
153
|
# :hide - If you want to hide this option from the help.
|
138
154
|
#
|
139
|
-
def method_option(name, options={})
|
155
|
+
def method_option(name, options = {})
|
140
156
|
scope = if options[:for]
|
141
|
-
|
157
|
+
find_and_refresh_command(options[:for]).options
|
142
158
|
else
|
143
159
|
method_options
|
144
160
|
end
|
145
161
|
|
146
162
|
build_option(name, options, scope)
|
147
163
|
end
|
164
|
+
alias_method :option, :method_option
|
148
165
|
|
149
|
-
|
150
|
-
|
151
|
-
# Prints help information for the given task.
|
166
|
+
# Prints help information for the given command.
|
152
167
|
#
|
153
168
|
# ==== Parameters
|
154
169
|
# shell<Thor::Shell>
|
155
|
-
#
|
170
|
+
# command_name<String>
|
156
171
|
#
|
157
|
-
def
|
158
|
-
meth =
|
159
|
-
|
160
|
-
|
172
|
+
def command_help(shell, command_name)
|
173
|
+
meth = normalize_command_name(command_name)
|
174
|
+
command = all_commands[meth]
|
175
|
+
handle_no_command_error(meth) unless command
|
161
176
|
|
162
177
|
shell.say "Usage:"
|
163
|
-
shell.say " #{banner(
|
178
|
+
shell.say " #{banner(command).split("\n").join("\n ")}"
|
164
179
|
shell.say
|
165
|
-
class_options_help(shell, nil =>
|
166
|
-
if
|
180
|
+
class_options_help(shell, nil => command.options.values)
|
181
|
+
if command.long_description
|
167
182
|
shell.say "Description:"
|
168
|
-
shell.print_wrapped(
|
183
|
+
shell.print_wrapped(command.long_description, :indent => 2)
|
169
184
|
else
|
170
|
-
shell.say
|
185
|
+
shell.say command.description
|
171
186
|
end
|
172
187
|
end
|
188
|
+
alias_method :task_help, :command_help
|
173
189
|
|
174
190
|
# Prints help information for this class.
|
175
191
|
#
|
@@ -177,49 +193,67 @@ class Thor
|
|
177
193
|
# shell<Thor::Shell>
|
178
194
|
#
|
179
195
|
def help(shell, subcommand = false)
|
180
|
-
list =
|
196
|
+
list = printable_commands(true, subcommand)
|
181
197
|
Thor::Util.thor_classes_in(self).each do |klass|
|
182
|
-
list += klass.
|
198
|
+
list += klass.printable_commands(false)
|
199
|
+
end
|
200
|
+
list.sort! { |a, b| a[0] <=> b[0] }
|
201
|
+
|
202
|
+
if defined?(@package_name) && @package_name
|
203
|
+
shell.say "#{@package_name} commands:"
|
204
|
+
else
|
205
|
+
shell.say "Commands:"
|
183
206
|
end
|
184
|
-
list.sort!{ |a,b| a[0] <=> b[0] }
|
185
207
|
|
186
|
-
shell.say "Tasks:"
|
187
208
|
shell.print_table(list, :indent => 2, :truncate => true)
|
188
209
|
shell.say
|
189
210
|
class_options_help(shell)
|
190
211
|
end
|
191
212
|
|
192
|
-
# Returns
|
193
|
-
def
|
194
|
-
(all ?
|
195
|
-
next if
|
213
|
+
# Returns commands ready to be printed.
|
214
|
+
def printable_commands(all = true, subcommand = false)
|
215
|
+
(all ? all_commands : commands).map do |_, command|
|
216
|
+
next if command.hidden?
|
196
217
|
item = []
|
197
|
-
item << banner(
|
198
|
-
item << (
|
218
|
+
item << banner(command, false, subcommand)
|
219
|
+
item << (command.description ? "# #{command.description.gsub(/\s+/m, ' ')}" : "")
|
199
220
|
item
|
200
221
|
end.compact
|
201
222
|
end
|
223
|
+
alias_method :printable_tasks, :printable_commands
|
202
224
|
|
203
225
|
def subcommands
|
204
226
|
@subcommands ||= from_superclass(:subcommands, [])
|
205
227
|
end
|
228
|
+
alias_method :subtasks, :subcommands
|
229
|
+
|
230
|
+
def subcommand_classes
|
231
|
+
@subcommand_classes ||= {}
|
232
|
+
end
|
206
233
|
|
207
234
|
def subcommand(subcommand, subcommand_class)
|
208
|
-
|
235
|
+
subcommands << subcommand.to_s
|
209
236
|
subcommand_class.subcommand_help subcommand
|
237
|
+
subcommand_classes[subcommand.to_s] = subcommand_class
|
210
238
|
|
211
239
|
define_method(subcommand) do |*args|
|
212
240
|
args, opts = Thor::Arguments.split(args)
|
213
|
-
|
241
|
+
invoke_args = [args, opts, {:invoked_via_subcommand => true, :class_options => options}]
|
242
|
+
invoke_args.unshift "help" if opts.delete("--help") || opts.delete("-h")
|
243
|
+
invoke subcommand_class, *invoke_args
|
244
|
+
end
|
245
|
+
subcommand_class.commands.each do |_meth, command|
|
246
|
+
command.ancestor_name = subcommand
|
214
247
|
end
|
215
248
|
end
|
249
|
+
alias_method :subtask, :subcommand
|
216
250
|
|
217
251
|
# Extend check unknown options to accept a hash of conditions.
|
218
252
|
#
|
219
253
|
# === Parameters
|
220
254
|
# options<Hash>: A hash containing :only and/or :except keys
|
221
|
-
def check_unknown_options!(options={})
|
222
|
-
@check_unknown_options ||=
|
255
|
+
def check_unknown_options!(options = {})
|
256
|
+
@check_unknown_options ||= {}
|
223
257
|
options.each do |key, value|
|
224
258
|
if value
|
225
259
|
@check_unknown_options[key] = Array(value)
|
@@ -235,10 +269,10 @@ class Thor
|
|
235
269
|
options = check_unknown_options
|
236
270
|
return false unless options
|
237
271
|
|
238
|
-
|
239
|
-
return true unless
|
272
|
+
command = config[:current_command]
|
273
|
+
return true unless command
|
240
274
|
|
241
|
-
name =
|
275
|
+
name = command.name
|
242
276
|
|
243
277
|
if subcommands.include?(name)
|
244
278
|
false
|
@@ -251,129 +285,232 @@ class Thor
|
|
251
285
|
end
|
252
286
|
end
|
253
287
|
|
254
|
-
|
288
|
+
# Stop parsing of options as soon as an unknown option or a regular
|
289
|
+
# argument is encountered. All remaining arguments are passed to the command.
|
290
|
+
# This is useful if you have a command that can receive arbitrary additional
|
291
|
+
# options, and where those additional options should not be handled by
|
292
|
+
# Thor.
|
293
|
+
#
|
294
|
+
# ==== Example
|
295
|
+
#
|
296
|
+
# To better understand how this is useful, let's consider a command that calls
|
297
|
+
# an external command. A user may want to pass arbitrary options and
|
298
|
+
# arguments to that command. The command itself also accepts some options,
|
299
|
+
# which should be handled by Thor.
|
300
|
+
#
|
301
|
+
# class_option "verbose", :type => :boolean
|
302
|
+
# stop_on_unknown_option! :exec
|
303
|
+
# check_unknown_options! :except => :exec
|
304
|
+
#
|
305
|
+
# desc "exec", "Run a shell command"
|
306
|
+
# def exec(*args)
|
307
|
+
# puts "diagnostic output" if options[:verbose]
|
308
|
+
# Kernel.exec(*args)
|
309
|
+
# end
|
310
|
+
#
|
311
|
+
# Here +exec+ can be called with +--verbose+ to get diagnostic output,
|
312
|
+
# e.g.:
|
313
|
+
#
|
314
|
+
# $ thor exec --verbose echo foo
|
315
|
+
# diagnostic output
|
316
|
+
# foo
|
317
|
+
#
|
318
|
+
# But if +--verbose+ is given after +echo+, it is passed to +echo+ instead:
|
319
|
+
#
|
320
|
+
# $ thor exec echo --verbose foo
|
321
|
+
# --verbose foo
|
322
|
+
#
|
323
|
+
# ==== Parameters
|
324
|
+
# Symbol ...:: A list of commands that should be affected.
|
325
|
+
def stop_on_unknown_option!(*command_names)
|
326
|
+
@stop_on_unknown_option = stop_on_unknown_option | command_names
|
327
|
+
end
|
255
328
|
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
task = all_tasks[normalize_task_name(meth)]
|
329
|
+
def stop_on_unknown_option?(command) #:nodoc:
|
330
|
+
command && stop_on_unknown_option.include?(command.name.to_sym)
|
331
|
+
end
|
260
332
|
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
333
|
+
# Disable the check for required options for the given commands.
|
334
|
+
# This is useful if you have a command that does not need the required options
|
335
|
+
# to work, like help.
|
336
|
+
#
|
337
|
+
# ==== Parameters
|
338
|
+
# Symbol ...:: A list of commands that should be affected.
|
339
|
+
def disable_required_check!(*command_names)
|
340
|
+
@disable_required_check = disable_required_check | command_names
|
341
|
+
end
|
267
342
|
|
268
|
-
|
269
|
-
|
343
|
+
def disable_required_check?(command) #:nodoc:
|
344
|
+
command && disable_required_check.include?(command.name.to_sym)
|
345
|
+
end
|
270
346
|
|
271
|
-
|
272
|
-
yield instance if block_given?
|
273
|
-
args = instance.args
|
274
|
-
trailing = args[Range.new(arguments.size, -1)]
|
275
|
-
instance.invoke_task(task, trailing || [])
|
276
|
-
end
|
347
|
+
protected
|
277
348
|
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
# the namespace should be displayed as arguments.
|
282
|
-
#
|
283
|
-
def banner(task, namespace = nil, subcommand = false)
|
284
|
-
"#{basename} #{task.formatted_usage(self, $thor_runner, subcommand)}"
|
285
|
-
end
|
349
|
+
def stop_on_unknown_option #:nodoc:
|
350
|
+
@stop_on_unknown_option ||= []
|
351
|
+
end
|
286
352
|
|
287
|
-
|
288
|
-
|
289
|
-
|
353
|
+
# help command has the required check disabled by default.
|
354
|
+
def disable_required_check #:nodoc:
|
355
|
+
@disable_required_check ||= [:help]
|
356
|
+
end
|
290
357
|
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
@usage, @desc, @long_desc, @method_options, @hide = nil
|
296
|
-
true
|
297
|
-
elsif self.all_tasks[meth] || meth == "method_missing"
|
298
|
-
true
|
299
|
-
else
|
300
|
-
puts "[WARNING] Attempted to create task #{meth.inspect} without usage or description. " <<
|
301
|
-
"Call desc if you want this method to be available as task or declare it inside a " <<
|
302
|
-
"no_tasks{} block. Invoked from #{caller[1].inspect}."
|
303
|
-
false
|
304
|
-
end
|
305
|
-
end
|
358
|
+
# The method responsible for dispatching given the args.
|
359
|
+
def dispatch(meth, given_args, given_opts, config) #:nodoc: # rubocop:disable MethodLength
|
360
|
+
meth ||= retrieve_command_name(given_args)
|
361
|
+
command = all_commands[normalize_command_name(meth)]
|
306
362
|
|
307
|
-
|
308
|
-
|
309
|
-
|
363
|
+
if !command && config[:invoked_via_subcommand]
|
364
|
+
# We're a subcommand and our first argument didn't match any of our
|
365
|
+
# commands. So we put it back and call our default command.
|
366
|
+
given_args.unshift(meth)
|
367
|
+
command = all_commands[normalize_command_name(default_command)]
|
310
368
|
end
|
311
369
|
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
370
|
+
if command
|
371
|
+
args, opts = Thor::Options.split(given_args)
|
372
|
+
if stop_on_unknown_option?(command) && !args.empty?
|
373
|
+
# given_args starts with a non-option, so we treat everything as
|
374
|
+
# ordinary arguments
|
375
|
+
args.concat opts
|
376
|
+
opts.clear
|
319
377
|
end
|
378
|
+
else
|
379
|
+
args = given_args
|
380
|
+
opts = nil
|
381
|
+
command = dynamic_command_class.new(meth)
|
320
382
|
end
|
321
383
|
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
possibilities = find_task_possibilities(meth)
|
333
|
-
if possibilities.size > 1
|
334
|
-
raise ArgumentError, "Ambiguous task #{meth} matches [#{possibilities.join(', ')}]"
|
335
|
-
elsif possibilities.size < 1
|
336
|
-
meth = meth || default_task
|
337
|
-
elsif map[meth]
|
338
|
-
meth = map[meth]
|
339
|
-
else
|
340
|
-
meth = possibilities.first
|
341
|
-
end
|
384
|
+
opts = given_opts || opts || []
|
385
|
+
config[:current_command] = command
|
386
|
+
config[:command_options] = command.options
|
387
|
+
|
388
|
+
instance = new(args, opts, config)
|
389
|
+
yield instance if block_given?
|
390
|
+
args = instance.args
|
391
|
+
trailing = args[Range.new(arguments.size, -1)]
|
392
|
+
instance.invoke_command(command, trailing || [])
|
393
|
+
end
|
342
394
|
|
343
|
-
|
395
|
+
# The banner for this class. You can customize it if you are invoking the
|
396
|
+
# thor class by another ways which is not the Thor::Runner. It receives
|
397
|
+
# the command that is going to be invoked and a boolean which indicates if
|
398
|
+
# the namespace should be displayed as arguments.
|
399
|
+
#
|
400
|
+
def banner(command, namespace = nil, subcommand = false)
|
401
|
+
command.formatted_usage(self, $thor_runner, subcommand).split("\n").map do |formatted_usage|
|
402
|
+
"#{basename} #{formatted_usage}"
|
403
|
+
end.join("\n")
|
404
|
+
end
|
405
|
+
|
406
|
+
def baseclass #:nodoc:
|
407
|
+
Thor
|
408
|
+
end
|
409
|
+
|
410
|
+
def dynamic_command_class #:nodoc:
|
411
|
+
Thor::DynamicCommand
|
412
|
+
end
|
413
|
+
|
414
|
+
def create_command(meth) #:nodoc:
|
415
|
+
@usage ||= nil
|
416
|
+
@desc ||= nil
|
417
|
+
@long_desc ||= nil
|
418
|
+
@hide ||= nil
|
419
|
+
|
420
|
+
if @usage && @desc
|
421
|
+
base_class = @hide ? Thor::HiddenCommand : Thor::Command
|
422
|
+
commands[meth] = base_class.new(meth, @desc, @long_desc, @usage, method_options)
|
423
|
+
@usage, @desc, @long_desc, @method_options, @hide = nil
|
424
|
+
true
|
425
|
+
elsif all_commands[meth] || meth == "method_missing"
|
426
|
+
true
|
427
|
+
else
|
428
|
+
puts "[WARNING] Attempted to create command #{meth.inspect} without usage or description. " \
|
429
|
+
"Call desc if you want this method to be available as command or declare it inside a " \
|
430
|
+
"no_commands{} block. Invoked from #{caller[1].inspect}."
|
431
|
+
false
|
344
432
|
end
|
433
|
+
end
|
434
|
+
alias_method :create_task, :create_command
|
345
435
|
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
436
|
+
def initialize_added #:nodoc:
|
437
|
+
class_options.merge!(method_options)
|
438
|
+
@method_options = nil
|
439
|
+
end
|
440
|
+
|
441
|
+
# Retrieve the command name from given args.
|
442
|
+
def retrieve_command_name(args) #:nodoc:
|
443
|
+
meth = args.first.to_s unless args.empty?
|
444
|
+
args.shift if meth && (map[meth] || meth !~ /^\-/)
|
445
|
+
end
|
446
|
+
alias_method :retrieve_task_name, :retrieve_command_name
|
447
|
+
|
448
|
+
# receives a (possibly nil) command name and returns a name that is in
|
449
|
+
# the commands hash. In addition to normalizing aliases, this logic
|
450
|
+
# will determine if a shortened command is an unambiguous substring of
|
451
|
+
# a command or alias.
|
452
|
+
#
|
453
|
+
# +normalize_command_name+ also converts names like +animal-prison+
|
454
|
+
# into +animal_prison+.
|
455
|
+
def normalize_command_name(meth) #:nodoc:
|
456
|
+
return default_command.to_s.tr("-", "_") unless meth
|
457
|
+
|
458
|
+
possibilities = find_command_possibilities(meth)
|
459
|
+
raise AmbiguousTaskError, "Ambiguous command #{meth} matches [#{possibilities.join(', ')}]" if possibilities.size > 1
|
460
|
+
|
461
|
+
if possibilities.empty?
|
462
|
+
meth ||= default_command
|
463
|
+
elsif map[meth]
|
464
|
+
meth = map[meth]
|
465
|
+
else
|
466
|
+
meth = possibilities.first
|
361
467
|
end
|
362
468
|
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
469
|
+
meth.to_s.tr("-", "_") # treat foo-bar as foo_bar
|
470
|
+
end
|
471
|
+
alias_method :normalize_task_name, :normalize_command_name
|
472
|
+
|
473
|
+
# this is the logic that takes the command name passed in by the user
|
474
|
+
# and determines whether it is an unambiguous substrings of a command or
|
475
|
+
# alias name.
|
476
|
+
def find_command_possibilities(meth)
|
477
|
+
len = meth.to_s.length
|
478
|
+
possibilities = all_commands.merge(map).keys.select { |n| meth == n[0, len] }.sort
|
479
|
+
unique_possibilities = possibilities.map { |k| map[k] || k }.uniq
|
480
|
+
|
481
|
+
if possibilities.include?(meth)
|
482
|
+
[meth]
|
483
|
+
elsif unique_possibilities.size == 1
|
484
|
+
unique_possibilities
|
485
|
+
else
|
486
|
+
possibilities
|
368
487
|
end
|
488
|
+
end
|
489
|
+
alias_method :find_task_possibilities, :find_command_possibilities
|
490
|
+
|
491
|
+
def subcommand_help(cmd)
|
492
|
+
desc "help [COMMAND]", "Describe subcommands or one specific subcommand"
|
493
|
+
class_eval "
|
494
|
+
def help(command = nil, subcommand = true); super; end
|
495
|
+
"
|
496
|
+
end
|
497
|
+
alias_method :subtask_help, :subcommand_help
|
369
498
|
end
|
370
499
|
|
371
500
|
include Thor::Base
|
372
501
|
|
373
502
|
map HELP_MAPPINGS => :help
|
374
503
|
|
375
|
-
desc "help [
|
376
|
-
def help(
|
377
|
-
|
504
|
+
desc "help [COMMAND]", "Describe available commands or one specific command"
|
505
|
+
def help(command = nil, subcommand = false)
|
506
|
+
if command
|
507
|
+
if self.class.subcommands.include? command
|
508
|
+
self.class.subcommand_classes[command].help(shell, true)
|
509
|
+
else
|
510
|
+
self.class.command_help(shell, command)
|
511
|
+
end
|
512
|
+
else
|
513
|
+
self.class.help(shell, subcommand)
|
514
|
+
end
|
378
515
|
end
|
379
516
|
end
|