thor 0.14.6 → 0.19.4
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/.document +5 -0
- data/{CHANGELOG.rdoc → CHANGELOG.md} +91 -31
- data/CONTRIBUTING.md +15 -0
- data/{LICENSE → LICENSE.md} +2 -2
- data/README.md +36 -296
- data/bin/thor +1 -1
- data/lib/thor/actions/create_file.rb +33 -35
- data/lib/thor/actions/create_link.rb +7 -5
- data/lib/thor/actions/directory.rb +49 -24
- data/lib/thor/actions/empty_directory.rb +68 -67
- data/lib/thor/actions/file_manipulation.rb +85 -28
- data/lib/thor/actions/inject_into_file.rb +26 -32
- data/lib/thor/actions.rb +70 -66
- data/lib/thor/base.rb +314 -237
- data/lib/thor/command.rb +133 -0
- data/lib/thor/core_ext/hash_with_indifferent_access.rb +34 -24
- data/lib/thor/core_ext/io_binary_read.rb +12 -0
- data/lib/thor/core_ext/ordered_hash.rb +94 -65
- data/lib/thor/error.rb +10 -8
- data/lib/thor/group.rb +74 -66
- data/lib/thor/invocation.rb +65 -56
- data/lib/thor/line_editor/basic.rb +35 -0
- data/lib/thor/line_editor/readline.rb +88 -0
- data/lib/thor/line_editor.rb +17 -0
- data/lib/thor/parser/argument.rb +32 -29
- data/lib/thor/parser/arguments.rb +107 -93
- data/lib/thor/parser/option.rb +39 -13
- data/lib/thor/parser/options.rb +144 -97
- data/lib/thor/parser.rb +4 -4
- data/lib/thor/rake_compat.rb +21 -16
- data/lib/thor/runner.rb +166 -153
- data/lib/thor/shell/basic.rb +268 -122
- data/lib/thor/shell/color.rb +84 -43
- data/lib/thor/shell/html.rb +71 -66
- data/lib/thor/shell.rb +30 -37
- data/lib/thor/util.rb +227 -188
- data/lib/thor/version.rb +1 -1
- data/lib/thor.rb +289 -131
- data/thor.gemspec +21 -0
- metadata +50 -189
- data/Thorfile +0 -24
- data/bin/rake2thor +0 -86
- data/lib/thor/core_ext/file_binary_read.rb +0 -9
- data/lib/thor/task.rb +0 -114
- data/spec/actions/create_file_spec.rb +0 -170
- data/spec/actions/directory_spec.rb +0 -136
- data/spec/actions/empty_directory_spec.rb +0 -98
- data/spec/actions/file_manipulation_spec.rb +0 -310
- data/spec/actions/inject_into_file_spec.rb +0 -135
- data/spec/actions_spec.rb +0 -322
- data/spec/base_spec.rb +0 -269
- data/spec/core_ext/hash_with_indifferent_access_spec.rb +0 -43
- data/spec/core_ext/ordered_hash_spec.rb +0 -115
- data/spec/fixtures/application.rb +0 -2
- 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/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/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 -184
- data/spec/fixtures/task.thor +0 -10
- data/spec/group_spec.rb +0 -178
- data/spec/invocation_spec.rb +0 -100
- data/spec/parser/argument_spec.rb +0 -47
- data/spec/parser/arguments_spec.rb +0 -64
- data/spec/parser/option_spec.rb +0 -202
- data/spec/parser/options_spec.rb +0 -319
- data/spec/rake_compat_spec.rb +0 -68
- data/spec/register_spec.rb +0 -92
- data/spec/runner_spec.rb +0 -210
- data/spec/shell/basic_spec.rb +0 -223
- data/spec/shell/color_spec.rb +0 -41
- data/spec/shell/html_spec.rb +0 -27
- data/spec/shell_spec.rb +0 -47
- data/spec/spec_helper.rb +0 -54
- data/spec/task_spec.rb +0 -69
- data/spec/thor_spec.rb +0 -334
- data/spec/util_spec.rb +0 -163
data/lib/thor/base.rb
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
|
-
require
|
|
2
|
-
require
|
|
3
|
-
require
|
|
4
|
-
require
|
|
5
|
-
require
|
|
6
|
-
require
|
|
7
|
-
require
|
|
8
|
-
require
|
|
1
|
+
require "thor/command"
|
|
2
|
+
require "thor/core_ext/hash_with_indifferent_access"
|
|
3
|
+
require "thor/core_ext/ordered_hash"
|
|
4
|
+
require "thor/error"
|
|
5
|
+
require "thor/invocation"
|
|
6
|
+
require "thor/parser"
|
|
7
|
+
require "thor/shell"
|
|
8
|
+
require "thor/line_editor"
|
|
9
|
+
require "thor/util"
|
|
9
10
|
|
|
10
11
|
class Thor
|
|
11
|
-
autoload :Actions,
|
|
12
|
-
autoload :RakeCompat,
|
|
12
|
+
autoload :Actions, "thor/actions"
|
|
13
|
+
autoload :RakeCompat, "thor/rake_compat"
|
|
14
|
+
autoload :Group, "thor/group"
|
|
13
15
|
|
|
14
16
|
# Shortcuts for help.
|
|
15
17
|
HELP_MAPPINGS = %w(-h -? --help -D)
|
|
@@ -18,8 +20,10 @@ class Thor
|
|
|
18
20
|
THOR_RESERVED_WORDS = %w(invoke shell options behavior root destination_root relative_root
|
|
19
21
|
action add_file create_file in_root inside run run_ruby_script)
|
|
20
22
|
|
|
23
|
+
TEMPLATE_EXTNAME = ".tt"
|
|
24
|
+
|
|
21
25
|
module Base
|
|
22
|
-
attr_accessor :options
|
|
26
|
+
attr_accessor :options, :parent_options, :args
|
|
23
27
|
|
|
24
28
|
# It receives arguments in an Array and two hashes, one for options and
|
|
25
29
|
# other for configuration.
|
|
@@ -37,28 +41,54 @@ class Thor
|
|
|
37
41
|
#
|
|
38
42
|
# config<Hash>:: Configuration for this Thor class.
|
|
39
43
|
#
|
|
40
|
-
def initialize(args=[],
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
44
|
+
def initialize(args = [], local_options = {}, config = {})
|
|
45
|
+
parse_options = config[:current_command] && config[:current_command].disable_class_options ? {} : self.class.class_options
|
|
46
|
+
|
|
47
|
+
# The start method splits inbound arguments at the first argument
|
|
48
|
+
# that looks like an option (starts with - or --). It then calls
|
|
49
|
+
# new, passing in the two halves of the arguments Array as the
|
|
50
|
+
# first two parameters.
|
|
51
|
+
|
|
52
|
+
command_options = config.delete(:command_options) # hook for start
|
|
53
|
+
parse_options = parse_options.merge(command_options) if command_options
|
|
54
|
+
if local_options.is_a?(Array)
|
|
55
|
+
array_options = local_options
|
|
56
|
+
hash_options = {}
|
|
50
57
|
else
|
|
51
|
-
|
|
58
|
+
# Handle the case where the class was explicitly instantiated
|
|
59
|
+
# with pre-parsed options.
|
|
60
|
+
array_options = []
|
|
61
|
+
hash_options = local_options
|
|
52
62
|
end
|
|
53
63
|
|
|
54
|
-
|
|
64
|
+
# Let Thor::Options parse the options first, so it can remove
|
|
65
|
+
# declared options from the array. This will leave us with
|
|
66
|
+
# a list of arguments that weren't declared.
|
|
67
|
+
stop_on_unknown = self.class.stop_on_unknown_option? config[:current_command]
|
|
68
|
+
opts = Thor::Options.new(parse_options, hash_options, stop_on_unknown)
|
|
55
69
|
self.options = opts.parse(array_options)
|
|
70
|
+
self.options = config[:class_options].merge(options) if config[:class_options]
|
|
71
|
+
|
|
72
|
+
# If unknown options are disallowed, make sure that none of the
|
|
73
|
+
# remaining arguments looks like an option.
|
|
56
74
|
opts.check_unknown! if self.class.check_unknown_options?(config)
|
|
75
|
+
|
|
76
|
+
# Add the remaining arguments from the options parser to the
|
|
77
|
+
# arguments passed in to initialize. Then remove any positional
|
|
78
|
+
# arguments declared using #argument (this is primarily used
|
|
79
|
+
# by Thor::Group). Tis will leave us with the remaining
|
|
80
|
+
# positional arguments.
|
|
81
|
+
to_parse = args
|
|
82
|
+
to_parse += opts.remaining unless self.class.strict_args_position?(config)
|
|
83
|
+
|
|
84
|
+
thor_args = Thor::Arguments.new(self.class.arguments)
|
|
85
|
+
thor_args.parse(to_parse).each { |k, v| __send__("#{k}=", v) }
|
|
86
|
+
@args = thor_args.remaining
|
|
57
87
|
end
|
|
58
88
|
|
|
59
89
|
class << self
|
|
60
90
|
def included(base) #:nodoc:
|
|
61
|
-
base.
|
|
91
|
+
base.extend ClassMethods
|
|
62
92
|
base.send :include, Invocation
|
|
63
93
|
base.send :include, Shell
|
|
64
94
|
end
|
|
@@ -78,7 +108,7 @@ class Thor
|
|
|
78
108
|
# Hash[path<String> => Class]
|
|
79
109
|
#
|
|
80
110
|
def subclass_files
|
|
81
|
-
@subclass_files ||= Hash.new{ |h,k| h[k] = [] }
|
|
111
|
+
@subclass_files ||= Hash.new { |h, k| h[k] = [] }
|
|
82
112
|
end
|
|
83
113
|
|
|
84
114
|
# Whenever a class inherits from Thor or Thor::Group, we should track the
|
|
@@ -94,18 +124,16 @@ class Thor
|
|
|
94
124
|
end
|
|
95
125
|
|
|
96
126
|
module ClassMethods
|
|
97
|
-
attr_accessor :debugging
|
|
98
|
-
|
|
99
127
|
def attr_reader(*) #:nodoc:
|
|
100
|
-
|
|
128
|
+
no_commands { super }
|
|
101
129
|
end
|
|
102
130
|
|
|
103
131
|
def attr_writer(*) #:nodoc:
|
|
104
|
-
|
|
132
|
+
no_commands { super }
|
|
105
133
|
end
|
|
106
134
|
|
|
107
135
|
def attr_accessor(*) #:nodoc:
|
|
108
|
-
|
|
136
|
+
no_commands { super }
|
|
109
137
|
end
|
|
110
138
|
|
|
111
139
|
# If you want to raise an error for unknown options, call check_unknown_options!
|
|
@@ -122,17 +150,39 @@ class Thor
|
|
|
122
150
|
!!check_unknown_options
|
|
123
151
|
end
|
|
124
152
|
|
|
153
|
+
# If true, option parsing is suspended as soon as an unknown option or a
|
|
154
|
+
# regular argument is encountered. All remaining arguments are passed to
|
|
155
|
+
# the command as regular arguments.
|
|
156
|
+
def stop_on_unknown_option?(command_name) #:nodoc:
|
|
157
|
+
false
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
# If you want only strict string args (useful when cascading thor classes),
|
|
161
|
+
# call strict_args_position! This is disabled by default to allow dynamic
|
|
162
|
+
# invocations.
|
|
163
|
+
def strict_args_position!
|
|
164
|
+
@strict_args_position = true
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def strict_args_position #:nodoc:
|
|
168
|
+
@strict_args_position ||= from_superclass(:strict_args_position, false)
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def strict_args_position?(config) #:nodoc:
|
|
172
|
+
!!strict_args_position
|
|
173
|
+
end
|
|
174
|
+
|
|
125
175
|
# Adds an argument to the class and creates an attr_accessor for it.
|
|
126
176
|
#
|
|
127
177
|
# Arguments are different from options in several aspects. The first one
|
|
128
178
|
# is how they are parsed from the command line, arguments are retrieved
|
|
129
179
|
# from position:
|
|
130
180
|
#
|
|
131
|
-
# thor
|
|
181
|
+
# thor command NAME
|
|
132
182
|
#
|
|
133
183
|
# Instead of:
|
|
134
184
|
#
|
|
135
|
-
# thor
|
|
185
|
+
# thor command --name=NAME
|
|
136
186
|
#
|
|
137
187
|
# Besides, arguments are used inside your code as an accessor (self.argument),
|
|
138
188
|
# while options are all kept in a hash (self.options).
|
|
@@ -157,9 +207,9 @@ class Thor
|
|
|
157
207
|
# ==== Errors
|
|
158
208
|
# ArgumentError:: Raised if you supply a required argument after a non required one.
|
|
159
209
|
#
|
|
160
|
-
def argument(name, options={})
|
|
210
|
+
def argument(name, options = {})
|
|
161
211
|
is_thor_reserved_word?(name, :argument)
|
|
162
|
-
|
|
212
|
+
no_commands { attr_accessor name }
|
|
163
213
|
|
|
164
214
|
required = if options.key?(:optional)
|
|
165
215
|
!options[:optional]
|
|
@@ -171,14 +221,17 @@ class Thor
|
|
|
171
221
|
|
|
172
222
|
remove_argument name
|
|
173
223
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
224
|
+
if required
|
|
225
|
+
arguments.each do |argument|
|
|
226
|
+
next if argument.required?
|
|
227
|
+
raise ArgumentError, "You cannot have #{name.to_s.inspect} as required argument after " \
|
|
228
|
+
"the non-required argument #{argument.human_name.inspect}."
|
|
229
|
+
end
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
options[:required] = required
|
|
179
233
|
|
|
180
|
-
arguments << Thor::Argument.new(name, options
|
|
181
|
-
options[:default], options[:banner])
|
|
234
|
+
arguments << Thor::Argument.new(name, options)
|
|
182
235
|
end
|
|
183
236
|
|
|
184
237
|
# Returns this class arguments, looking up in the ancestors chain.
|
|
@@ -199,7 +252,7 @@ class Thor
|
|
|
199
252
|
# ==== Parameters
|
|
200
253
|
# Hash[Symbol => Object]
|
|
201
254
|
#
|
|
202
|
-
def class_options(options=nil)
|
|
255
|
+
def class_options(options = nil)
|
|
203
256
|
@class_options ||= from_superclass(:class_options, {})
|
|
204
257
|
build_options(options, @class_options) if options
|
|
205
258
|
@class_options
|
|
@@ -212,22 +265,23 @@ class Thor
|
|
|
212
265
|
# options<Hash>:: Described below.
|
|
213
266
|
#
|
|
214
267
|
# ==== Options
|
|
215
|
-
# :desc
|
|
216
|
-
# :required
|
|
217
|
-
# :default
|
|
218
|
-
# :group
|
|
219
|
-
# :aliases
|
|
220
|
-
# :type
|
|
221
|
-
# :banner
|
|
222
|
-
#
|
|
223
|
-
|
|
268
|
+
# :desc:: -- Description for the argument.
|
|
269
|
+
# :required:: -- If the argument is required or not.
|
|
270
|
+
# :default:: -- Default value for this argument.
|
|
271
|
+
# :group:: -- The group for this options. Use by class options to output options in different levels.
|
|
272
|
+
# :aliases:: -- Aliases for this option. <b>Note:</b> Thor follows a convention of one-dash-one-letter options. Thus aliases like "-something" wouldn't be parsed; use either "\--something" or "-s" instead.
|
|
273
|
+
# :type:: -- The type of the argument, can be :string, :hash, :array, :numeric or :boolean.
|
|
274
|
+
# :banner:: -- String to show on usage notes.
|
|
275
|
+
# :hide:: -- If you want to hide this option from the help.
|
|
276
|
+
#
|
|
277
|
+
def class_option(name, options = {})
|
|
224
278
|
build_option(name, options, class_options)
|
|
225
279
|
end
|
|
226
280
|
|
|
227
281
|
# Removes a previous defined argument. If :undefine is given, undefine
|
|
228
282
|
# accessors as well.
|
|
229
283
|
#
|
|
230
|
-
# ====
|
|
284
|
+
# ==== Parameters
|
|
231
285
|
# names<Array>:: Arguments to be removed
|
|
232
286
|
#
|
|
233
287
|
# ==== Examples
|
|
@@ -246,7 +300,7 @@ class Thor
|
|
|
246
300
|
|
|
247
301
|
# Removes a previous defined class option.
|
|
248
302
|
#
|
|
249
|
-
# ====
|
|
303
|
+
# ==== Parameters
|
|
250
304
|
# names<Array>:: Class options to be removed
|
|
251
305
|
#
|
|
252
306
|
# ==== Examples
|
|
@@ -261,88 +315,91 @@ class Thor
|
|
|
261
315
|
end
|
|
262
316
|
|
|
263
317
|
# Defines the group. This is used when thor list is invoked so you can specify
|
|
264
|
-
# that only
|
|
318
|
+
# that only commands from a pre-defined group will be shown. Defaults to standard.
|
|
265
319
|
#
|
|
266
320
|
# ==== Parameters
|
|
267
321
|
# name<String|Symbol>
|
|
268
322
|
#
|
|
269
|
-
def group(name=nil)
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
@group = name.to_s
|
|
323
|
+
def group(name = nil)
|
|
324
|
+
if name
|
|
325
|
+
@group = name.to_s
|
|
326
|
+
else
|
|
327
|
+
@group ||= from_superclass(:group, "standard")
|
|
275
328
|
end
|
|
276
329
|
end
|
|
277
330
|
|
|
278
|
-
# Returns the
|
|
331
|
+
# Returns the commands for this Thor class.
|
|
279
332
|
#
|
|
280
333
|
# ==== Returns
|
|
281
|
-
# OrderedHash:: An ordered hash with
|
|
334
|
+
# OrderedHash:: An ordered hash with commands names as keys and Thor::Command
|
|
282
335
|
# objects as values.
|
|
283
336
|
#
|
|
284
|
-
def
|
|
285
|
-
@
|
|
337
|
+
def commands
|
|
338
|
+
@commands ||= Thor::CoreExt::OrderedHash.new
|
|
286
339
|
end
|
|
340
|
+
alias_method :tasks, :commands
|
|
287
341
|
|
|
288
|
-
# Returns the
|
|
342
|
+
# Returns the commands for this Thor class and all subclasses.
|
|
289
343
|
#
|
|
290
344
|
# ==== Returns
|
|
291
|
-
# OrderedHash:: An ordered hash with
|
|
345
|
+
# OrderedHash:: An ordered hash with commands names as keys and Thor::Command
|
|
292
346
|
# objects as values.
|
|
293
347
|
#
|
|
294
|
-
def
|
|
295
|
-
@
|
|
296
|
-
@
|
|
348
|
+
def all_commands
|
|
349
|
+
@all_commands ||= from_superclass(:all_commands, Thor::CoreExt::OrderedHash.new)
|
|
350
|
+
@all_commands.merge!(commands)
|
|
297
351
|
end
|
|
352
|
+
alias_method :all_tasks, :all_commands
|
|
298
353
|
|
|
299
|
-
# Removes a given
|
|
354
|
+
# Removes a given command from this Thor class. This is usually done if you
|
|
300
355
|
# are inheriting from another class and don't want it to be available
|
|
301
356
|
# anymore.
|
|
302
357
|
#
|
|
303
|
-
# By default it only remove the mapping to the
|
|
358
|
+
# By default it only remove the mapping to the command. But you can supply
|
|
304
359
|
# :undefine => true to undefine the method from the class as well.
|
|
305
360
|
#
|
|
306
361
|
# ==== Parameters
|
|
307
|
-
# name<Symbol|String>:: The name of the
|
|
308
|
-
# options<Hash>:: You can give :undefine => true if you want
|
|
362
|
+
# name<Symbol|String>:: The name of the command to be removed
|
|
363
|
+
# options<Hash>:: You can give :undefine => true if you want commands the method
|
|
309
364
|
# to be undefined from the class as well.
|
|
310
365
|
#
|
|
311
|
-
def
|
|
366
|
+
def remove_command(*names)
|
|
312
367
|
options = names.last.is_a?(Hash) ? names.pop : {}
|
|
313
368
|
|
|
314
369
|
names.each do |name|
|
|
315
|
-
|
|
316
|
-
|
|
370
|
+
commands.delete(name.to_s)
|
|
371
|
+
all_commands.delete(name.to_s)
|
|
317
372
|
undef_method name if options[:undefine]
|
|
318
373
|
end
|
|
319
374
|
end
|
|
375
|
+
alias_method :remove_task, :remove_command
|
|
320
376
|
|
|
321
|
-
# All methods defined inside the given block are not added as
|
|
377
|
+
# All methods defined inside the given block are not added as commands.
|
|
322
378
|
#
|
|
323
379
|
# So you can do:
|
|
324
380
|
#
|
|
325
381
|
# class MyScript < Thor
|
|
326
|
-
#
|
|
327
|
-
# def
|
|
382
|
+
# no_commands do
|
|
383
|
+
# def this_is_not_a_command
|
|
328
384
|
# end
|
|
329
385
|
# end
|
|
330
386
|
# end
|
|
331
387
|
#
|
|
332
|
-
# You can also add the method and remove it from the
|
|
388
|
+
# You can also add the method and remove it from the command list:
|
|
333
389
|
#
|
|
334
390
|
# class MyScript < Thor
|
|
335
|
-
# def
|
|
391
|
+
# def this_is_not_a_command
|
|
336
392
|
# end
|
|
337
|
-
#
|
|
393
|
+
# remove_command :this_is_not_a_command
|
|
338
394
|
# end
|
|
339
395
|
#
|
|
340
|
-
def
|
|
341
|
-
@
|
|
396
|
+
def no_commands
|
|
397
|
+
@no_commands = true
|
|
342
398
|
yield
|
|
343
399
|
ensure
|
|
344
|
-
@
|
|
400
|
+
@no_commands = false
|
|
345
401
|
end
|
|
402
|
+
alias_method :no_tasks, :no_commands
|
|
346
403
|
|
|
347
404
|
# Sets the namespace for the Thor or Thor::Group class. By default the
|
|
348
405
|
# namespace is retrieved from the class name. If your Thor class is named
|
|
@@ -354,7 +411,7 @@ class Thor
|
|
|
354
411
|
#
|
|
355
412
|
# namespace :my_scripts
|
|
356
413
|
#
|
|
357
|
-
# You change how your
|
|
414
|
+
# You change how your commands are invoked:
|
|
358
415
|
#
|
|
359
416
|
# thor my_scripts -h
|
|
360
417
|
#
|
|
@@ -362,218 +419,238 @@ class Thor
|
|
|
362
419
|
#
|
|
363
420
|
# namespace :default
|
|
364
421
|
#
|
|
365
|
-
# Your
|
|
422
|
+
# Your commands can be invoked with a shortcut. Instead of:
|
|
366
423
|
#
|
|
367
|
-
# thor :
|
|
424
|
+
# thor :my_command
|
|
368
425
|
#
|
|
369
|
-
def namespace(name=nil)
|
|
370
|
-
|
|
371
|
-
when nil
|
|
372
|
-
@namespace ||= Thor::Util.namespace_from_thor_class(self)
|
|
373
|
-
else
|
|
426
|
+
def namespace(name = nil)
|
|
427
|
+
if name
|
|
374
428
|
@namespace = name.to_s
|
|
429
|
+
else
|
|
430
|
+
@namespace ||= Thor::Util.namespace_from_thor_class(self)
|
|
375
431
|
end
|
|
376
432
|
end
|
|
377
433
|
|
|
378
|
-
# Parses the
|
|
379
|
-
# and invoke the
|
|
434
|
+
# Parses the command and options from the given args, instantiate the class
|
|
435
|
+
# and invoke the command. This method is used when the arguments must be parsed
|
|
380
436
|
# from an array. If you are inside Ruby and want to use a Thor class, you
|
|
381
437
|
# can simply initialize it:
|
|
382
438
|
#
|
|
383
439
|
# script = MyScript.new(args, options, config)
|
|
384
|
-
# script.invoke(:
|
|
440
|
+
# script.invoke(:command, first_arg, second_arg, third_arg)
|
|
385
441
|
#
|
|
386
|
-
def start(given_args=ARGV, config={})
|
|
387
|
-
self.debugging = given_args.delete("--debug")
|
|
442
|
+
def start(given_args = ARGV, config = {})
|
|
388
443
|
config[:shell] ||= Thor::Base.shell.new
|
|
389
444
|
dispatch(nil, given_args.dup, nil, config)
|
|
390
445
|
rescue Thor::Error => e
|
|
391
|
-
|
|
446
|
+
config[:debug] || ENV["THOR_DEBUG"] == "1" ? (raise e) : config[:shell].error(e.message)
|
|
392
447
|
exit(1) if exit_on_failure?
|
|
448
|
+
rescue Errno::EPIPE
|
|
449
|
+
# This happens if a thor command is piped to something like `head`,
|
|
450
|
+
# which closes the pipe when it's done reading. This will also
|
|
451
|
+
# mean that if the pipe is closed, further unnecessary
|
|
452
|
+
# computation will not occur.
|
|
453
|
+
exit(0)
|
|
393
454
|
end
|
|
394
455
|
|
|
395
|
-
# Allows to use private methods from parent in child classes as
|
|
456
|
+
# Allows to use private methods from parent in child classes as commands.
|
|
396
457
|
#
|
|
397
|
-
# ====
|
|
398
|
-
# names<Array>:: Method names to be used as
|
|
458
|
+
# ==== Parameters
|
|
459
|
+
# names<Array>:: Method names to be used as commands
|
|
399
460
|
#
|
|
400
461
|
# ==== Examples
|
|
401
462
|
#
|
|
402
|
-
#
|
|
403
|
-
#
|
|
463
|
+
# public_command :foo
|
|
464
|
+
# public_command :foo, :bar, :baz
|
|
404
465
|
#
|
|
405
|
-
def
|
|
466
|
+
def public_command(*names)
|
|
406
467
|
names.each do |name|
|
|
407
468
|
class_eval "def #{name}(*); super end"
|
|
408
469
|
end
|
|
409
470
|
end
|
|
471
|
+
alias_method :public_task, :public_command
|
|
410
472
|
|
|
411
|
-
def
|
|
412
|
-
if
|
|
413
|
-
|
|
414
|
-
else
|
|
415
|
-
raise UndefinedTaskError, "Could not find task #{task.inspect}."
|
|
416
|
-
end
|
|
473
|
+
def handle_no_command_error(command, has_namespace = $thor_runner) #:nodoc:
|
|
474
|
+
raise UndefinedCommandError, "Could not find command #{command.inspect} in #{namespace.inspect} namespace." if has_namespace
|
|
475
|
+
raise UndefinedCommandError, "Could not find command #{command.inspect}."
|
|
417
476
|
end
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
477
|
+
alias_method :handle_no_task_error, :handle_no_command_error
|
|
478
|
+
|
|
479
|
+
def handle_argument_error(command, error, args, arity) #:nodoc:
|
|
480
|
+
msg = "ERROR: \"#{basename} #{command.name}\" was called with "
|
|
481
|
+
msg << "no arguments" if args.empty?
|
|
482
|
+
msg << "arguments " << args.inspect unless args.empty?
|
|
483
|
+
msg << "\nUsage: #{banner(command).inspect}"
|
|
484
|
+
raise InvocationError, msg
|
|
421
485
|
end
|
|
422
486
|
|
|
423
|
-
|
|
487
|
+
protected
|
|
424
488
|
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
489
|
+
# Prints the class options per group. If an option does not belong to
|
|
490
|
+
# any group, it's printed as Class option.
|
|
491
|
+
#
|
|
492
|
+
def class_options_help(shell, groups = {}) #:nodoc:
|
|
493
|
+
# Group options by group
|
|
494
|
+
class_options.each do |_, value|
|
|
495
|
+
groups[value.group] ||= []
|
|
496
|
+
groups[value.group] << value
|
|
497
|
+
end
|
|
434
498
|
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
499
|
+
# Deal with default group
|
|
500
|
+
global_options = groups.delete(nil) || []
|
|
501
|
+
print_options(shell, global_options)
|
|
438
502
|
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
end
|
|
503
|
+
# Print all others
|
|
504
|
+
groups.each do |group_name, options|
|
|
505
|
+
print_options(shell, options, group_name)
|
|
443
506
|
end
|
|
507
|
+
end
|
|
444
508
|
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
509
|
+
# Receives a set of options and print them.
|
|
510
|
+
def print_options(shell, options, group_name = nil)
|
|
511
|
+
return if options.empty?
|
|
448
512
|
|
|
449
|
-
|
|
450
|
-
|
|
513
|
+
list = []
|
|
514
|
+
padding = options.map { |o| o.aliases.size }.max.to_i * 4
|
|
451
515
|
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
516
|
+
options.each do |option|
|
|
517
|
+
next if option.hide
|
|
518
|
+
item = [option.usage(padding)]
|
|
519
|
+
item.push(option.description ? "# #{option.description}" : "")
|
|
455
520
|
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
shell.say(group_name ? "#{group_name} options:" : "Options:")
|
|
461
|
-
shell.print_table(list, :ident => 2)
|
|
462
|
-
shell.say ""
|
|
521
|
+
list << item
|
|
522
|
+
list << ["", "# Default: #{option.default}"] if option.show_default?
|
|
523
|
+
list << ["", "# Possible values: #{option.enum.join(', ')}"] if option.enum
|
|
463
524
|
end
|
|
464
525
|
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
end
|
|
526
|
+
shell.say(group_name ? "#{group_name} options:" : "Options:")
|
|
527
|
+
shell.print_table(list, :indent => 2)
|
|
528
|
+
shell.say ""
|
|
529
|
+
end
|
|
470
530
|
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
#
|
|
475
|
-
|
|
476
|
-
def build_option(name, options, scope) #:nodoc:
|
|
477
|
-
scope[name] = Thor::Option.new(name, options[:desc], options[:required],
|
|
478
|
-
options[:type], options[:default], options[:banner],
|
|
479
|
-
options[:lazy_default], options[:group], options[:aliases])
|
|
480
|
-
end
|
|
531
|
+
# Raises an error if the word given is a Thor reserved word.
|
|
532
|
+
def is_thor_reserved_word?(word, type) #:nodoc:
|
|
533
|
+
return false unless THOR_RESERVED_WORDS.include?(word.to_s)
|
|
534
|
+
raise "#{word.inspect} is a Thor reserved word and cannot be defined as #{type}"
|
|
535
|
+
end
|
|
481
536
|
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
scope[key] = Thor::Option.parse(key, value)
|
|
492
|
-
end
|
|
493
|
-
end
|
|
537
|
+
# Build an option and adds it to the given scope.
|
|
538
|
+
#
|
|
539
|
+
# ==== Parameters
|
|
540
|
+
# name<Symbol>:: The name of the argument.
|
|
541
|
+
# options<Hash>:: Described in both class_option and method_option.
|
|
542
|
+
# scope<Hash>:: Options hash that is being built up
|
|
543
|
+
def build_option(name, options, scope) #:nodoc:
|
|
544
|
+
scope[name] = Thor::Option.new(name, options)
|
|
545
|
+
end
|
|
494
546
|
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
end
|
|
547
|
+
# Receives a hash of options, parse them and add to the scope. This is a
|
|
548
|
+
# fast way to set a bunch of options:
|
|
549
|
+
#
|
|
550
|
+
# build_options :foo => true, :bar => :required, :baz => :string
|
|
551
|
+
#
|
|
552
|
+
# ==== Parameters
|
|
553
|
+
# Hash[Symbol => Object]
|
|
554
|
+
def build_options(options, scope) #:nodoc:
|
|
555
|
+
options.each do |key, value|
|
|
556
|
+
scope[key] = Thor::Option.parse(key, value)
|
|
506
557
|
end
|
|
558
|
+
end
|
|
507
559
|
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
560
|
+
# Finds a command with the given name. If the command belongs to the current
|
|
561
|
+
# class, just return it, otherwise dup it and add the fresh copy to the
|
|
562
|
+
# current command hash.
|
|
563
|
+
def find_and_refresh_command(name) #:nodoc:
|
|
564
|
+
if commands[name.to_s]
|
|
565
|
+
commands[name.to_s]
|
|
566
|
+
elsif command = all_commands[name.to_s] # rubocop:disable AssignmentInCondition
|
|
567
|
+
commands[name.to_s] = command.clone
|
|
568
|
+
else
|
|
569
|
+
raise ArgumentError, "You supplied :for => #{name.inspect}, but the command #{name.inspect} could not be found."
|
|
512
570
|
end
|
|
571
|
+
end
|
|
572
|
+
alias_method :find_and_refresh_task, :find_and_refresh_command
|
|
513
573
|
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
574
|
+
# Everytime someone inherits from a Thor class, register the klass
|
|
575
|
+
# and file into baseclass.
|
|
576
|
+
def inherited(klass)
|
|
577
|
+
Thor::Base.register_klass_file(klass)
|
|
578
|
+
klass.instance_variable_set(:@no_commands, false)
|
|
579
|
+
end
|
|
518
580
|
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
581
|
+
# Fire this callback whenever a method is added. Added methods are
|
|
582
|
+
# tracked as commands by invoking the create_command method.
|
|
583
|
+
def method_added(meth)
|
|
584
|
+
meth = meth.to_s
|
|
523
585
|
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
586
|
+
if meth == "initialize"
|
|
587
|
+
initialize_added
|
|
588
|
+
return
|
|
589
|
+
end
|
|
527
590
|
|
|
528
|
-
|
|
591
|
+
# Return if it's not a public instance method
|
|
592
|
+
return unless public_method_defined?(meth.to_sym)
|
|
529
593
|
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
594
|
+
@no_commands ||= false
|
|
595
|
+
return if @no_commands || !create_command(meth)
|
|
596
|
+
|
|
597
|
+
is_thor_reserved_word?(meth, :command)
|
|
598
|
+
Thor::Base.register_klass_file(self)
|
|
599
|
+
end
|
|
533
600
|
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
601
|
+
# Retrieves a value from superclass. If it reaches the baseclass,
|
|
602
|
+
# returns default.
|
|
603
|
+
def from_superclass(method, default = nil)
|
|
604
|
+
if self == baseclass || !superclass.respond_to?(method, true)
|
|
605
|
+
default
|
|
606
|
+
else
|
|
607
|
+
value = superclass.send(method)
|
|
608
|
+
|
|
609
|
+
# Ruby implements `dup` on Object, but raises a `TypeError`
|
|
610
|
+
# if the method is called on immediates. As a result, we
|
|
611
|
+
# don't have a good way to check whether dup will succeed
|
|
612
|
+
# without calling it and rescuing the TypeError.
|
|
613
|
+
begin
|
|
614
|
+
value.dup
|
|
615
|
+
rescue TypeError
|
|
616
|
+
value
|
|
542
617
|
end
|
|
543
|
-
end
|
|
544
618
|
|
|
545
|
-
# A flag that makes the process exit with status 1 if any error happens.
|
|
546
|
-
def exit_on_failure?
|
|
547
|
-
false
|
|
548
619
|
end
|
|
620
|
+
end
|
|
549
621
|
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
File.basename($0).split(' ').first
|
|
555
|
-
end
|
|
622
|
+
# A flag that makes the process exit with status 1 if any error happens.
|
|
623
|
+
def exit_on_failure?
|
|
624
|
+
false
|
|
625
|
+
end
|
|
556
626
|
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
627
|
+
#
|
|
628
|
+
# The basename of the program invoking the thor class.
|
|
629
|
+
#
|
|
630
|
+
def basename
|
|
631
|
+
File.basename($PROGRAM_NAME).split(" ").first
|
|
632
|
+
end
|
|
561
633
|
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
634
|
+
# SIGNATURE: Sets the baseclass. This is where the superclass lookup
|
|
635
|
+
# finishes.
|
|
636
|
+
def baseclass #:nodoc:
|
|
637
|
+
end
|
|
566
638
|
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
639
|
+
# SIGNATURE: Creates a new command if valid_command? is true. This method is
|
|
640
|
+
# called when a new method is added to the class.
|
|
641
|
+
def create_command(meth) #:nodoc:
|
|
642
|
+
end
|
|
643
|
+
alias_method :create_task, :create_command
|
|
571
644
|
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
645
|
+
# SIGNATURE: Defines behavior when the initialize method is added to the
|
|
646
|
+
# class.
|
|
647
|
+
def initialize_added #:nodoc:
|
|
648
|
+
end
|
|
576
649
|
|
|
650
|
+
# SIGNATURE: The hook invoked by start.
|
|
651
|
+
def dispatch(command, given_args, given_opts, config) #:nodoc:
|
|
652
|
+
raise NotImplementedError
|
|
653
|
+
end
|
|
577
654
|
end
|
|
578
655
|
end
|
|
579
656
|
end
|