Class: Rap::Task

Inherits:
Tap::Task
  • Object
show all
Extended by:
Declarations
Defined in:
lib/rap/task.rb,
lib/rap/declarations.rb

Overview

Rap tasks are a special breed of Tap::Task designed to behave much like Rake tasks. As such, declaration tasks:

  • return nil and pass nil in workflows

  • only execute once

  • are effectively singletons (one instance per app)

  • allow for multiple actions

The Rap::Task class partially includes Declarations so subclasses may directly declare tasks. A few alias acrobatics makes it so that ONLY Declarations#task is made available (desc cannot be used because Task classes already use that method for documentation, and namespace would be silly).

Weird? Yes, but it leads to this syntax:

# [Rapfile]
# class Subclass < Rap::Task
#   def helper(); "help"; end
# end
#
# # ::desc a help task
# Subclass.task(:help) {|task, args| puts "got #{task.helper}"}

% rap help
got help

Class Attribute Summary collapse

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Declarations

app, app=, current_desc, current_namespace, desc, env, env=, instance, namespace, task

Methods included from Utils

#sh

Constructor Details

#initialize(config = {}, app = Tap::App.instance) ⇒ Task

Returns a new instance of Task.



146
147
148
149
150
151
# File 'lib/rap/task.rb', line 146

def initialize(config={}, app=Tap::App.instance)
  super
  @resolved = false
  @result = nil
  @args = nil
end

Class Attribute Details

.actionsObject

An array of actions (blocks) associated with this class. Each of the actions is called during process, with the instance and any args passed to process organized into an OpenStruct.



44
45
46
# File 'lib/rap/task.rb', line 44

def actions
  @actions ||= []
end

.arg_namesObject

The argument names pulled from a task declaration.



52
53
54
# File 'lib/rap/task.rb', line 52

def arg_names
  @arg_names ||= []
end

Instance Attribute Details

#argsObject

The arguments assigned to self during call.



144
145
146
# File 'lib/rap/task.rb', line 144

def args
  @args
end

#resultObject (readonly)

The result of self, set by call.



141
142
143
# File 'lib/rap/task.rb', line 141

def result
  @result
end

Class Method Details

.argsObject

Returns a Lazydoc::Arguments constructed from arg_names.



57
58
59
60
61
# File 'lib/rap/task.rb', line 57

def args
  args = Lazydoc::Arguments.new
  arg_names.each {|name| args.arguments << name.to_s }
  args
end

.instantiate(argh = {}, app = Tap::App.instance) ⇒ Object

Instantiates the instance of self for app and reconfigures it as specified in argh.



82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/rap/task.rb', line 82

def instantiate(argh={}, app=Tap::App.instance)
  instance = self.instance(app)
  
  if config = argh[:config]
    instance.reconfigure(config)
  end
  
  if args = argh[:args]
    instance.args = args
  end
  
  instance
end

.parse!(argv = ARGV, app = Tap::App.instance) ⇒ Object

Parses as normal, but also stores the arguments on the instance to allows arguments to be specified on dependency tasks:

# [Rapfile]
# Rap.task(:a, :obj) {|t, a| puts "A #{a.obj}"}
# Rap.task({:b => :a}, :obj) {|t, a| puts "B #{a.obj}"}

% rap b world -- a hello
A hello
B world


74
75
76
77
78
# File 'lib/rap/task.rb', line 74

def parse!(argv=ARGV, app=Tap::App.instance)
  instance = super
  instance.args = argv
  instance
end

.subclass(const_name, configs = {}, dependencies = []) ⇒ Object

Looks up or creates the Rap::Task subclass specified by const_name and adds the configs and dependencies.

Configurations are always validated using the yaml transformation block (see Configurable::Validation).



101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/rap/task.rb', line 101

def subclass(const_name, configs={}, dependencies=[])
  # lookup or generate the subclass
  subclass = Tap::Env::Constant.constantize(const_name.to_s) do |base, constants|
    subclass_const = constants.pop
    constants.inject(base) do |namespace, const|
      # nesting Task classes into other Task classes is required
      # for namespaces with the same name as a task
      namespace.const_set(const, Class.new(Rap::Task))
    end.const_set(subclass_const, Class.new(self))
  end

  # check a correct class was found
  unless subclass.ancestors.include?(self)
    raise "not a #{self}: #{subclass}"
  end

  # append configuration (note that specifying a desc 
  # prevents lazydoc registration of these lines)
  convert_to_yaml = Configurable::Validation.yaml
  configs.each_pair do |key, value|
    subclass.send(:config, key, value, :desc => "", &convert_to_yaml)
  end

  # add dependencies
  dependencies.each do |dependency|
    dependency_name = File.basename(dependency.to_s.underscore)
    
    # this suppresses 'method redefined' warnings
    if subclass.method_defined?(dependency_name)
      subclass.send(:undef_method, dependency_name)
    end
    
    subclass.send(:depends_on, dependency_name, dependency)
  end
  
  subclass
end

Instance Method Details

#call(*args) ⇒ Object

Conditional call to the super call; only calls once. Returns result.



154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
# File 'lib/rap/task.rb', line 154

def call(*args)
  
  # Declaration tasks function as dependencies, but unlike normal
  # dependencies, they CAN take arguments from the command line.
  # Such arguments will be set as args, and be used to enque the
  # task.  
  #
  # If the task executes from the queue first, args will be
  # provided to call and they should equal self.args.  If the task
  # executes as a dependency first, call will not receive args and
  # in that case self.args will be used.
  #
  # This warns for cases that odd workflows can produce where the
  # args have been set and DIFFERENT args are used to enque the task.
  # In these cases always go with the pre-set args but warn the issue.
  self.args ||= args
  unless self.args == args
    if @resolved
      warn "warn: ignorning dependency task inputs #{args.inspect} (#{self})"
    else
      warn "warn: invoking dependency task with preset args #{self.args.inspect} and not inputs #{args.inspect} (#{self})"
    end
  end
  
  unless @resolved
    @resolved = true
    @result = super(*self.args)
  end
  result
end

#process(*inputs) ⇒ Object

Collects the inputs into an OpenStruct according to the class arg_names, and calls each class action in turn. This behavior echoes the behavior of Rake tasks.



199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
# File 'lib/rap/task.rb', line 199

def process(*inputs)
  # collect inputs to make a rakish-args object
  args = {}
  self.class.arg_names.each do |arg_name|
    break if inputs.empty?
    args[arg_name] = inputs.shift
  end
  args = OpenStruct.new(args)
  
  # execute each block assciated with this task
  self.class.actions.each do |action|
    case action.arity
    when 0 then action.call()
    when 1 then action.call(self)
    else action.call(self, args)
    end
  end
  
  nil
end

#resetObject

Resets self so call will call again. Also sets result to nil.



191
192
193
194
# File 'lib/rap/task.rb', line 191

def reset
  @resolved = false
  @result = nil
end

#resolved?Boolean

Returns true if already resolved by call.

Returns:

  • (Boolean)


186
187
188
# File 'lib/rap/task.rb', line 186

def resolved?
  @resolved
end