bahuvrihi-tap 0.10.8 → 0.11.0

Sign up to get free protection for your applications and to get access to all the features.
data/History CHANGED
@@ -1,3 +1,15 @@
1
+ == 0.11.0 / 2008-10-20
2
+
3
+ Significant update to Tap with many internal reworks.
4
+ Major changes include:
5
+
6
+ * Addition of Parser/Schema
7
+ * Addition of rap and task declarations
8
+ * Removal of Workflow in preference of workflow
9
+ definitions within Task
10
+ * Refactoring of Test modules
11
+ * Expanded/updated documentation
12
+
1
13
  == 0.10.1 / 2008-08-21
2
14
 
3
15
  Update of Tap with a few improvements to manifests
data/README CHANGED
@@ -28,6 +28,8 @@ Check out these links for tutorials, development, and bug tracking.
28
28
 
29
29
  A simple task illustrates the usage of tap:
30
30
 
31
+ [lib/goodnight.rb]
32
+
31
33
  # Goodnight::manifest your basic goodnight moon task
32
34
  # Says goodnight with a configurable message.
33
35
  class Goodnight < Tap::Task
data/bin/rap CHANGED
@@ -15,7 +15,7 @@ begin
15
15
  task_file = File.expand_path('Tapfile')
16
16
  if File.exists?(task_file)
17
17
  env.loads.unshift(task_file)
18
- env.tasks.register(Dir.pwd, task_file)
18
+ env.tasks.register(Dir.pwd, 'Tapfile')
19
19
  end
20
20
 
21
21
  rescue(Tap::Env::ConfigError)
