josevalim-thor 0.10.10 → 0.10.11
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/lib/thor/actions.rb +3 -3
- data/lib/thor/base.rb +91 -224
- data/lib/thor/group.rb +24 -20
- data/lib/thor/invocation.rb +148 -0
- data/lib/thor/options.rb +1 -1
- data/lib/thor/shell/basic.rb +20 -22
- data/lib/thor/shell.rb +71 -0
- data/lib/thor.rb +12 -17
- metadata +4 -2
data/lib/thor/actions.rb
CHANGED
@@ -306,10 +306,10 @@ class Thor
|
|
306
306
|
|
307
307
|
protected
|
308
308
|
|
309
|
-
#
|
309
|
+
# Allow current root to be sent as configuration value to the invoked class.
|
310
310
|
#
|
311
|
-
def
|
312
|
-
super.merge!(:
|
311
|
+
def _overrides_config #:nodoc:
|
312
|
+
super.merge!(:root => self.root)
|
313
313
|
end
|
314
314
|
|
315
315
|
end
|
data/lib/thor/base.rb
CHANGED
@@ -1,64 +1,81 @@
|
|
1
1
|
require 'thor/core_ext/hash_with_indifferent_access'
|
2
2
|
require 'thor/core_ext/ordered_hash'
|
3
|
-
require 'thor/shell/basic'
|
4
3
|
require 'thor/error'
|
4
|
+
require 'thor/shell'
|
5
|
+
require 'thor/invocation'
|
5
6
|
require 'thor/options'
|
6
7
|
require 'thor/task'
|
7
8
|
require 'thor/util'
|
8
9
|
|
9
10
|
class Thor
|
10
11
|
HELP_MAPPINGS = %w(-h -? --help -D)
|
11
|
-
|
12
|
+
THOR_RESERVED_WORDS = %w(all invoke shell options behavior root destination_root relative_root source_root)
|
12
13
|
|
13
14
|
class Maxima < Struct.new(:usage, :options, :class_options)
|
14
15
|
end
|
15
16
|
|
16
17
|
module Base
|
18
|
+
attr_accessor :options
|
17
19
|
|
18
|
-
|
19
|
-
|
20
|
-
base.send :include, SingletonMethods
|
21
|
-
end
|
22
|
-
|
23
|
-
# Returns the classes that inherits from Thor or Thor::Group.
|
20
|
+
# It receives arguments in an Array and two hashes, one for options and
|
21
|
+
# other for configuration.
|
24
22
|
#
|
25
|
-
#
|
26
|
-
#
|
23
|
+
# Notice that it does not check arguments type neither if all required
|
24
|
+
# arguments were supplied. It should be done by the parser.
|
27
25
|
#
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
# Returns the files where the subclasses are kept.
|
26
|
+
# ==== Parameters
|
27
|
+
# args<Array[Object]>:: An array of objects. The objects are applied to their
|
28
|
+
# respective accessors declared with <tt>argument</tt>.
|
33
29
|
#
|
34
|
-
#
|
35
|
-
#
|
30
|
+
# options<Hash>:: An options hash that will be available as self.options.
|
31
|
+
# The hash given is converted to a hash with indifferent
|
32
|
+
# access, magic predicates (options.skip?) and then frozen.
|
36
33
|
#
|
37
|
-
|
38
|
-
@subclass_files ||= Hash.new{ |h,k| h[k] = [] }
|
39
|
-
end
|
40
|
-
|
41
|
-
# Returns the shell used in all Thor classes.
|
34
|
+
# config<Hash>:: Configuration for this Thor class.
|
42
35
|
#
|
43
|
-
def
|
44
|
-
|
45
|
-
|
36
|
+
def initialize(args=[], options={}, config={})
|
37
|
+
self.class.arguments.zip(args).each do |argument, value|
|
38
|
+
send("#{argument.human_name}=", value)
|
39
|
+
end
|
46
40
|
|
47
|
-
|
48
|
-
#
|
49
|
-
def self.shell=(klass)
|
50
|
-
@shell = klass
|
41
|
+
self.options = Thor::CoreExt::HashWithIndifferentAccess.new(options).freeze
|
51
42
|
end
|
52
43
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
44
|
+
class << self
|
45
|
+
def included(base) #:nodoc:
|
46
|
+
base.send :extend, ClassMethods
|
47
|
+
base.send :include, Invocation
|
48
|
+
base.send :include, Shell
|
49
|
+
end
|
50
|
+
|
51
|
+
# Returns the classes that inherits from Thor or Thor::Group.
|
52
|
+
#
|
53
|
+
# ==== Returns
|
54
|
+
# Array[Class]
|
55
|
+
#
|
56
|
+
def subclasses
|
57
|
+
@subclasses ||= []
|
58
|
+
end
|
59
|
+
|
60
|
+
# Returns the files where the subclasses are kept.
|
61
|
+
#
|
62
|
+
# ==== Returns
|
63
|
+
# Hash[path<String> => Class]
|
64
|
+
#
|
65
|
+
def subclass_files
|
66
|
+
@subclass_files ||= Hash.new{ |h,k| h[k] = [] }
|
67
|
+
end
|
59
68
|
|
60
|
-
|
61
|
-
|
69
|
+
# Whenever a class inherits from Thor or Thor::Group, we should track the
|
70
|
+
# class and the file on Thor::Base. This is the method responsable for it.
|
71
|
+
#
|
72
|
+
def register_klass_file(klass) #:nodoc:
|
73
|
+
file = caller[1].match(/(.*):\d+/)[1]
|
74
|
+
Thor::Base.subclasses << klass unless Thor::Base.subclasses.include?(klass)
|
75
|
+
|
76
|
+
file_subclasses = Thor::Base.subclass_files[File.expand_path(file)]
|
77
|
+
file_subclasses << klass unless file_subclasses.include?(klass)
|
78
|
+
end
|
62
79
|
end
|
63
80
|
|
64
81
|
module ClassMethods
|
@@ -97,6 +114,7 @@ class Thor
|
|
97
114
|
# ArgumentError:: Raised if you supply a required argument after a non required one.
|
98
115
|
#
|
99
116
|
def argument(name, options={})
|
117
|
+
is_thor_reserved_word?(name, :argument)
|
100
118
|
no_tasks { attr_accessor name }
|
101
119
|
|
102
120
|
required = if options.key?(:optional)
|
@@ -308,6 +326,26 @@ class Thor
|
|
308
326
|
end
|
309
327
|
end
|
310
328
|
|
329
|
+
# Parses the task and options from the given args, instantiate the class
|
330
|
+
# and invoke the task. This method is used when the arguments must be parsed
|
331
|
+
# from an array. If you are inside Ruby and want to use a Thor class, you
|
332
|
+
# can simply initialize it:
|
333
|
+
#
|
334
|
+
# script = MyScript.new(args, options, config)
|
335
|
+
# script.invoke(:task, first_arg, second_arg, third_arg)
|
336
|
+
#
|
337
|
+
def start(args=ARGV, config={})
|
338
|
+
config[:shell] ||= Thor::Base.shell.new
|
339
|
+
|
340
|
+
task = normalize_arguments(args, config)
|
341
|
+
return unless task
|
342
|
+
|
343
|
+
instance, trailing = prepare(task, args, config)
|
344
|
+
instance.invoke(task, trailing)
|
345
|
+
rescue Thor::Error => e
|
346
|
+
config[:shell].error e.message
|
347
|
+
end
|
348
|
+
|
311
349
|
protected
|
312
350
|
|
313
351
|
# Prints the class optins per group. If a class options does not belong
|
@@ -350,6 +388,13 @@ class Thor
|
|
350
388
|
end
|
351
389
|
end
|
352
390
|
|
391
|
+
# Raises an error if the word given is a Thor reserved word.
|
392
|
+
#
|
393
|
+
def is_thor_reserved_word?(word, type)
|
394
|
+
return false unless THOR_RESERVED_WORDS.include?(word.to_s)
|
395
|
+
raise ScriptError, "'#{word}' is a Thor reserved word and cannot be defined as #{type}"
|
396
|
+
end
|
397
|
+
|
353
398
|
# Build an option and adds it to the given scope.
|
354
399
|
#
|
355
400
|
# ==== Parameters
|
@@ -408,11 +453,7 @@ class Thor
|
|
408
453
|
end
|
409
454
|
|
410
455
|
return if @no_tasks || !valid_task?(meth)
|
411
|
-
|
412
|
-
if RESERVED_TASK_NAMES.include?(meth)
|
413
|
-
raise ScriptError, "'#{meth}' is a Thor reserved word and cannot be defined as task"
|
414
|
-
end
|
415
|
-
|
456
|
+
is_thor_reserved_word?(meth, :task)
|
416
457
|
Thor::Base.register_klass_file(self)
|
417
458
|
create_task(meth)
|
418
459
|
end
|
@@ -445,192 +486,18 @@ class Thor
|
|
445
486
|
# class.
|
446
487
|
def initialize_added #:nodoc:
|
447
488
|
end
|
448
|
-
end
|
449
|
-
|
450
|
-
module SingletonMethods
|
451
|
-
attr_accessor :options
|
452
|
-
|
453
|
-
SHELL_DELEGATED_METHODS = [:ask, :yes?, :no?, :say, :say_status, :print_list, :print_table]
|
454
489
|
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
# arguments were supplied. It should be done by the parser.
|
460
|
-
#
|
461
|
-
# ==== Parameters
|
462
|
-
# args<Array[Object]>:: An array of objects. The objects are applied to their
|
463
|
-
# respective accessors declared with <tt>argument</tt>.
|
464
|
-
#
|
465
|
-
# options<Hash>:: An options hash that will be available as self.options.
|
466
|
-
# The hash given is converted to a hash with indifferent
|
467
|
-
# access, magic predicates (options.skip?) and then frozen.
|
468
|
-
#
|
469
|
-
# config<Hash>:: Configuration for this Thor class.
|
470
|
-
#
|
471
|
-
# ==== Configuration
|
472
|
-
# shell<Object>:: An instance of the shell to be used.
|
473
|
-
#
|
474
|
-
# ==== Examples
|
475
|
-
#
|
476
|
-
# class MyScript < Thor
|
477
|
-
# argument :first, :type => :numeric
|
478
|
-
# end
|
479
|
-
#
|
480
|
-
# MyScript.new [1.0], { :foo => :bar }, :shell => Thor::Shell::Basic.new
|
481
|
-
#
|
482
|
-
def initialize(args=[], options={}, config={})
|
483
|
-
self.class.arguments.zip(args).each do |argument, value|
|
484
|
-
send("#{argument.human_name}=", value)
|
490
|
+
# SIGNATURE: Normalize arguments when invoked through start. Should
|
491
|
+
# return the name of the task to be invoked. Returning nil makes the
|
492
|
+
# start process to exist without error message.
|
493
|
+
def normalize_arguments(args, config) #:nodoc:
|
485
494
|
end
|
486
495
|
|
487
|
-
|
488
|
-
|
489
|
-
#
|
490
|
-
|
491
|
-
self.shell.base ||= self if self.shell.respond_to?(:base)
|
492
|
-
end
|
493
|
-
|
494
|
-
# Holds the shell for the given Thor instance. If no shell is given,
|
495
|
-
# it gets a default shell from Thor::Base.shell.
|
496
|
-
#
|
497
|
-
def shell
|
498
|
-
@shell ||= Thor::Base.shell.new
|
499
|
-
end
|
500
|
-
|
501
|
-
# Sets the shell for this thor class.
|
502
|
-
#
|
503
|
-
def shell=(shell)
|
504
|
-
@shell = shell
|
505
|
-
end
|
506
|
-
|
507
|
-
# Common methods that are delegated to the shell.
|
508
|
-
#
|
509
|
-
SHELL_DELEGATED_METHODS.each do |method|
|
510
|
-
module_eval <<-METHOD, __FILE__, __LINE__
|
511
|
-
def #{method}(*args)
|
512
|
-
shell.#{method}(*args)
|
513
|
-
end
|
514
|
-
METHOD
|
515
|
-
end
|
516
|
-
|
517
|
-
# Receives a name and invokes it. The name can be either a namespaced name,
|
518
|
-
# a current class task or even a class. Arguments are given in an array and
|
519
|
-
# options given are merged with the invoker options.
|
520
|
-
#
|
521
|
-
# ==== Examples
|
522
|
-
#
|
523
|
-
# class A < Thor
|
524
|
-
# def foo
|
525
|
-
# invoke :bar
|
526
|
-
# invoke "b:lib", ["merb", "rails"]
|
527
|
-
# end
|
528
|
-
#
|
529
|
-
# def bar
|
530
|
-
# invoke "b:lib", ["merb", "rails"]
|
531
|
-
# # magic
|
532
|
-
# end
|
533
|
-
# end
|
534
|
-
#
|
535
|
-
# class B < Thor
|
536
|
-
# argument :preferred_framework, :type => :string
|
537
|
-
#
|
538
|
-
# def lib(second_framework)
|
539
|
-
# # magic
|
540
|
-
# end
|
541
|
-
# end
|
542
|
-
#
|
543
|
-
# You can notice that the method "foo" above invokes two tasks: "bar",
|
544
|
-
# which belongs to the same class and "lib" that belongs to the class B.
|
545
|
-
#
|
546
|
-
# By using an invocation system you ensure that a task is invoked only once.
|
547
|
-
# In the example above, invoking foo will invoke "b:lib" just once, even if
|
548
|
-
# it's invoked later by "bar" method.
|
549
|
-
#
|
550
|
-
# When invoking another class, there are a few things to keep in mind:
|
551
|
-
#
|
552
|
-
# 1) Class arguments are parsed first. In the example above, preferred
|
553
|
-
# framework is going to consume "merb" and second framework is going
|
554
|
-
# to be set to "rails".
|
555
|
-
#
|
556
|
-
# 2) All options and configurations are sent to the invoked class.
|
557
|
-
# So the invoked class is going to use the same shell instance, will
|
558
|
-
# have the same behavior (:invoke or :revoke) and so on.
|
559
|
-
#
|
560
|
-
# Invoking a Thor::Group happens in the same away as above:
|
561
|
-
#
|
562
|
-
# class C < Thor::Group
|
563
|
-
# def one
|
564
|
-
# end
|
565
|
-
# end
|
566
|
-
#
|
567
|
-
# Is invoked as:
|
568
|
-
#
|
569
|
-
# invoke "c"
|
570
|
-
#
|
571
|
-
# Or even as:
|
572
|
-
#
|
573
|
-
# invoke C
|
574
|
-
#
|
575
|
-
def invoke(name, method_args=[], options={})
|
576
|
-
@_invocations ||= Hash.new { |h,k| h[k] = [] }
|
577
|
-
instance, task = _setup_for_invoke(name, method_args, options)
|
578
|
-
|
579
|
-
current = @_invocations[instance.class]
|
580
|
-
return if current.include?("all")
|
581
|
-
|
582
|
-
if task
|
583
|
-
task = self.class.all_tasks[task.to_s] || Task.dynamic(task) unless task.is_a?(Thor::Task)
|
584
|
-
return if current.include?(task.name)
|
585
|
-
|
586
|
-
current << task.name
|
587
|
-
task.run(instance, method_args)
|
588
|
-
else
|
589
|
-
current << "all"
|
590
|
-
instance.class.all_tasks.collect { |_, task| task.run(instance) }
|
496
|
+
# SIGNATURE: Receives a task, arguments to be parsed and configuration
|
497
|
+
# values and initializes the current class. Trailing arguments are
|
498
|
+
# returned to be sent to the invoked task.
|
499
|
+
def prepare(task, args, config) #:nodoc:
|
591
500
|
end
|
592
|
-
end
|
593
|
-
|
594
|
-
protected
|
595
|
-
|
596
|
-
# This is the method responsable for retrieving and setting up an
|
597
|
-
# instance to be used in invoke.
|
598
|
-
#
|
599
|
-
def _setup_for_invoke(name, method_args, options) #:nodoc:
|
600
|
-
case name
|
601
|
-
when NilClass, Thor::Task
|
602
|
-
# Do nothing, we already have what we want
|
603
|
-
when Class
|
604
|
-
klass = name
|
605
|
-
else
|
606
|
-
name = name.to_s
|
607
|
-
unless self.class.all_tasks[name]
|
608
|
-
klass, task = Thor::Util.namespace_to_thor_class(name) rescue Thor::Error
|
609
|
-
end
|
610
|
-
end
|
611
|
-
|
612
|
-
if klass.nil?
|
613
|
-
return self, name
|
614
|
-
elsif klass <= Thor::Base
|
615
|
-
size = klass.arguments.size
|
616
|
-
class_args = method_args.slice!(0, size)
|
617
|
-
instance = klass.new(class_args, self.options.merge(options), _dump_config)
|
618
|
-
|
619
|
-
task ||= klass.default_task if klass <= Thor
|
620
|
-
instance.instance_variable_set("@_invocations", @_invocations)
|
621
|
-
|
622
|
-
return instance, task
|
623
|
-
else
|
624
|
-
raise ScriptError, "Expected Thor class, got #{klass}"
|
625
|
-
end
|
626
|
-
end
|
627
|
-
|
628
|
-
# Dump the configuration values for this current class.
|
629
|
-
#
|
630
|
-
def _dump_config #:nodoc:
|
631
|
-
{ :shell => self.shell }
|
632
|
-
end
|
633
|
-
|
634
501
|
end
|
635
502
|
end
|
636
503
|
end
|
data/lib/thor/group.rb
CHANGED
@@ -3,8 +3,8 @@ class Thor::Group
|
|
3
3
|
class << self
|
4
4
|
|
5
5
|
# The descrition for this Thor::Group. If none is provided, but a source root
|
6
|
-
# exists
|
7
|
-
#
|
6
|
+
# exists, tries to find the USAGE one folder above it, otherwise searches
|
7
|
+
# in the superclass.
|
8
8
|
#
|
9
9
|
# ==== Parameters
|
10
10
|
# description<String>:: The description for this Thor::Group.
|
@@ -12,8 +12,9 @@ class Thor::Group
|
|
12
12
|
def desc(description=nil)
|
13
13
|
case description
|
14
14
|
when nil
|
15
|
-
|
16
|
-
|
15
|
+
usage = File.join(source_root, "..", "USAGE") if respond_to?(:source_root)
|
16
|
+
@desc ||= if usage && File.exist?(usage)
|
17
|
+
File.read(usage)
|
17
18
|
else
|
18
19
|
from_superclass(:desc, nil)
|
19
20
|
end
|
@@ -22,22 +23,17 @@ class Thor::Group
|
|
22
23
|
end
|
23
24
|
end
|
24
25
|
|
25
|
-
#
|
26
|
-
# class and does not have to parse task options.
|
26
|
+
# Implements the prepare interface being used by start.
|
27
27
|
#
|
28
|
-
def
|
29
|
-
|
28
|
+
def prepare(task, args, config) #:nodoc:
|
29
|
+
opts = Thor::Options.new(class_options)
|
30
|
+
opts.parse(args)
|
30
31
|
|
31
|
-
|
32
|
-
|
33
|
-
else
|
34
|
-
opts = Thor::Options.new(class_options)
|
35
|
-
opts.parse(args)
|
36
|
-
|
37
|
-
new(opts.arguments, opts.options, config).invoke(:all)
|
32
|
+
instance = new(opts.arguments, opts.options, config) do |klass, invoke|
|
33
|
+
klass.prepare(invoke, args, config)
|
38
34
|
end
|
39
|
-
|
40
|
-
|
35
|
+
|
36
|
+
return instance, nil
|
41
37
|
end
|
42
38
|
|
43
39
|
# Prints help information.
|
@@ -77,6 +73,15 @@ class Thor::Group
|
|
77
73
|
def create_task(meth) #:nodoc:
|
78
74
|
tasks[meth.to_s] = Thor::Task.new(meth, nil, nil, nil)
|
79
75
|
end
|
76
|
+
|
77
|
+
def normalize_arguments(args, config) #:nodoc:
|
78
|
+
if Thor::HELP_MAPPINGS.include?(args.first)
|
79
|
+
help(config[:shell])
|
80
|
+
nil
|
81
|
+
else
|
82
|
+
:all
|
83
|
+
end
|
84
|
+
end
|
80
85
|
end
|
81
86
|
|
82
87
|
include Thor::Base
|
@@ -86,9 +91,8 @@ class Thor::Group
|
|
86
91
|
# Overwrite _setup_for_invoke to force invocation of all tasks when :all is
|
87
92
|
# supplied.
|
88
93
|
#
|
89
|
-
def _setup_for_invoke(
|
90
|
-
|
91
|
-
super(name, method_args, options)
|
94
|
+
def _setup_for_invoke(object, task=nil)
|
95
|
+
super(object.to_s == "all" ? nil : object)
|
92
96
|
end
|
93
97
|
|
94
98
|
end
|
@@ -0,0 +1,148 @@
|
|
1
|
+
class Thor
|
2
|
+
module Invocation
|
3
|
+
|
4
|
+
# Make initializer aware of invocations and the initializer proc.
|
5
|
+
#
|
6
|
+
def initialize(args=[], options={}, config={}, &block) #:nodoc:
|
7
|
+
@_invocations = config[:invocations] || Hash.new { |h,k| h[k] = [] }
|
8
|
+
@_initializer = block || lambda do |klass, invoke, overrides|
|
9
|
+
klass.new(args, options, config.merge(overrides))
|
10
|
+
end
|
11
|
+
super
|
12
|
+
end
|
13
|
+
|
14
|
+
# Receives a name and invokes it. The name can be a string (either "task" or
|
15
|
+
# "namespace:task"), a Thor::Task, a Class or a Thor instance. If the task
|
16
|
+
# cannot be guessed name, it can also be supplied as second argument. The
|
17
|
+
# arguments used to invoke the task are always supplied as the last argument.
|
18
|
+
#
|
19
|
+
# ==== Examples
|
20
|
+
#
|
21
|
+
# class A < Thor
|
22
|
+
# def foo
|
23
|
+
# invoke :bar
|
24
|
+
# invoke "b:hello", ["José"]
|
25
|
+
# end
|
26
|
+
#
|
27
|
+
# def bar
|
28
|
+
# invoke "b:hello", ["José"]
|
29
|
+
# end
|
30
|
+
# end
|
31
|
+
#
|
32
|
+
# class B < Thor
|
33
|
+
# def hello(name)
|
34
|
+
# puts "hello #{name}"
|
35
|
+
# end
|
36
|
+
# end
|
37
|
+
#
|
38
|
+
# You can notice that the method "foo" above invokes two tasks: "bar",
|
39
|
+
# which belongs to the same class and "hello" which belongs to the class B.
|
40
|
+
#
|
41
|
+
# By using an invocation system you ensure that a task is invoked only once.
|
42
|
+
# In the example above, invoking "foo" will invoke "b:hello" just once, even
|
43
|
+
# if it's invoked later by "bar" method.
|
44
|
+
#
|
45
|
+
# When class A invokes class B, all arguments used on A initialization are
|
46
|
+
# supplied to B. This allows lazy parse of options. Let's suppose you have
|
47
|
+
# some rspec tasks:
|
48
|
+
#
|
49
|
+
# class Rspec < Thor::Group
|
50
|
+
# class_option :mock_framework, :type => :string, :default => :rr
|
51
|
+
#
|
52
|
+
# def invoke_mock_framework
|
53
|
+
# invoke "rspec:#{options[:mock_framework]}"
|
54
|
+
# end
|
55
|
+
# end
|
56
|
+
#
|
57
|
+
# As you notice, it invokes the given mock framework, which might have its
|
58
|
+
# own options:
|
59
|
+
#
|
60
|
+
# class Rspec::RR < Thor::Group
|
61
|
+
# class_option :style, :type => :string, :default => :mock
|
62
|
+
# end
|
63
|
+
#
|
64
|
+
# Since it's not rspec concern to parse mock framework options, when RR
|
65
|
+
# is invoked all options are parsed again, so RR can extract only the options
|
66
|
+
# that it's going to use.
|
67
|
+
#
|
68
|
+
# If you want Rspec::RR to be initialized with its own set of options, you
|
69
|
+
# have to do that explicitely:
|
70
|
+
#
|
71
|
+
# invoke Rspec::RR.new([], :style => :foo)
|
72
|
+
#
|
73
|
+
# Besides giving an instance, you can also give a class to invoke:
|
74
|
+
#
|
75
|
+
# invoke Rspec::RR
|
76
|
+
#
|
77
|
+
# Or even a class, the task to invoke from it and its arguments:
|
78
|
+
#
|
79
|
+
# invoke Rspec::RR, :foo, [ args ]
|
80
|
+
#
|
81
|
+
def invoke(name, task=nil, method_args=nil)
|
82
|
+
task, method_args = nil, task if task.is_a?(Array)
|
83
|
+
|
84
|
+
object, task = _setup_for_invoke(name, task)
|
85
|
+
klass = object.is_a?(Class) ? object : object.class
|
86
|
+
|
87
|
+
current = @_invocations[klass]
|
88
|
+
return if current.include?("all")
|
89
|
+
|
90
|
+
if object.is_a?(Class)
|
91
|
+
instance, trailing = @_initializer.call(klass, task, _overrides_config)
|
92
|
+
method_args ||= trailing
|
93
|
+
else
|
94
|
+
instance = object
|
95
|
+
end
|
96
|
+
|
97
|
+
method_args ||= []
|
98
|
+
|
99
|
+
if task
|
100
|
+
return if current.include?(task.name)
|
101
|
+
current << task.name
|
102
|
+
task.run(instance, method_args)
|
103
|
+
else
|
104
|
+
current << "all"
|
105
|
+
klass.all_tasks.collect { |_, task| task.run(instance) }
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
protected
|
110
|
+
|
111
|
+
# Values that are sent to overwrite defined configuration values.
|
112
|
+
#
|
113
|
+
def _overrides_config #:nodoc:
|
114
|
+
{ :invocations => @_invocations }
|
115
|
+
end
|
116
|
+
|
117
|
+
# This is the method responsable for retrieving and setting up an
|
118
|
+
# instance to be used in invoke.
|
119
|
+
#
|
120
|
+
def _setup_for_invoke(name, sent_task=nil) #:nodoc:
|
121
|
+
case name
|
122
|
+
when Thor::Task
|
123
|
+
task = name
|
124
|
+
when Symbol, String
|
125
|
+
name = name.to_s
|
126
|
+
|
127
|
+
begin
|
128
|
+
task = self.class.all_tasks[name]
|
129
|
+
object, task = Thor::Util.namespace_to_thor_class(name) unless task
|
130
|
+
task = task || sent_task
|
131
|
+
rescue Thor::Error
|
132
|
+
task = name
|
133
|
+
end
|
134
|
+
else
|
135
|
+
object, task = name, sent_task
|
136
|
+
end
|
137
|
+
|
138
|
+
object ||= self
|
139
|
+
klass = object.is_a?(Class) ? object : object.class
|
140
|
+
raise "Expected Thor class, got #{klass}" unless klass <= Thor::Base
|
141
|
+
|
142
|
+
task ||= klass.default_task if klass <= Thor
|
143
|
+
task = klass.all_tasks[task.to_s] || Task.dynamic(task) if task && !task.is_a?(Thor::Task)
|
144
|
+
return object, task
|
145
|
+
end
|
146
|
+
|
147
|
+
end
|
148
|
+
end
|
data/lib/thor/options.rb
CHANGED
data/lib/thor/shell/basic.rb
CHANGED
@@ -125,30 +125,28 @@ class Thor
|
|
125
125
|
#
|
126
126
|
def file_collision(destination)
|
127
127
|
return true if @always_force
|
128
|
-
|
129
128
|
options = block_given? ? "[Ynaqdh]" : "[Ynaqh]"
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
129
|
+
|
130
|
+
while true
|
131
|
+
answer = ask %[Overwrite #{destination}? (enter "h" for help) #{options}]
|
132
|
+
|
133
|
+
case answer
|
134
|
+
when is?(:yes), is?(:force)
|
135
|
+
return true
|
136
|
+
when is?(:no), is?(:skip)
|
137
|
+
return false
|
138
|
+
when is?(:always)
|
139
|
+
return @always_force = true
|
140
|
+
when is?(:quit)
|
141
|
+
say 'Aborting...'
|
142
|
+
raise SystemExit
|
143
|
+
when is?(:diff)
|
144
|
+
show_diff(destination, yield) if block_given?
|
145
|
+
say 'Retrying...'
|
146
|
+
else
|
147
|
+
say file_collision_help
|
148
|
+
end
|
149
149
|
end
|
150
|
-
rescue ScriptError
|
151
|
-
retry
|
152
150
|
end
|
153
151
|
|
154
152
|
# Called if something goes wrong during the execution. This is used by Thor
|
data/lib/thor/shell.rb
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'thor/shell/basic'
|
2
|
+
|
3
|
+
class Thor
|
4
|
+
module Base
|
5
|
+
# Returns the shell used in all Thor classes.
|
6
|
+
#
|
7
|
+
def self.shell
|
8
|
+
@shell || Thor::Shell::Basic
|
9
|
+
end
|
10
|
+
|
11
|
+
# Sets the shell used in all Thor classes.
|
12
|
+
#
|
13
|
+
def self.shell=(klass)
|
14
|
+
@shell = klass
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
module Shell
|
19
|
+
SHELL_DELEGATED_METHODS = [:ask, :yes?, :no?, :say, :say_status, :print_list, :print_table]
|
20
|
+
|
21
|
+
# Add shell to initialize config values.
|
22
|
+
#
|
23
|
+
# ==== Configuration
|
24
|
+
# shell<Object>:: An instance of the shell to be used.
|
25
|
+
#
|
26
|
+
# ==== Examples
|
27
|
+
#
|
28
|
+
# class MyScript < Thor
|
29
|
+
# argument :first, :type => :numeric
|
30
|
+
# end
|
31
|
+
#
|
32
|
+
# MyScript.new [1.0], { :foo => :bar }, :shell => Thor::Shell::Basic.new
|
33
|
+
#
|
34
|
+
def initialize(args=[], options={}, config={})
|
35
|
+
super
|
36
|
+
self.shell = config[:shell]
|
37
|
+
self.shell.base ||= self if self.shell.respond_to?(:base)
|
38
|
+
end
|
39
|
+
|
40
|
+
# Holds the shell for the given Thor instance. If no shell is given,
|
41
|
+
# it gets a default shell from Thor::Base.shell.
|
42
|
+
#
|
43
|
+
def shell
|
44
|
+
@shell ||= Thor::Base.shell.new
|
45
|
+
end
|
46
|
+
|
47
|
+
# Sets the shell for this thor class.
|
48
|
+
#
|
49
|
+
def shell=(shell)
|
50
|
+
@shell = shell
|
51
|
+
end
|
52
|
+
|
53
|
+
# Common methods that are delegated to the shell.
|
54
|
+
#
|
55
|
+
SHELL_DELEGATED_METHODS.each do |method|
|
56
|
+
module_eval <<-METHOD, __FILE__, __LINE__
|
57
|
+
def #{method}(*args)
|
58
|
+
shell.#{method}(*args)
|
59
|
+
end
|
60
|
+
METHOD
|
61
|
+
end
|
62
|
+
|
63
|
+
protected
|
64
|
+
|
65
|
+
# Send the current shell to be used by the invoked class.
|
66
|
+
#
|
67
|
+
def _overrides_config #:nodoc:
|
68
|
+
super.merge!(:shell => self.shell)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
data/lib/thor.rb
CHANGED
@@ -115,28 +115,18 @@ class Thor
|
|
115
115
|
build_option(name, options, scope)
|
116
116
|
end
|
117
117
|
|
118
|
-
#
|
119
|
-
# and invoke the task. This method is used when the arguments must be parsed
|
120
|
-
# from an array. If you are inside Ruby and want to use a Thor class, you
|
121
|
-
# can simply initialize it:
|
118
|
+
# Implements the prepare interface being used by start.
|
122
119
|
#
|
123
|
-
|
124
|
-
# script.invoke(:task, first_arg, second_arg, third_arg)
|
125
|
-
#
|
126
|
-
def start(args=ARGV, config={})
|
127
|
-
config[:shell] ||= Thor::Base.shell.new
|
128
|
-
|
129
|
-
meth = normalize_task_name(args.shift)
|
130
|
-
task = all_tasks[meth] || Task.dynamic(meth)
|
131
|
-
|
120
|
+
def prepare(task, args, config) #:nodoc:
|
132
121
|
options = class_options.merge(task.options)
|
133
122
|
opts = Thor::Options.new(options)
|
134
123
|
opts.parse(args)
|
135
124
|
|
136
|
-
instance = new(opts.arguments, opts.options, config)
|
137
|
-
|
138
|
-
|
139
|
-
|
125
|
+
instance = new(opts.arguments, opts.options, config) do |klass, invoke, overrides|
|
126
|
+
klass.prepare(invoke, args, config.merge(overrides))
|
127
|
+
end
|
128
|
+
|
129
|
+
return instance, opts.trailing
|
140
130
|
end
|
141
131
|
|
142
132
|
# Prints help information. If a task name is given, it shows information
|
@@ -207,6 +197,11 @@ class Thor
|
|
207
197
|
@method_options = nil
|
208
198
|
end
|
209
199
|
|
200
|
+
def normalize_arguments(args, config) #:nodoc:
|
201
|
+
meth = normalize_task_name(args.shift)
|
202
|
+
all_tasks[meth] || Task.dynamic(meth)
|
203
|
+
end
|
204
|
+
|
210
205
|
# Receives a task name (can be nil), and try to get a map from it.
|
211
206
|
# If a map can't be found use the sent name or the default task.
|
212
207
|
#
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: josevalim-thor
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.10.
|
4
|
+
version: 0.10.11
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Yehuda Katz
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-06-
|
12
|
+
date: 2009-06-23 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -52,6 +52,7 @@ files:
|
|
52
52
|
- lib/thor/options.rb
|
53
53
|
- lib/thor/shell
|
54
54
|
- lib/thor/shell/basic.rb
|
55
|
+
- lib/thor/invocation.rb
|
55
56
|
- lib/thor/tasks.rb
|
56
57
|
- lib/thor/core_ext
|
57
58
|
- lib/thor/core_ext/hash_with_indifferent_access.rb
|
@@ -60,6 +61,7 @@ files:
|
|
60
61
|
- lib/thor/tasks/install.rb
|
61
62
|
- lib/thor/tasks/spec.rb
|
62
63
|
- lib/thor/tasks/package.rb
|
64
|
+
- lib/thor/shell.rb
|
63
65
|
- lib/thor/task.rb
|
64
66
|
has_rdoc: true
|
65
67
|
homepage: http://yehudakatz.com
|