tap 0.17.1 → 0.18.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -2,7 +2,7 @@
2
2
 
3
3
  === Sequence
4
4
 
5
- A simple sequence using the --: syntax that joins the previous to next task.
5
+ A simple sequence using the --: syntax.
6
6
 
7
7
  % tap run -- load 'goodnight moon' --: dump
8
8
  goodnight moon
data/lib/tap.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  lib = File.expand_path(File.dirname(__FILE__))
2
2
  $:.unshift(lib) unless $:.include?(lib)
3
3
 
4
- require 'tap/constants'
4
+ require 'tap/version'
5
5
  require 'tap/exe'
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. Internally used to store class
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
- @stack = middleware.new(@stack, *argv)
200
+ synchronize do
201
+ @stack = middleware.new(@stack, *argv)
202
+ end
224
203
  end
225
204
 
226
- def build(schema, options={})
227
- unless schema.kind_of?(Schema)
228
- schema = Schema.new(schema)
229
- end
205
+ # Returns an array of middlware in use by self.
206
+ def middleware
207
+ middleware = []
230
208
 
231
- if resources = options[:resources]
232
- schema.resolve! do |type, id|
233
- resources[type][id]
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
- schema.validate!
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 audited result of nodes with no join
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/support/templater'
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 scan(load_path, pattern='**/*.rb')
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 (because a dir is needed to figure the relative path).
77
- # A conflict could arise if the same path is globed from two different
78
- # dirs... no surefire solution.
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.scan(load_path) do |type, constant|
445
- entries = registry[type.to_sym] ||= []
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
- # Environment-seek
493
- def eeek(type, key)
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 result = env.manifest(type).minimatch(key)
508
- return [env, result]
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
- # Searches across each for the first registered object minimatching key. A
516
- # single env can be specified by using a compound key like 'env_key:key'.
517
- #
518
- # Returns nil if no matching object is found.
519
- def seek(type, key, &block) # :yields: env, key
520
- env, result = eeek(type, key, &block)
521
- result
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 = Support::Templater.new(template, :env => env, :env_key => env_keys[env])
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|
@@ -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/support/intern'
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 a simple, unsyncrhonized, multi-way 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 array like [inputs, outputs, instance] where
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 and return an array like [inputs,
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 Support::Intern(:call)
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.