@@ -32,6 +32,10 @@ Instances of a Configurable class have a <tt>config</tt> method accessing a {Ins
32
32
  config :key, 'value' do |input|
33
33
  input.upcase
34
34
  end
35
+
36
+ def initialize
37
+ initialize_config
38
+ end
35
39
  end
36
40
 
37
41
  Is basically the same as:
@@ -112,7 +116,7 @@ Lazydoc parses a constant name, the key, the value, and any comment following th
112
116
  lazydoc.resolve
113
117
 
114
118
  lazydoc['Name::Space']['key'].to_s # => "This documentation gets parsed."
115
- lazydoc['Name::Space']['another'].subject # => "another value"
119
+ lazydoc['Name::Space']['another'].value # => "another value"
116
120
 
117
121
  Furthermore, Lazydoc can register specific lines for documentation. These lines are parsed to echo what happens in RDoc.
118
122
 
@@ -186,10 +190,12 @@ and batched.
186
190
 
187
191
  Batched tasks enque together, and therefore execute sequentially with the same inputs. Results are aggregated into the underlying Tap::App.
188
192
 
189
- t1.execute('input')
190
- runlist # => [t0, t1, t2, t3, t2]
193
+ t1.enq('input')
191
194
 
192
195
  app = Tap::App.instance
196
+ app.run
197
+
198
+ runlist # => [t0, t1, t2, t3, t2]
193
199
  app.results(t2) # => ["input:one:two", "input:three:two"]
194
200
 
195
201
  Tracking the evolution of a result through a workflow can get complex; Tap audits workflows to help. In the audit trail, the tasks are identified by name. Lets set the names of the tasks and take a look at the audit trails of the t2 results:
@@ -199,7 +205,7 @@ Tracking the evolution of a result through a workflow can get complex; Tap audit
199
205
  t3.name = 'trois'
200
206
 
201
207
  app._results(t2).collect do |_result|
202
- _result.to_s
208
+ _result._to_s
203
209
  end.join("---\n")
204
210
  # =>
205
211
  # o-[] "input"
@@ -221,7 +227,7 @@ A Root represents the base of a directory structure. Roots allow you to alias di
221
227
  root = Tap::Root.new '/path/to/root'
222
228
  root.root # => '/path/to/root'
223
229
  root['config'] # => '/path/to/root/config'
224
- root.filepath('config', 'sample.yml') # => '/path/to/root/config/sampl.yml'
230
+ root.filepath('config', 'sample.yml') # => '/path/to/root/config/sample.yml'
225
231
 
226
232
  While simple, this ability to alias paths is useful, powerful, and forms the basis of the Tap execution environment.
227
233
 
@@ -261,15 +267,18 @@ Instances of Tap::App coordinate the execution of tasks. Apps are basically a s
261
267
  log = StringIO.new
262
268
  app = Tap::App.instance
263
269
  app.logger = Logger.new(log)
270
+ app.logger.formatter = lambda do |severity, time, progname, msg|
271
+ " %s %s: %s\n" % [severity[0,1], progname, msg]
272
+ end
264
273
 
265
274
  t = Tap::Task.intern {|task, *inputs| inputs }
266
275
  t.log 'action', 'to app'
267
- log.string # => " I[15:21:23] action to app\n"
276
+ log.string # => " I action: to app\n"
268
277
 
269
278
  t.enq(1)
270
279
  t.enq(2,3)
271
280
 
272
- app.queue.to_a # => [[t, [1]], [t, [2,3]]
281
+ app.queue.to_a # => [[t, [1]], [t, [2,3]]]
273
282
  app.run
274
283
  app.results(t) # => [[1], [2,3]]
275
284
 
@@ -27,7 +27,7 @@ Generate and destory launch generator scripts, similar to those in {Rails}[http:
27
27
  {command}[link:classes/Tap/Generator/Generators/CommandGenerator.html]:: a new command
28
28
  {config}[link:classes/Tap/Generator/Generators/ConfigGenerator.html]:: a static config file for the specified task
29
29
  {file_task}[link:classes/Tap/Generator/Generators/FileTaskGenerator.html]:: a file task and test
30
- {generator}[link:classes/Tap/Generator/GeneratorseneratorGenerator.html]:: a new generator
30
+ {generator}[link:classes/Tap/Generator/Generators/GeneratorGenerator.html]:: a new generator
31
31
  {root}[link:classes/Tap/Generator/Generators/RootGenerator.html]:: the basic directory structure
32
32
  {task}[link:classes/Tap/Generator/Generators/TaskGenerator.html]:: a task class and test
33
33
 
@@ -68,7 +68,6 @@ Manifest prints a list of all resources (commands, tasks, generators, etc) avail
68
68
  generate (cmd/generate.rb)
69
69
  manifest (cmd/manifest.rb)
70
70
  run (cmd/run.rb)
71
- server (cmd/server.rb)
72
71
  tasks
73
72
  dump (lib/tap/tasks/dump.rb)
74
73
  load (lib/tap/tasks/load.rb)
@@ -86,8 +86,6 @@ Going back to the first example, lets take a look at how a task declaration maps
86
86
 
87
87
  Here is a corresponding class:
88
88
 
89
- [Tapfile]
90
-
91
89
  # Goodnight::manifest your basic goodnight moon task
92
90
  # Says goodnight with a configurable message.
93
91
  class Goodnight < Tap::Task
@@ -259,7 +257,7 @@ Run the test:
259
257
 
260
258
  Run the task:
261
259
 
262
- % tap run -- goodnight moon
260
+ % rap goodnight moon
263
261
  I[23:22:19] goodnight moon
264
262
 
265
263
  Ok, lets share it. Print the current gemspec manifest:
@@ -269,7 +267,7 @@ Ok, lets share it. Print the current gemspec manifest:
269
267
  Rakefile
270
268
  lib/goodnight.rb
271
269
  sample.gemspec
272
- tap.yml
270
+ true tap.yml
273
271
  test/goodnight_test.rb
274
272
  true test/tap_test_helper.rb
275
273
  true test/tap_test_suite.rb
@@ -284,7 +282,7 @@ As you can see, this needs an update to include the task file. Open up sample.g
284
282
  s.platform = Gem::Platform::RUBY
285
283
  s.summary = "sample"
286
284
  s.require_path = "lib"
287
- s.add_dependency("tap", "~> 0.10.8")
285
+ s.add_dependency("tap", ">= 0.11")
288
286
  s.files = %W{
289
287
  lib/goodnight.rb
290
288
  tap.yml
@@ -6,91 +6,91 @@ require 'tap/support/executable_queue'
6
6
  module Tap
7
7
 
8
8
  # App coordinates the setup and running of tasks, and provides an interface
9
- # to the application directory structure. App is convenient for use within
10
- # scripts and, with Env, provides the basis for the 'tap' command line
11
- # application.
9
+ # to the application directory structure. All tasks have an App (by default
10
+ # App.instance) through which tasks access access application-wide resources
11
+ # like the logger, executable queue, aggregator, and dependencies.
12
12
  #
13
13
  # === Running Tasks
14
14
  #
15
- # All tasks have an App (by default App.instance) through which tasks access
16
- # access application-wide resources like the logger. Additionally, task
17
- # enque command are forwarded to App#enq:
15
+ # Task enque command are forwarded to App#enq:
18
16
  #
19
- # t1 = Task.intern {|task, input| input += 1 }
20
- # t1.enq(0)
21
- # app.enq(t1, 1)
17
+ # t0 = Task.intern {|task, input| "#{input}.0" }
18
+ # t0.enq('a')
19
+ # app.enq(t0, 'b')
22
20
  #
23
21
  # app.run
24
- # app.results(t1) # => [1, 2]
22
+ # app.results(t0) # => ['a.0', 'b.0']
25
23
  #
26
24
  # When a task completes, the results will be passed to the task on_complete
27
25
  # block, if set, or be collected into an Aggregator (aggregated results may
28
- # be accessed per-task, as shown above); on_complete blocks typically enque
29
- # other tasks, allowing the construction of imperative workflows:
26
+ # be accessed per-task, as shown above); on_complete blocks typically
27
+ # execute or enque other tasks, allowing the construction of imperative
28
+ # workflows:
30
29
  #
31
30
  # # clear the previous results
32
31
  # app.aggregator.clear
33
32
  #
34
- # t2 = Task.intern {|task, input| input += 10 }
35
- # t1.on_complete {|_result| t2.enq(_result) }
36
- #
37
- # t1.enq 0
38
- # t1.enq 10
33
+ # t1 = Task.intern {|task, input| "#{input}.1" }
34
+ # t0.on_complete {|_result| t1.enq(_result) }
35
+ # t0.enq 'c'
39
36
  #
40
37
  # app.run
41
- # app.results(t1) # => []
42
- # app.results(t2) # => [11, 21]
38
+ # app.results(t0) # => []
39
+ # app.results(t1) # => ['c.0.1']
43
40
  #
44
- # Here t1 has no results because the on_complete block passed them to t2 in
41
+ # Here t0 has no results because the on_complete block passed them to t1 in
45
42
  # a simple sequence.
46
43
  #
47
- # ==== Dependencies
44
+ # === Dependencies
48
45
  #
49
- # Tasks allow the construction of dependency-based workflows as well; tasks
50
- # may be set to depend on other tasks such that the dependent task only
51
- # executes after the dependencies have been resolved.
46
+ # Tasks allow the construction of dependency-based workflows such that a
47
+ # dependent task only executes after its dependencies have been resolved.
52
48
  #
53
- # array = []
54
- # t1 = Task.intern {|task, *inputs| array << inputs }
55
- # t2 = Task.intern {|task| array << self }
49
+ # runlist = []
50
+ # t0 = Task.intern {|task| runlist << task }
51
+ # t1 = Task.intern {|task| runlist << task }
56
52
  #
57
- # t1.depends_on(t2)
58
- # t1.enq(4,5,6)
53
+ # t0.depends_on(t1)
54
+ # t0.enq
59
55
  #
60
56
  # app.run
61
- # array # => [t2, [4,5,6]]
57
+ # runlist # => [t1, t0]
62
58
  #
63
59
  # Once a dependency is resolved, it will not execute again:
64
60
  #
65
- # t1.enq(7,8)
61
+ # t0.enq
66
62
  # app.run
67
- # array # => [t2, [4,5,6], [7,8]]
63
+ # runlist # => [t1, t0, t0]
68
64
  #
69
- # ==== Batching
65
+ # === Batching
70
66
  #
71
67
  # Tasks can be batched, allowing the same input to be enqued to multiple
72
68
  # tasks at once.
73
69
  #
74
- # t1 = Task.intern {|task, input| input += 1 }
75
- # t2 = Task.intern {|task, input| input += 10 }
70
+ # t0 = Task.intern {|task, input| "#{input}.0" }
71
+ # t1 = Task.intern {|task, input| "#{input}.1" }
76
72
  #
77
- # t1.batch_with(t2)
78
- # t1.enq 0
73
+ # t0.batch_with(t1)
74
+ # t0.enq 'a'
75
+ # t1.enq 'b'
79
76
  #
80
77
  # app.run
81
- # app.results(t1) # => [1]
82
- # app.results(t2) # => [10]
78
+ # app.results(t0) # => ['a.0', 'b.0']
79
+ # app.results(t1) # => ['a.1', 'b.1']
83
80
  #
84
- # ==== Executables
81
+ # === Executables
85
82
  #
86
- # App can enque and run any Executable object. One way to initialize an
87
- # Executable for a method is to use the Object#_method added by Tap.
88
- # The mq (method enq) method generates and enques the method in one step.
83
+ # App can enque and run any Executable object. Arbitrary methods may be
84
+ # made into Executables using Object#_method. The mq (method enq) method
85
+ # generates and enques methods in one step.
89
86
  #
90
87
  # array = []
88
+ #
89
+ # # longhand
91
90
  # m = array._method(:push)
92
- #
93
- # app.enq(m, 1)
91
+ # m.enq(1)
92
+ #
93
+ # # shorthand
94
94
  # app.mq(array, :push, 2)
95
95
  #
96
96
  # array.empty? # => true
@@ -100,29 +100,29 @@ module Tap
100
100
  # === Auditing
101
101
  #
102
102
  # All results are audited to track how a given input evolves during a workflow.
103
- # To illustrate auditing, consider a workflow that uses the 'add_one' method
104
- # to add one to an input until the result is 3, then adds five more with the
105
- # 'add_five' method. The final result should always be 8.
106
- #
107
- # t1 = Tap::Task.intern {|task, input| input += 1 }
108
- # t1.name = "add_one"
103
+ # To illustrate auditing, consider and addition workflow that ends in eights.
109
104
  #
110
- # t2 = Tap::Task.intern {|task, input| input += 5 }
111
- # t2.name = "add_five"
105
+ # add_one = Tap::Task.intern({}, 'add_one') {|task, input| input += 1 }
106
+ # add_five = Tap::Task.intern({}, 'add_five') {|task, input| input += 5 }
112
107
  #
113
- # t1.on_complete do |_result|
108
+ # add_one.on_complete do |_result|
114
109
  # # _result is the audit; use the _current method
115
110
  # # to get the current value in the audit trail
111
+ # current_value = _result._current
116
112
  #
117
- # _result._current < 3 ? t1.enq(_result) : t2.enq(_result)
113
+ # if current_value < 3
114
+ # add_one.enq(_result)
115
+ # else
116
+ # add_five.enq(_result)
117
+ # end
118
118
  # end
119
119
  #
120
- # t1.enq(0)
121
- # t1.enq(1)
122
- # t1.enq(2)
120
+ # add_one.enq(0)
121
+ # add_one.enq(1)
122
+ # add_one.enq(2)
123
123
  #
124
124
  # app.run
125
- # app.results(t2) # => [8,8,8]
125
+ # app.results(add_five) # => [8,8,8]
126
126
  #
127
127
  # Although the results are indistinguishable, each achieved the final value
128
128
  # through a different series of tasks. With auditing you can see how each
@@ -130,7 +130,7 @@ module Tap
130
130
  #
131
131
  # # app.results returns the actual result values
132
132
  # # app._results returns the audits for these values
133
- # app._results(t2).each do |_result|
133
+ # app._results(add_five).each do |_result|
134
134
  # puts "How #{_result._original} became #{_result._current}:"
135
135
  # puts _result._to_s
136
136
  # puts
@@ -257,10 +257,6 @@ module Tap
257
257
  name == nil ? nil : filepath('config', "#{name}.yml")
258
258
  end
259
259
 
260
- #
261
- # Execution methods
262
- #
263
-
264
260
  # Sets state = State::READY unless the app is running. Returns self.
265
261
  def ready
266
262
  @state = State::READY unless state == State::RUN
@@ -337,10 +333,8 @@ module Tap
337
333
  "state: #{state} (#{State.state_str(state)}) queue: #{queue.size} results: #{aggregator.size}"
338
334
  end
339
335
 
340
- # Enques the task with the inputs. If the task is batched, then each
341
- # task in task.batch will be enqued with the inputs. Returns task.
342
- #
343
- # An Executable may provided instead of a task.
336
+ # Enques the task (or Executable) with the inputs. If the task is batched,
337
+ # then each task in task.batch will be enqued with the inputs. Returns task.
344
338
  def enq(task, *inputs)
345
339
  case task
346
340
  when Tap::Task
@@ -371,16 +365,17 @@ module Tap
371
365
  # Returns all aggregated results for the specified tasks. Results are
372
366
  # joined into a single array. Arrays of tasks are allowed as inputs.
373
367
  #
374
- # t1 = Task.intern {|task, input| input += 1 }
375
- # t2 = Task.intern {|task, input| input += 10 }
376
- # t3 = t2.initialize_batch_obj
368
+ # t0 = Task.intern {|task, input| "#{input}.0" }
369
+ # t1 = Task.intern {|task, input| "#{input}.1" }
370
+ # t2 = Task.intern {|task, input| "#{input}.2" }
371
+ # t1.batch_with(t2)
377
372
  #
378
- # t1.enq(0)
379
- # t2.enq(1)
373
+ # t0.enq(0)
374
+ # t1.enq(1)
380
375
  #
381
376
  # app.run
382
- # app.results(t1, t2.batch) # => [1, 11, 11]
383
- # app.results(t2, t1) # => [11, 1]
377
+ # app.results(t0, t1.batch) # => ["0.0", "1.1", "1.2"]
378
+ # app.results(t1, t0) # => ["1.1", "0.0"]
384
379
  #
385
380
  def results(*tasks)
386
381
  _results(tasks).collect {|_result| _result._current}
@@ -1,7 +1,7 @@
1
1
  module Tap
2
2
  MAJOR = 0
3
- MINOR = 10
4
- TINY = 8
3
+ MINOR = 11
4
+ TINY = 0
5
5
 
6
6
  VERSION="#{MAJOR}.#{MINOR}.#{TINY}"
7
7
  WEBSITE="http://tap.rubyforge.org"
@@ -450,22 +450,22 @@ module Tap
450
450
  # be returned if the block returns true.
451
451
  #
452
452
  # Returns nil if no file can be found.
453
- # def search_path(dir, path)
454
- # each do |env|
455
- # directory = env.root.filepath(dir)
456
- # file = env.root.filepath(dir, path)
457
- #
458
- # # check the file is relative to the
459
- # # directory, and that the file exists.
460
- # if file.rindex(directory, 0) == 0 &&
461
- # File.exists?(file) &&
462
- # (!block_given? || yield(file))
463
- # return file
464
- # end
465
- # end
466
- #
467
- # nil
468
- # end
453
+ def search_path(dir, path)
454
+ each do |env|
455
+ directory = env.root.filepath(dir)
456
+ file = env.root.filepath(dir, path)
457
+
458
+ # check the file is relative to the
459
+ # directory, and that the file exists.
460
+ if file.rindex(directory, 0) == 0 &&
461
+ File.exists?(file) &&
462
+ (!block_given? || yield(file))
463
+ return file
464
+ end
465
+ end
466
+
467
+ nil
468
+ end
469
469
 
470
470
  # def reset(name, &block)
471
471
  # each do |env|
@@ -474,30 +474,28 @@ module Tap
474
474
  # end
475
475
  # end
476
476
 
477
- TEMPLATES = {
478
- :commands => %Q{<% if count > 1 %>
477
+ #
478
+ TEMPLATES = {}
479
+ TEMPLATES[:commands] = %Q{<% if count > 1 %>
479
480
  <%= env_name %>:
480
481
  <% end %>
481
482
  <% entries.each do |name, const| %>
482
483
  <%= name.ljust(width) %>
483
- <% end %>},
484
-
485
- :tasks => %Q{<% if count > 1 %>
484
+ <% end %>}
485
+ TEMPLATES[:tasks] = %Q{<% if count > 1 %>
486
486
  <%= env_name %>:
487
487
  <% end %>
488
488
  <% entries.each do |name, const| %>
489
489
  <% desc = const.document[const.name]['manifest'] %>
490
490
  <%= name.ljust(width) %><%= desc.empty? ? '' : ' # ' %><%= desc %>
491
- <% end %>},
492
-
493
- :generators => %Q{<% if count > 1 %>
491
+ <% end %>}
492
+ TEMPLATES[:generators] = %Q{<% if count > 1 %>
494
493
  <%= env_name %>:
495
494
  <% end %>
496
495
  <% entries.each do |name, const| %>
497
496
  <% desc = const.document[const.name]['generator'] %>
498
497
  <%= name.ljust(width) %><%= desc.empty? ? '' : ' # ' %><%= desc %>
499
498
  <% end %>}
500
- }
501
499
 
502
500
  def summarize(name, template=TEMPLATES[name])
503
501
  count = 0