thor 0.17.0 → 0.18.0
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.
- data/{CHANGELOG.rdoc → CHANGELOG.md} +30 -36
- data/README.md +10 -3
- data/lib/thor.rb +205 -180
- data/lib/thor/actions.rb +5 -5
- data/lib/thor/actions/create_link.rb +3 -0
- data/lib/thor/actions/directory.rb +2 -0
- data/lib/thor/actions/file_manipulation.rb +1 -1
- data/lib/thor/base.rb +84 -95
- data/lib/thor/{task.rb → command.rb} +17 -13
- data/lib/thor/core_ext/io_binary_read.rb +12 -0
- data/lib/thor/error.rb +4 -7
- data/lib/thor/group.rb +30 -33
- data/lib/thor/invocation.rb +28 -26
- data/lib/thor/parser/options.rb +3 -1
- data/lib/thor/runner.rb +21 -20
- data/lib/thor/shell/basic.rb +5 -1
- data/lib/thor/shell/color.rb +4 -0
- data/lib/thor/shell/html.rb +4 -0
- data/lib/thor/util.rb +214 -210
- data/lib/thor/version.rb +1 -1
- data/spec/actions/create_file_spec.rb +1 -1
- data/spec/actions/create_link_spec.rb +15 -1
- data/spec/actions/directory_spec.rb +18 -5
- data/spec/actions/empty_directory_spec.rb +1 -1
- data/spec/actions/file_manipulation_spec.rb +13 -13
- data/spec/actions/inject_into_file_spec.rb +1 -1
- data/spec/actions_spec.rb +1 -1
- data/spec/base_spec.rb +40 -24
- data/spec/command_spec.rb +80 -0
- data/spec/core_ext/hash_with_indifferent_access_spec.rb +1 -1
- data/spec/core_ext/ordered_hash_spec.rb +1 -1
- data/spec/exit_condition_spec.rb +3 -3
- data/spec/fixtures/{task.thor → command.thor} +0 -0
- data/spec/fixtures/doc/%file_name%.rb.tt +1 -0
- data/spec/fixtures/doc/COMMENTER +11 -0
- data/spec/fixtures/doc/README +3 -0
- data/spec/fixtures/doc/block_helper.rb +3 -0
- data/spec/fixtures/doc/config.rb +1 -0
- data/spec/fixtures/doc/config.yaml.tt +1 -0
- data/spec/fixtures/doc/excluding/%file_name%.rb.tt +1 -0
- data/spec/fixtures/group.thor +24 -10
- data/spec/fixtures/invoke.thor +3 -3
- data/spec/fixtures/script.thor +40 -15
- data/spec/fixtures/subcommand.thor +17 -0
- data/spec/group_spec.rb +13 -13
- data/spec/{spec_helper.rb → helper.rb} +11 -7
- data/spec/invocation_spec.rb +16 -16
- data/spec/parser/argument_spec.rb +4 -4
- data/spec/parser/arguments_spec.rb +1 -1
- data/spec/parser/option_spec.rb +1 -1
- data/spec/parser/options_spec.rb +6 -1
- data/spec/rake_compat_spec.rb +1 -1
- data/spec/register_spec.rb +12 -12
- data/spec/runner_spec.rb +36 -36
- data/spec/shell/basic_spec.rb +9 -10
- data/spec/shell/color_spec.rb +16 -2
- data/spec/shell/html_spec.rb +1 -1
- data/spec/shell_spec.rb +1 -1
- data/spec/subcommand_spec.rb +30 -0
- data/spec/thor_spec.rb +81 -78
- data/spec/util_spec.rb +10 -10
- data/thor.gemspec +22 -18
- metadata +49 -38
- data/.gitignore +0 -44
- data/.rspec +0 -3
- data/.travis.yml +0 -8
- data/Gemfile +0 -19
- data/bin/rake2thor +0 -86
- data/lib/thor/core_ext/file_binary_read.rb +0 -9
- data/spec/task_spec.rb +0 -80
data/lib/thor/actions.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'fileutils'
|
2
2
|
require 'uri'
|
3
|
-
require 'thor/core_ext/
|
3
|
+
require 'thor/core_ext/io_binary_read'
|
4
4
|
require 'thor/actions/create_file'
|
5
5
|
require 'thor/actions/create_link'
|
6
6
|
require 'thor/actions/directory'
|
@@ -268,8 +268,8 @@ class Thor
|
|
268
268
|
# switches.
|
269
269
|
#
|
270
270
|
# ==== Parameters
|
271
|
-
#
|
272
|
-
# args<Array>:: arguments to the
|
271
|
+
# command<String>:: the command to be invoked
|
272
|
+
# args<Array>:: arguments to the command
|
273
273
|
# config<Hash>:: give :verbose => false to not log the status, :capture => true to hide to output.
|
274
274
|
# Other options are given as parameter to Thor.
|
275
275
|
#
|
@@ -282,13 +282,13 @@ class Thor
|
|
282
282
|
# thor :list, :all => true, :substring => 'rails'
|
283
283
|
# #=> thor list --all --substring=rails
|
284
284
|
#
|
285
|
-
def thor(
|
285
|
+
def thor(command, *args)
|
286
286
|
config = args.last.is_a?(Hash) ? args.pop : {}
|
287
287
|
verbose = config.key?(:verbose) ? config.delete(:verbose) : true
|
288
288
|
pretend = config.key?(:pretend) ? config.delete(:pretend) : false
|
289
289
|
capture = config.key?(:capture) ? config.delete(:capture) : false
|
290
290
|
|
291
|
-
args.unshift
|
291
|
+
args.unshift(command)
|
292
292
|
args.push Thor::Options.to_switches(config)
|
293
293
|
command = args.join(' ').strip
|
294
294
|
|
@@ -39,6 +39,7 @@ class Thor
|
|
39
39
|
# config<Hash>:: give :verbose => false to not log the status.
|
40
40
|
# If :recursive => false, does not look for paths recursively.
|
41
41
|
# If :mode => :preserve, preserve the file mode from the source.
|
42
|
+
# If :exclude_pattern => /regexp/, prevents copying files that match that regexp.
|
42
43
|
#
|
43
44
|
# ==== Examples
|
44
45
|
#
|
@@ -78,6 +79,7 @@ class Thor
|
|
78
79
|
|
79
80
|
files(lookup).sort.each do |file_source|
|
80
81
|
next if File.directory?(file_source)
|
82
|
+
next if config[:exclude_pattern] && file_source.match(config[:exclude_pattern])
|
81
83
|
file_destination = File.join(given_destination, file_source.gsub(source, '.'))
|
82
84
|
file_destination.gsub!('/./', '/')
|
83
85
|
|
data/lib/thor/base.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
|
+
require 'thor/command'
|
1
2
|
require 'thor/core_ext/hash_with_indifferent_access'
|
2
3
|
require 'thor/core_ext/ordered_hash'
|
3
4
|
require 'thor/error'
|
4
|
-
require 'thor/shell'
|
5
5
|
require 'thor/invocation'
|
6
6
|
require 'thor/parser'
|
7
|
-
require 'thor/
|
7
|
+
require 'thor/shell'
|
8
8
|
require 'thor/util'
|
9
9
|
|
10
10
|
class Thor
|
@@ -47,8 +47,8 @@ class Thor
|
|
47
47
|
# first two parameters.
|
48
48
|
|
49
49
|
if options.is_a?(Array)
|
50
|
-
|
51
|
-
parse_options = parse_options.merge(
|
50
|
+
command_options = config.delete(:command_options) # hook for start
|
51
|
+
parse_options = parse_options.merge(command_options) if command_options
|
52
52
|
array_options, hash_options = options, {}
|
53
53
|
else
|
54
54
|
# Handle the case where the class was explicitly instantiated
|
@@ -59,9 +59,10 @@ class Thor
|
|
59
59
|
# Let Thor::Options parse the options first, so it can remove
|
60
60
|
# declared options from the array. This will leave us with
|
61
61
|
# a list of arguments that weren't declared.
|
62
|
-
stop_on_unknown = self.class.stop_on_unknown_option? config[:
|
62
|
+
stop_on_unknown = self.class.stop_on_unknown_option? config[:current_command]
|
63
63
|
opts = Thor::Options.new(parse_options, hash_options, stop_on_unknown)
|
64
64
|
self.options = opts.parse(array_options)
|
65
|
+
self.options = config[:class_options].merge(self.options) if config[:class_options]
|
65
66
|
|
66
67
|
# If unknown options are disallowed, make sure that none of the
|
67
68
|
# remaining arguments looks like an option.
|
@@ -118,18 +119,6 @@ class Thor
|
|
118
119
|
end
|
119
120
|
|
120
121
|
module ClassMethods
|
121
|
-
def attr_reader(*) #:nodoc:
|
122
|
-
no_tasks { super }
|
123
|
-
end
|
124
|
-
|
125
|
-
def attr_writer(*) #:nodoc:
|
126
|
-
no_tasks { super }
|
127
|
-
end
|
128
|
-
|
129
|
-
def attr_accessor(*) #:nodoc:
|
130
|
-
no_tasks { super }
|
131
|
-
end
|
132
|
-
|
133
122
|
# If you want to raise an error for unknown options, call check_unknown_options!
|
134
123
|
# This is disabled by default to allow dynamic invocations.
|
135
124
|
def check_unknown_options!
|
@@ -146,8 +135,8 @@ class Thor
|
|
146
135
|
|
147
136
|
# If true, option parsing is suspended as soon as an unknown option or a
|
148
137
|
# regular argument is encountered. All remaining arguments are passed to
|
149
|
-
# the
|
150
|
-
def stop_on_unknown_option?(
|
138
|
+
# the command as regular arguments.
|
139
|
+
def stop_on_unknown_option?(command_name) #:nodoc:
|
151
140
|
false
|
152
141
|
end
|
153
142
|
|
@@ -172,11 +161,11 @@ class Thor
|
|
172
161
|
# is how they are parsed from the command line, arguments are retrieved
|
173
162
|
# from position:
|
174
163
|
#
|
175
|
-
# thor
|
164
|
+
# thor command NAME
|
176
165
|
#
|
177
166
|
# Instead of:
|
178
167
|
#
|
179
|
-
# thor
|
168
|
+
# thor command --name=NAME
|
180
169
|
#
|
181
170
|
# Besides, arguments are used inside your code as an accessor (self.argument),
|
182
171
|
# while options are all kept in a hash (self.options).
|
@@ -203,7 +192,7 @@ class Thor
|
|
203
192
|
#
|
204
193
|
def argument(name, options={})
|
205
194
|
is_thor_reserved_word?(name, :argument)
|
206
|
-
|
195
|
+
no_commands { attr_accessor name }
|
207
196
|
|
208
197
|
required = if options.key?(:optional)
|
209
198
|
!options[:optional]
|
@@ -307,7 +296,7 @@ class Thor
|
|
307
296
|
end
|
308
297
|
|
309
298
|
# Defines the group. This is used when thor list is invoked so you can specify
|
310
|
-
# that only
|
299
|
+
# that only commands from a pre-defined group will be shown. Defaults to standard.
|
311
300
|
#
|
312
301
|
# ==== Parameters
|
313
302
|
# name<String|Symbol>
|
@@ -321,74 +310,78 @@ class Thor
|
|
321
310
|
end
|
322
311
|
end
|
323
312
|
|
324
|
-
# Returns the
|
313
|
+
# Returns the commands for this Thor class.
|
325
314
|
#
|
326
315
|
# ==== Returns
|
327
|
-
# OrderedHash:: An ordered hash with
|
316
|
+
# OrderedHash:: An ordered hash with commands names as keys and Thor::Command
|
328
317
|
# objects as values.
|
329
318
|
#
|
330
|
-
def
|
331
|
-
@
|
319
|
+
def commands
|
320
|
+
@commands ||= Thor::CoreExt::OrderedHash.new
|
332
321
|
end
|
322
|
+
alias tasks commands
|
333
323
|
|
334
|
-
# Returns the
|
324
|
+
# Returns the commands for this Thor class and all subclasses.
|
335
325
|
#
|
336
326
|
# ==== Returns
|
337
|
-
# OrderedHash:: An ordered hash with
|
327
|
+
# OrderedHash:: An ordered hash with commands names as keys and Thor::Command
|
338
328
|
# objects as values.
|
339
329
|
#
|
340
|
-
def
|
341
|
-
@
|
342
|
-
@
|
330
|
+
def all_commands
|
331
|
+
@all_commands ||= from_superclass(:all_commands, Thor::CoreExt::OrderedHash.new)
|
332
|
+
@all_commands.merge(commands)
|
343
333
|
end
|
334
|
+
alias all_tasks all_commands
|
344
335
|
|
345
|
-
# Removes a given
|
336
|
+
# Removes a given command from this Thor class. This is usually done if you
|
346
337
|
# are inheriting from another class and don't want it to be available
|
347
338
|
# anymore.
|
348
339
|
#
|
349
|
-
# By default it only remove the mapping to the
|
340
|
+
# By default it only remove the mapping to the command. But you can supply
|
350
341
|
# :undefine => true to undefine the method from the class as well.
|
351
342
|
#
|
352
343
|
# ==== Parameters
|
353
|
-
# name<Symbol|String>:: The name of the
|
354
|
-
# options<Hash>:: You can give :undefine => true if you want
|
344
|
+
# name<Symbol|String>:: The name of the command to be removed
|
345
|
+
# options<Hash>:: You can give :undefine => true if you want commands the method
|
355
346
|
# to be undefined from the class as well.
|
356
347
|
#
|
357
|
-
def
|
348
|
+
def remove_command(*names)
|
358
349
|
options = names.last.is_a?(Hash) ? names.pop : {}
|
359
350
|
|
360
351
|
names.each do |name|
|
361
|
-
|
362
|
-
|
352
|
+
commands.delete(name.to_s)
|
353
|
+
all_commands.delete(name.to_s)
|
363
354
|
undef_method name if options[:undefine]
|
364
355
|
end
|
365
356
|
end
|
357
|
+
alias remove_task remove_command
|
366
358
|
|
367
|
-
# All methods defined inside the given block are not added as
|
359
|
+
# All methods defined inside the given block are not added as commands.
|
368
360
|
#
|
369
361
|
# So you can do:
|
370
362
|
#
|
371
363
|
# class MyScript < Thor
|
372
|
-
#
|
373
|
-
# def
|
364
|
+
# no_commands do
|
365
|
+
# def this_is_not_a_command
|
374
366
|
# end
|
375
367
|
# end
|
376
368
|
# end
|
377
369
|
#
|
378
|
-
# You can also add the method and remove it from the
|
370
|
+
# You can also add the method and remove it from the command list:
|
379
371
|
#
|
380
372
|
# class MyScript < Thor
|
381
|
-
# def
|
373
|
+
# def this_is_not_a_command
|
382
374
|
# end
|
383
|
-
#
|
375
|
+
# remove_command :this_is_not_a_command
|
384
376
|
# end
|
385
377
|
#
|
386
|
-
def
|
387
|
-
@
|
378
|
+
def no_commands
|
379
|
+
@no_commands = true
|
388
380
|
yield
|
389
381
|
ensure
|
390
|
-
@
|
382
|
+
@no_commands = false
|
391
383
|
end
|
384
|
+
alias no_tasks no_commands
|
392
385
|
|
393
386
|
# Sets the namespace for the Thor or Thor::Group class. By default the
|
394
387
|
# namespace is retrieved from the class name. If your Thor class is named
|
@@ -400,7 +393,7 @@ class Thor
|
|
400
393
|
#
|
401
394
|
# namespace :my_scripts
|
402
395
|
#
|
403
|
-
# You change how your
|
396
|
+
# You change how your commands are invoked:
|
404
397
|
#
|
405
398
|
# thor my_scripts -h
|
406
399
|
#
|
@@ -408,9 +401,9 @@ class Thor
|
|
408
401
|
#
|
409
402
|
# namespace :default
|
410
403
|
#
|
411
|
-
# Your
|
404
|
+
# Your commands can be invoked with a shortcut. Instead of:
|
412
405
|
#
|
413
|
-
# thor :
|
406
|
+
# thor :my_command
|
414
407
|
#
|
415
408
|
def namespace(name=nil)
|
416
409
|
@namespace = case name
|
@@ -421,13 +414,13 @@ class Thor
|
|
421
414
|
end
|
422
415
|
end
|
423
416
|
|
424
|
-
# Parses the
|
425
|
-
# and invoke the
|
417
|
+
# Parses the command and options from the given args, instantiate the class
|
418
|
+
# and invoke the command. This method is used when the arguments must be parsed
|
426
419
|
# from an array. If you are inside Ruby and want to use a Thor class, you
|
427
420
|
# can simply initialize it:
|
428
421
|
#
|
429
422
|
# script = MyScript.new(args, options, config)
|
430
|
-
# script.invoke(:
|
423
|
+
# script.invoke(:command, first_arg, second_arg, third_arg)
|
431
424
|
#
|
432
425
|
def start(given_args=ARGV, config={})
|
433
426
|
config[:shell] ||= Thor::Base.shell.new
|
@@ -436,52 +429,44 @@ class Thor
|
|
436
429
|
ENV["THOR_DEBUG"] == "1" ? (raise e) : config[:shell].error(e.message)
|
437
430
|
exit(1) if exit_on_failure?
|
438
431
|
rescue Errno::EPIPE
|
439
|
-
# This happens if a thor
|
432
|
+
# This happens if a thor command is piped to something like `head`,
|
440
433
|
# which closes the pipe when it's done reading. This will also
|
441
434
|
# mean that if the pipe is closed, further unnecessary
|
442
435
|
# computation will not occur.
|
443
436
|
exit(0)
|
444
437
|
end
|
445
438
|
|
446
|
-
# Allows to use private methods from parent in child classes as
|
439
|
+
# Allows to use private methods from parent in child classes as commands.
|
447
440
|
#
|
448
441
|
# ==== Parameters
|
449
|
-
# names<Array>:: Method names to be used as
|
442
|
+
# names<Array>:: Method names to be used as commands
|
450
443
|
#
|
451
444
|
# ==== Examples
|
452
445
|
#
|
453
|
-
#
|
454
|
-
#
|
446
|
+
# public_command :foo
|
447
|
+
# public_command :foo, :bar, :baz
|
455
448
|
#
|
456
|
-
def
|
449
|
+
def public_command(*names)
|
457
450
|
names.each do |name|
|
458
451
|
class_eval "def #{name}(*); super end"
|
459
452
|
end
|
460
453
|
end
|
454
|
+
alias public_task public_command
|
461
455
|
|
462
|
-
def
|
456
|
+
def handle_no_command_error(command, has_namespace = $thor_runner) #:nodoc:
|
463
457
|
if has_namespace
|
464
|
-
raise
|
458
|
+
raise UndefinedCommandError, "Could not find command #{command.inspect} in #{namespace.inspect} namespace."
|
465
459
|
else
|
466
|
-
raise
|
460
|
+
raise UndefinedCommandError, "Could not find command #{command.inspect}."
|
467
461
|
end
|
468
462
|
end
|
463
|
+
alias handle_no_task_error handle_no_command_error
|
469
464
|
|
470
|
-
def handle_argument_error(
|
471
|
-
msg = "#{basename} #{
|
472
|
-
if
|
473
|
-
|
474
|
-
|
475
|
-
msg << " should have no arguments"
|
476
|
-
else
|
477
|
-
msg << " requires at least #{required} argument"
|
478
|
-
msg << "s" if required > 1
|
479
|
-
end
|
480
|
-
else
|
481
|
-
msg = "call #{msg} as"
|
482
|
-
end
|
483
|
-
|
484
|
-
msg << ": #{self.banner(task).inspect}."
|
465
|
+
def handle_argument_error(command, error, args, arity) #:nodoc:
|
466
|
+
msg = "ERROR: #{basename} #{command.name} was called with "
|
467
|
+
msg << 'no arguments' if args.empty?
|
468
|
+
msg << 'arguments ' << args.inspect if !args.empty?
|
469
|
+
msg << "\nUsage: #{self.banner(command).inspect}."
|
485
470
|
raise InvocationError, msg
|
486
471
|
end
|
487
472
|
|
@@ -559,28 +544,29 @@ class Thor
|
|
559
544
|
end
|
560
545
|
end
|
561
546
|
|
562
|
-
# Finds a
|
547
|
+
# Finds a command with the given name. If the command belongs to the current
|
563
548
|
# class, just return it, otherwise dup it and add the fresh copy to the
|
564
|
-
# current
|
565
|
-
def
|
566
|
-
|
567
|
-
|
568
|
-
elsif
|
569
|
-
|
549
|
+
# current command hash.
|
550
|
+
def find_and_refresh_command(name) #:nodoc:
|
551
|
+
command = if command = commands[name.to_s]
|
552
|
+
command
|
553
|
+
elsif command = all_commands[name.to_s]
|
554
|
+
commands[name.to_s] = command.clone
|
570
555
|
else
|
571
|
-
raise ArgumentError, "You supplied :for => #{name.inspect}, but the
|
556
|
+
raise ArgumentError, "You supplied :for => #{name.inspect}, but the command #{name.inspect} could not be found."
|
572
557
|
end
|
573
558
|
end
|
559
|
+
alias find_and_refresh_task find_and_refresh_command
|
574
560
|
|
575
561
|
# Everytime someone inherits from a Thor class, register the klass
|
576
562
|
# and file into baseclass.
|
577
563
|
def inherited(klass)
|
578
564
|
Thor::Base.register_klass_file(klass)
|
579
|
-
klass.instance_variable_set(:@
|
565
|
+
klass.instance_variable_set(:@no_commands, false)
|
580
566
|
end
|
581
567
|
|
582
568
|
# Fire this callback whenever a method is added. Added methods are
|
583
|
-
# tracked as
|
569
|
+
# tracked as commands by invoking the create_command method.
|
584
570
|
def method_added(meth)
|
585
571
|
meth = meth.to_s
|
586
572
|
|
@@ -590,12 +576,14 @@ class Thor
|
|
590
576
|
end
|
591
577
|
|
592
578
|
# Return if it's not a public instance method
|
593
|
-
return unless
|
594
|
-
|
579
|
+
return unless public_method_defined?(meth.to_sym)
|
580
|
+
|
581
|
+
# Return if attr_* added the method
|
582
|
+
return if caller.first.to_s[/`attr_(reader|writer|accessor)'/]
|
595
583
|
|
596
|
-
return if @
|
584
|
+
return if @no_commands || !create_command(meth)
|
597
585
|
|
598
|
-
is_thor_reserved_word?(meth, :
|
586
|
+
is_thor_reserved_word?(meth, :command)
|
599
587
|
Thor::Base.register_klass_file(self)
|
600
588
|
end
|
601
589
|
|
@@ -634,10 +622,11 @@ class Thor
|
|
634
622
|
def baseclass #:nodoc:
|
635
623
|
end
|
636
624
|
|
637
|
-
# SIGNATURE: Creates a new
|
625
|
+
# SIGNATURE: Creates a new command if valid_command? is true. This method is
|
638
626
|
# called when a new method is added to the class.
|
639
|
-
def
|
627
|
+
def create_command(meth) #:nodoc:
|
640
628
|
end
|
629
|
+
alias create_task create_command
|
641
630
|
|
642
631
|
# SIGNATURE: Defines behavior when the initialize method is added to the
|
643
632
|
# class.
|
@@ -645,7 +634,7 @@ class Thor
|
|
645
634
|
end
|
646
635
|
|
647
636
|
# SIGNATURE: The hook invoked by start.
|
648
|
-
def dispatch(
|
637
|
+
def dispatch(command, given_args, given_opts, config) #:nodoc:
|
649
638
|
raise NotImplementedError
|
650
639
|
end
|
651
640
|
|