thor 0.17.0 → 0.18.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
|