Class: Rap::Task
- Inherits:
-
Tap::Task
- Object
- Tap::Task
- Rap::Task
- 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
#
# # :: a help task
# Subclass.task(:help) {|task, args| puts "got #{task.helper}"}
% rap help
got help
Class Attribute Summary collapse
-
.actions ⇒ Object
An array of actions (blocks) associated with this class.
-
.arg_names ⇒ Object
The argument names pulled from a task declaration.
-
.dependencies ⇒ Object
readonly
Returns class dependencies.
Instance Attribute Summary collapse
-
#args ⇒ Object
The arguments assigned to self.
-
#dependencies ⇒ Object
readonly
An array of node dependencies.
Class Method Summary collapse
-
.args ⇒ Object
Returns a Lazydoc::Arguments constructed from arg_names.
-
.build(argh = {}, app = Tap::App.instance) ⇒ Object
Instantiates the instance of self for app and reconfigures it as specified in argh.
-
.depends_on(name, dependency_class) ⇒ Object
Sets a class-level dependency; when task class B depends_on another task class A, instances of B are initialized to depend on a shared instance of A.
-
.inherited(child) ⇒ Object
:nodoc:.
-
.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:.
-
.subclass(const_name, configs = {}, dependencies = []) ⇒ Object
Looks up or creates the Rap::Task subclass specified by const_name and adds the configs and dependencies.
Instance Method Summary collapse
-
#call ⇒ Object
Conditional call to the super call; only calls once and with args (if set).
-
#depends_on(dependency) ⇒ Object
Adds the dependency to self.
-
#initialize(config = {}, app = Tap::App.instance) ⇒ Task
constructor
A new instance of Task.
-
#process(*inputs) ⇒ Object
Collects the inputs into an OpenStruct according to the class arg_names, and calls each class action in turn.
-
#reset ⇒ Object
Resets self so call will call again.
-
#resolve! ⇒ Object
Alias for call.
-
#resolved? ⇒ Boolean
Returns true if already resolved by call.
- #resolving? ⇒ Boolean
Methods included from Declarations
app, desc, instance, namespace, task
Constructor Details
#initialize(config = {}, app = Tap::App.instance) ⇒ Task
Returns a new instance of Task.
213 214 215 216 217 218 219 220 221 222 223 |
# File 'lib/rap/task.rb', line 213 def initialize(config={}, app=Tap::App.instance) super @dependencies = [] @resolved = false @args = nil # setup class dependencies self.class.dependencies.each do |dependency_class| depends_on dependency_class.instance(app) end end |
Class Attribute Details
.actions ⇒ Object
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.
58 59 60 |
# File 'lib/rap/task.rb', line 58 def actions @actions ||= [] end |
.arg_names ⇒ Object
The argument names pulled from a task declaration.
66 67 68 |
# File 'lib/rap/task.rb', line 66 def arg_names @arg_names ||= [] end |
.dependencies ⇒ Object (readonly)
Returns class dependencies
40 41 42 |
# File 'lib/rap/task.rb', line 40 def dependencies @dependencies end |
Instance Attribute Details
#args ⇒ Object
The arguments assigned to self.
211 212 213 |
# File 'lib/rap/task.rb', line 211 def args @args end |
#dependencies ⇒ Object (readonly)
An array of node dependencies
208 209 210 |
# File 'lib/rap/task.rb', line 208 def dependencies @dependencies end |
Class Method Details
.args ⇒ Object
Returns a Lazydoc::Arguments constructed from arg_names.
71 72 73 74 75 |
# File 'lib/rap/task.rb', line 71 def args args = Lazydoc::Arguments.new arg_names.each {|name| args.arguments << name.to_s } args end |
.build(argh = {}, app = Tap::App.instance) ⇒ Object
Instantiates the instance of self for app and reconfigures it as specified in argh.
104 105 106 107 108 109 110 111 112 113 114 115 116 |
# File 'lib/rap/task.rb', line 104 def build(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 |
.depends_on(name, dependency_class) ⇒ Object
Sets a class-level dependency; when task class B depends_on another task class A, instances of B are initialized to depend on a shared instance of A. The shared instance is specific to an app and can be accessed through instance(app).
If a non-nil name is specified, depends_on will create a reader of the dependency instance.
class A < Rap::Task
end
class B < Rap::Task
depends_on :a, A
end
app = Tap::App.new
b = B.new({}, app)
b.dependencies # => [A.instance(app)]
b.a # => A.instance(app)
Returns self.
182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 |
# File 'lib/rap/task.rb', line 182 def depends_on(name, dependency_class) unless dependency_class.ancestors.include?(Rap::Task) raise "not a Rap::Task: #{dependency_class}" end unless dependencies.include?(dependency_class) dependencies << dependency_class end if name # returns the resolved result of the dependency define_method(name) do dependency_class.instance(app) end public(name) end self end |
.inherited(child) ⇒ Object
:nodoc:
47 48 49 50 |
# File 'lib/rap/task.rb', line 47 def inherited(child) # :nodoc: child.instance_variable_set(:@dependencies, dependencies.dup) super 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
88 89 90 91 92 93 94 95 96 97 98 99 100 |
# File 'lib/rap/task.rb', line 88 def parse!(argv=ARGV, app=Tap::App.instance) parser = self.parser argv = parser.parse!(argv, :add_defaults => false) enque = parser.config.delete('enque') instance = build({'config' => parser.nested_config, 'args' => argv.dup}, app) # enque with no inputs to satisfy call, and # clear argv so auto-enque will do the same instance.enq if enque [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).
123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 |
# File 'lib/rap/task.rb', line 123 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 ⇒ Object
Conditional call to the super call; only calls once and with args (if set). Call recursively resolves dependencies and raises an error for circular dependencies.
229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 |
# File 'lib/rap/task.rb', line 229 def call if resolved? return end if resolving? raise DependencyError.new(self) end @resolved = nil begin dependencies.each do |dependency| dependency.call end rescue(DependencyError) $!.trace.unshift(self) raise $! end @resolved = true args ? super(*args) : super() end |
#depends_on(dependency) ⇒ Object
Adds the dependency to self.
297 298 299 300 301 302 303 |
# File 'lib/rap/task.rb', line 297 def depends_on(dependency) raise "cannot depend on self" if dependency == self unless dependencies.include?(dependency) dependencies << dependency end self 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.
275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 |
# File 'lib/rap/task.rb', line 275 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 |
#reset ⇒ Object
Resets self so call will call again. Also sets result to nil.
267 268 269 270 |
# File 'lib/rap/task.rb', line 267 def reset raise "cannot reset when resolving" if resolving? @resolved = false end |
#resolve! ⇒ Object
Alias for call.
253 254 255 |
# File 'lib/rap/task.rb', line 253 def resolve! call end |
#resolved? ⇒ Boolean
Returns true if already resolved by call.
258 259 260 |
# File 'lib/rap/task.rb', line 258 def resolved? @resolved == true end |
#resolving? ⇒ Boolean
262 263 264 |
# File 'lib/rap/task.rb', line 262 def resolving? @resolved == nil end |