tap 0.17.1 → 0.18.0
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/History +22 -0
- data/README +15 -14
- data/cmd/console.rb +1 -1
- data/cmd/manifest.rb +25 -5
- data/cmd/run.rb +60 -25
- data/doc/API +37 -38
- data/doc/Class Reference +36 -46
- data/doc/Examples/Workflow +1 -1
- data/lib/tap.rb +1 -1
- data/lib/tap/app.rb +15 -80
- data/lib/tap/app/node.rb +0 -14
- data/lib/tap/env.rb +55 -27
- data/lib/tap/env/manifest.rb +2 -2
- data/lib/tap/intern.rb +50 -0
- data/lib/tap/join.rb +12 -9
- data/lib/tap/middleware.rb +56 -0
- data/lib/tap/schema.rb +182 -14
- data/lib/tap/schema/utils.rb +5 -3
- data/lib/tap/task.rb +53 -130
- data/lib/tap/tasks/dump.rb +1 -1
- data/lib/tap/tasks/load.rb +8 -9
- data/lib/tap/templater.rb +203 -0
- data/lib/tap/{constants.rb → version.rb} +2 -2
- metadata +6 -5
- data/lib/tap/support/intern.rb +0 -53
- data/lib/tap/support/templater.rb +0 -207
data/doc/Examples/Workflow
CHANGED
data/lib/tap.rb
CHANGED
data/lib/tap/app.rb
CHANGED
@@ -4,7 +4,6 @@ require 'tap/app/node'
|
|
4
4
|
require 'tap/app/state'
|
5
5
|
require 'tap/app/stack'
|
6
6
|
require 'tap/app/queue'
|
7
|
-
require 'tap/schema'
|
8
7
|
|
9
8
|
module Tap
|
10
9
|
|
@@ -94,26 +93,6 @@ module Tap
|
|
94
93
|
#
|
95
94
|
# Middleware can be nested with multiple calls to use.
|
96
95
|
#
|
97
|
-
# === Dependencies
|
98
|
-
#
|
99
|
-
# Nodes allow the construction of dependency-based workflows. A node only
|
100
|
-
# executes after its dependencies have been resolved (ie executed).
|
101
|
-
#
|
102
|
-
# runlist = []
|
103
|
-
# n0 = app.node { runlist << 0 }
|
104
|
-
# n1 = app.node { runlist << 1 }
|
105
|
-
#
|
106
|
-
# n0.depends_on(n1)
|
107
|
-
# app.enq(n0)
|
108
|
-
#
|
109
|
-
# app.run
|
110
|
-
# runlist # => [1, 0]
|
111
|
-
#
|
112
|
-
# Dependencies are resolved <em>every time</em> a node executes; individual
|
113
|
-
# dependencies can implement single-execution if desired. Dependencies are
|
114
|
-
# not resolved with arguments, ie dependency nodes must be able to execute
|
115
|
-
# without inputs.
|
116
|
-
#
|
117
96
|
class App
|
118
97
|
class << self
|
119
98
|
# Sets the current app instance
|
@@ -148,8 +127,7 @@ module Tap
|
|
148
127
|
# The application queue
|
149
128
|
attr_reader :queue
|
150
129
|
|
151
|
-
# A cache of application-specific data.
|
152
|
-
# instances of tasks. Not recommended for casual use.
|
130
|
+
# A cache of application-specific data.
|
153
131
|
attr_reader :cache
|
154
132
|
|
155
133
|
# The default joins for nodes that have no joins set
|
@@ -171,7 +149,6 @@ module Tap
|
|
171
149
|
@stack = options[:stack] || Stack.new(self)
|
172
150
|
@queue = options[:queue] || Queue.new
|
173
151
|
@cache = options[:cache] || {}
|
174
|
-
@trace = []
|
175
152
|
@default_joins = []
|
176
153
|
on_complete(&block)
|
177
154
|
|
@@ -220,27 +197,24 @@ module Tap
|
|
220
197
|
|
221
198
|
# Adds the specified middleware to the stack.
|
222
199
|
def use(middleware, *argv)
|
223
|
-
|
200
|
+
synchronize do
|
201
|
+
@stack = middleware.new(@stack, *argv)
|
202
|
+
end
|
224
203
|
end
|
225
204
|
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
end
|
205
|
+
# Returns an array of middlware in use by self.
|
206
|
+
def middleware
|
207
|
+
middleware = []
|
230
208
|
|
231
|
-
|
232
|
-
|
233
|
-
|
209
|
+
synchronize do
|
210
|
+
current = stack
|
211
|
+
until current.kind_of?(Stack)
|
212
|
+
middleware << current
|
213
|
+
current = current.stack
|
234
214
|
end
|
235
215
|
end
|
236
216
|
|
237
|
-
|
238
|
-
|
239
|
-
if options[:clean]
|
240
|
-
reset
|
241
|
-
end
|
242
|
-
|
243
|
-
schema.build!(self)
|
217
|
+
middleware
|
244
218
|
end
|
245
219
|
|
246
220
|
# Clears the cache, the queue, and resets the stack so that no middleware
|
@@ -257,36 +231,6 @@ module Tap
|
|
257
231
|
end
|
258
232
|
end
|
259
233
|
|
260
|
-
# Dispatches each dependency of node. A block can be given to do something
|
261
|
-
# else with the nodes (ex: reset single-execution dependencies). Resolve
|
262
|
-
# will recursively yield dependencies if specified.
|
263
|
-
#
|
264
|
-
# Resolve raises an error for circular dependencies.
|
265
|
-
def resolve(node, recursive=false, &block)
|
266
|
-
node.dependencies.each do |dependency|
|
267
|
-
if @trace.include?(dependency)
|
268
|
-
@trace.push dependency
|
269
|
-
raise DependencyError.new(@trace)
|
270
|
-
end
|
271
|
-
|
272
|
-
# mark the results at the index to prevent
|
273
|
-
# infinite loops with circular dependencies
|
274
|
-
@trace.push dependency
|
275
|
-
|
276
|
-
if recursive
|
277
|
-
resolve(dependency, recursive, &block)
|
278
|
-
end
|
279
|
-
|
280
|
-
if block_given?
|
281
|
-
yield(dependency)
|
282
|
-
else
|
283
|
-
dispatch(dependency)
|
284
|
-
end
|
285
|
-
|
286
|
-
@trace.pop
|
287
|
-
end
|
288
|
-
end
|
289
|
-
|
290
234
|
# Dispatches node to the application stack with the inputs.
|
291
235
|
def execute(node, *inputs)
|
292
236
|
dispatch(node, inputs)
|
@@ -295,13 +239,11 @@ module Tap
|
|
295
239
|
# Dispatch sends the node into the application stack with the inputs.
|
296
240
|
# Dispatch does the following in order:
|
297
241
|
#
|
298
|
-
# - resolve node dependencies using resolve_dependencies
|
299
242
|
# - call stack with the node and inputs
|
300
243
|
# - call the node joins, if set, or the default_joins with the results
|
301
244
|
#
|
302
245
|
# Dispatch returns the node result.
|
303
246
|
def dispatch(node, inputs=[])
|
304
|
-
resolve(node)
|
305
247
|
result = stack.call(node, inputs)
|
306
248
|
|
307
249
|
joins = node.joins.empty? ? default_joins : node.joins
|
@@ -430,8 +372,8 @@ module Tap
|
|
430
372
|
target
|
431
373
|
end
|
432
374
|
|
433
|
-
# Sets the block to receive the
|
434
|
-
# (ie the block is set as default_join).
|
375
|
+
# Sets the block to receive the result of nodes with no joins
|
376
|
+
# (ie the block is set as a default_join).
|
435
377
|
def on_complete(&block) # :yields: _result
|
436
378
|
self.default_joins << block if block
|
437
379
|
self
|
@@ -443,12 +385,5 @@ module Tap
|
|
443
385
|
# called on an running App. They are handled by the run rescue code.
|
444
386
|
class TerminateError < RuntimeError
|
445
387
|
end
|
446
|
-
|
447
|
-
# Raised when Tap::App#resolve detects a circular dependency.
|
448
|
-
class DependencyError < StandardError
|
449
|
-
def initialize(trace)
|
450
|
-
super "circular dependency: [#{trace.join(', ')}]"
|
451
|
-
end
|
452
|
-
end
|
453
388
|
end
|
454
389
|
end
|
data/lib/tap/app/node.rb
CHANGED
@@ -9,9 +9,6 @@ module Tap
|
|
9
9
|
# The joins called when call completes
|
10
10
|
attr_accessor :joins
|
11
11
|
|
12
|
-
# An array of node dependencies
|
13
|
-
attr_reader :dependencies
|
14
|
-
|
15
12
|
# Interns a new node by extending the block with Node.
|
16
13
|
def self.intern(&block)
|
17
14
|
block.extend self
|
@@ -20,7 +17,6 @@ module Tap
|
|
20
17
|
# Sets up required variables for extended objects.
|
21
18
|
def self.extended(obj) # :nodoc:
|
22
19
|
obj.instance_variable_set(:@joins, [])
|
23
|
-
obj.instance_variable_set(:@dependencies, [])
|
24
20
|
end
|
25
21
|
|
26
22
|
# Sets the block as a join for self.
|
@@ -28,16 +24,6 @@ module Tap
|
|
28
24
|
self.joins << block if block
|
29
25
|
self
|
30
26
|
end
|
31
|
-
|
32
|
-
# Adds the dependency to self. Dependencies are resolved by an app
|
33
|
-
# during App#dispatch and must be valid nodes.
|
34
|
-
def depends_on(dependency)
|
35
|
-
raise "cannot depend on self" if dependency == self
|
36
|
-
unless dependencies.include?(dependency)
|
37
|
-
dependencies << dependency
|
38
|
-
end
|
39
|
-
self
|
40
|
-
end
|
41
27
|
end
|
42
28
|
end
|
43
29
|
end
|
data/lib/tap/env.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'tap/root'
|
2
2
|
require 'tap/env/manifest'
|
3
|
-
require 'tap/
|
3
|
+
require 'tap/templater'
|
4
4
|
autoload(:YAML, 'yaml')
|
5
5
|
|
6
6
|
module Tap
|
@@ -65,7 +65,7 @@ module Tap
|
|
65
65
|
end
|
66
66
|
end
|
67
67
|
|
68
|
-
def
|
68
|
+
def scan_dir(load_path, pattern='**/*.rb')
|
69
69
|
Dir.chdir(load_path) do
|
70
70
|
Dir.glob(pattern).each do |require_path|
|
71
71
|
next unless File.file?(require_path)
|
@@ -73,15 +73,9 @@ module Tap
|
|
73
73
|
default_const_name = require_path.chomp('.rb').camelize
|
74
74
|
|
75
75
|
# note: the default const name has to be set here to allow for implicit
|
76
|
-
# constant attributes
|
77
|
-
#
|
78
|
-
|
79
|
-
document = Lazydoc[require_path]
|
80
|
-
case document.default_const_name
|
81
|
-
when nil then document.default_const_name = default_const_name
|
82
|
-
when default_const_name
|
83
|
-
else raise "found a conflicting default const name"
|
84
|
-
end
|
76
|
+
# constant attributes. An error can arise if the same path is globed
|
77
|
+
# from two different dirs... no surefire solution.
|
78
|
+
Lazydoc[require_path].default_const_name = default_const_name
|
85
79
|
|
86
80
|
# scan for constants
|
87
81
|
Lazydoc::Document.scan(File.read(require_path)) do |const_name, type, comment|
|
@@ -100,6 +94,27 @@ module Tap
|
|
100
94
|
end
|
101
95
|
end
|
102
96
|
end
|
97
|
+
|
98
|
+
def scan(path, key='[a-z_]+')
|
99
|
+
Lazydoc::Document.scan(File.read(path), key) do |const_name, type, comment|
|
100
|
+
if const_name.empty?
|
101
|
+
unless const_name = Lazydoc[path].default_const_name
|
102
|
+
raise "could not determine a constant name for #{type} in: #{path.inspect}"
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
constant = Constant.new(const_name, path, comment)
|
107
|
+
yield(type, constant)
|
108
|
+
|
109
|
+
###############################################################
|
110
|
+
# [depreciated] manifest as a task key will be removed at 1.0
|
111
|
+
if type == 'manifest'
|
112
|
+
warn "depreciation: ::task should be used instead of ::manifest as a resource key (#{require_path})"
|
113
|
+
yield('task', constant)
|
114
|
+
end
|
115
|
+
###############################################################
|
116
|
+
end
|
117
|
+
end
|
103
118
|
end
|
104
119
|
self.instance = nil
|
105
120
|
|
@@ -441,9 +456,8 @@ module Tap
|
|
441
456
|
load_paths.each do |load_path|
|
442
457
|
next unless File.directory?(load_path)
|
443
458
|
|
444
|
-
Env.
|
445
|
-
|
446
|
-
entries << constant
|
459
|
+
Env.scan_dir(load_path) do |type, constant|
|
460
|
+
(registry[type.to_sym] ||= []) << constant
|
447
461
|
end
|
448
462
|
end
|
449
463
|
|
@@ -468,6 +482,15 @@ module Tap
|
|
468
482
|
builders[type] = block
|
469
483
|
end
|
470
484
|
|
485
|
+
#--
|
486
|
+
# Potential bug, constants can be added twice.
|
487
|
+
def scan(path, key='[a-z_]+')
|
488
|
+
registry = self.registry
|
489
|
+
Env.scan(path, key) do |type, constant|
|
490
|
+
(registry[type.to_sym] ||= []) << constant
|
491
|
+
end
|
492
|
+
end
|
493
|
+
|
471
494
|
def manifest(type) # :yields: env
|
472
495
|
type = type.to_sym
|
473
496
|
|
@@ -488,9 +511,11 @@ module Tap
|
|
488
511
|
registries.clear
|
489
512
|
end
|
490
513
|
|
491
|
-
|
492
|
-
#
|
493
|
-
|
514
|
+
# Searches across each for the first registered object minimatching key. A
|
515
|
+
# single env can be specified by using a compound key like 'env_key:key'.
|
516
|
+
#
|
517
|
+
# Returns nil if no matching object is found.
|
518
|
+
def seek(type, key, value_only=true)
|
494
519
|
key =~ COMPOUND_KEY
|
495
520
|
envs = if $2
|
496
521
|
# compound key, match for env
|
@@ -504,21 +529,24 @@ module Tap
|
|
504
529
|
# traverse envs looking for the first
|
505
530
|
# manifest entry matching key
|
506
531
|
envs.each do |env|
|
507
|
-
if
|
508
|
-
return [env,
|
532
|
+
if value = env.manifest(type).minimatch(key)
|
533
|
+
return value_only ? value : [env, value]
|
509
534
|
end
|
510
535
|
end
|
511
536
|
|
512
537
|
nil
|
513
538
|
end
|
514
539
|
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
540
|
+
def reverse_seek(type, key_only=true, &block)
|
541
|
+
each do |env|
|
542
|
+
manifest = env.manifest(type)
|
543
|
+
if value = manifest.find(&block)
|
544
|
+
key = manifest.minihash(true)[value]
|
545
|
+
return key_only ? key : "#{minihash(true)[env]}:#{key}"
|
546
|
+
end
|
547
|
+
end
|
548
|
+
|
549
|
+
nil
|
522
550
|
end
|
523
551
|
|
524
552
|
# All templaters are yielded to the block before any are built. This
|
@@ -530,7 +558,7 @@ module Tap
|
|
530
558
|
|
531
559
|
env_keys = minihash(true)
|
532
560
|
collect do |env|
|
533
|
-
templater =
|
561
|
+
templater = Templater.new(template, :env => env, :env_key => env_keys[env])
|
534
562
|
yield(templater, globals) if block_given?
|
535
563
|
templater
|
536
564
|
end.collect! do |templater|
|
data/lib/tap/env/manifest.rb
CHANGED
@@ -43,8 +43,8 @@ module Tap
|
|
43
43
|
# env can be specified by using a compound key like 'env_key:key'.
|
44
44
|
#
|
45
45
|
# Returns nil if no matching entry is found.
|
46
|
-
def seek(key)
|
47
|
-
env.seek(type, key)
|
46
|
+
def seek(key, value_only=true)
|
47
|
+
env.seek(type, key, value_only)
|
48
48
|
end
|
49
49
|
|
50
50
|
def [](key)
|
data/lib/tap/intern.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
module Tap
|
2
|
+
# Generates an Intern module to override the specified method_name. Intern
|
3
|
+
# modules are useful to override a tiny bit of functionality without having
|
4
|
+
# to generate a full subclass.
|
5
|
+
#
|
6
|
+
# An Intern module:
|
7
|
+
#
|
8
|
+
# - adds an accessor for <method_name>_block
|
9
|
+
# - overrides <method_name> to call the block, prepending self to
|
10
|
+
# the input arguments
|
11
|
+
#
|
12
|
+
# For example:
|
13
|
+
#
|
14
|
+
# array = [1,2,3].extend Intern(:last)
|
15
|
+
#
|
16
|
+
# array.last # => 3
|
17
|
+
# array.last_block = lambda {|arr| arr.first }
|
18
|
+
# array.last # => 3
|
19
|
+
#
|
20
|
+
def self.Intern(method_name)
|
21
|
+
mod = INTERN_MODULES[method_name.to_sym]
|
22
|
+
return mod unless mod == nil
|
23
|
+
|
24
|
+
mod = INTERN_MODULES[method_name.to_sym] = Module.new
|
25
|
+
mod.module_eval %Q{
|
26
|
+
attr_accessor :#{method_name}_block
|
27
|
+
|
28
|
+
def #{method_name}(*inputs)
|
29
|
+
return super unless #{method_name}_block
|
30
|
+
inputs.unshift(self)
|
31
|
+
|
32
|
+
arity = #{method_name}_block.arity
|
33
|
+
n = inputs.length
|
34
|
+
unless n == arity || (arity < 0 && (-1-n) <= arity)
|
35
|
+
raise ArgumentError.new("wrong number of arguments (\#{n} for \#{arity})")
|
36
|
+
end
|
37
|
+
|
38
|
+
#{method_name}_block.call(*inputs)
|
39
|
+
end
|
40
|
+
}
|
41
|
+
mod
|
42
|
+
end
|
43
|
+
|
44
|
+
# An array of already-declared intern modules,
|
45
|
+
# keyed by method_name.
|
46
|
+
INTERN_MODULES = {}
|
47
|
+
|
48
|
+
# An Intern module for :process.
|
49
|
+
Intern = Tap::Intern(:process)
|
50
|
+
end
|
data/lib/tap/join.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
require 'tap/app'
|
2
|
-
require 'tap/
|
2
|
+
require 'tap/intern'
|
3
3
|
|
4
4
|
module Tap
|
5
5
|
class App
|
@@ -9,7 +9,7 @@ module Tap
|
|
9
9
|
end
|
10
10
|
end
|
11
11
|
|
12
|
-
# :startdoc::join
|
12
|
+
# :startdoc::join an unsyncrhonized, multi-way join
|
13
13
|
#
|
14
14
|
# Join defines an unsynchronized, multi-way join where n inputs send their
|
15
15
|
# results to m outputs. Flags can augment how the results are passed, in
|
@@ -25,10 +25,7 @@ module Tap
|
|
25
25
|
super
|
26
26
|
end
|
27
27
|
|
28
|
-
# Parses the argv into an
|
29
|
-
# inputs and outputs implicitly define the inputs and output for the
|
30
|
-
# instance. By default parse parses an argh then calls instantiate,
|
31
|
-
# but there is no requirement that this occurs in subclasses.
|
28
|
+
# Parses the argv into an instance of self.
|
32
29
|
def parse(argv=ARGV, app=Tap::App.instance)
|
33
30
|
parse!(argv.dup, app)
|
34
31
|
end
|
@@ -47,8 +44,7 @@ module Tap
|
|
47
44
|
}, app)
|
48
45
|
end
|
49
46
|
|
50
|
-
# Instantiates an instance of self
|
51
|
-
# outputs, instance].
|
47
|
+
# Instantiates an instance of self.
|
52
48
|
def instantiate(argh, app=Tap::App.instance)
|
53
49
|
new(argh[:config] || {}, app)
|
54
50
|
end
|
@@ -61,7 +57,7 @@ module Tap
|
|
61
57
|
def intern(config={}, app=Tap::App.instance, &block) # :yields: join, result
|
62
58
|
instance = new(config, app)
|
63
59
|
if block_given?
|
64
|
-
instance.extend
|
60
|
+
instance.extend Intern(:call)
|
65
61
|
instance.call_block = block
|
66
62
|
end
|
67
63
|
instance
|
@@ -151,6 +147,13 @@ module Tap
|
|
151
147
|
end
|
152
148
|
end
|
153
149
|
|
150
|
+
def to_hash
|
151
|
+
{
|
152
|
+
:class => self.class,
|
153
|
+
:config => config.to_hash
|
154
|
+
}
|
155
|
+
end
|
156
|
+
|
154
157
|
protected
|
155
158
|
|
156
159
|
# Dispatches the results to the node.
|