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.
@@ -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.