Module: Thor::Invocation

Defined in:
lib/thor/invocation.rb

Defined Under Namespace

Modules: ClassMethods

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.included(base) ⇒ Object

:nodoc:



3
4
5
# File 'lib/thor/invocation.rb', line 3

def self.included(base) #:nodoc:
  base.extend ClassMethods
end

Instance Method Details

#initialize(args = [], options = {}, config = {}, &block) ⇒ Object

Make initializer aware of invocations and the initializer proc.



23
24
25
26
27
# File 'lib/thor/invocation.rb', line 23

def initialize(args=[], options={}, config={}, &block) #:nodoc:
  @_invocations = config[:invocations] || Hash.new { |h,k| h[k] = [] }
  @_initializer = [ args, options, config ]
  super
end

#invoke(name = nil, task = nil, args = nil, opts = nil, config = nil) ⇒ Object

Receives a name and invokes it. The name can be a string (either "task" or "namespace:task"), a Thor::Task, a Class or a Thor instance. If the task cannot be guessed by name, it can also be supplied as second argument.

You can also supply the arguments, options and configuration values for the task to be invoked, if none is given, the same values used to initialize the invoker are used to initialize the invoked.

Examples

class A < Thor
def foo
  invoke :bar
  invoke "b:hello", ["José"]
end

def bar
  invoke "b:hello", ["José"]
end
end

class B < Thor
def hello(name)
  puts "hello #{name}"
end
end

You can notice that the method "foo" above invokes two tasks: "bar", which belongs to the same class and "hello" which belongs to the class B.

By using an invocation system you ensure that a task is invoked only once. In the example above, invoking "foo" will invoke "b:hello" just once, even if it's invoked later by "bar" method.

When class A invokes class B, all arguments used on A initialization are supplied to B. This allows lazy parse of options. Let's suppose you have some rspec tasks:

class Rspec < Thor::Group
class_option :mock_framework, :type => :string, :default => :rr

def invoke_mock_framework
  invoke "rspec:#{options[:mock_framework]}"
end
end

As you noticed, it invokes the given mock framework, which might have its own options:

class Rspec::RR < Thor::Group
class_option :style, :type => :string, :default => :mock
end

Since it's not rspec concern to parse mock framework options, when RR is invoked all options are parsed again, so RR can extract only the options that it's going to use.

If you want Rspec::RR to be initialized with its own set of options, you have to do that explicitely:

invoke "rspec:rr", [], :style => :foo

Besides giving an instance, you can also give a class to invoke:

invoke Rspec::RR, [], :style => :foo


95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# File 'lib/thor/invocation.rb', line 95

def invoke(name=nil, task=nil, args=nil, opts=nil, config=nil)
  task, args, opts, config = nil, task, args, opts if task.nil? || task.is_a?(Array)
  args, opts, config = nil, args, opts if args.is_a?(Hash)

  object, task    = _prepare_for_invocation(name, task)
  klass, instance = _initialize_klass_with_initializer(object, args, opts, config)

  method_args = []
  current = @_invocations[klass]

  iterator = proc do |_, task|
    unless current.include?(task.name)
      current << task.name
      task.run(instance, method_args)
    end
  end

  if task
    args ||= []
    method_args = args[Range.new(klass.arguments.size, -1)] || []
    iterator.call(nil, task)
  else
    klass.all_tasks.map(&iterator)
  end
end