tap 0.10.1 → 0.11.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (124) hide show
  1. data/History +12 -0
  2. data/MIT-LICENSE +0 -2
  3. data/README +23 -32
  4. data/bin/rap +116 -0
  5. data/bin/tap +6 -9
  6. data/cgi/run.rb +67 -0
  7. data/cmd/console.rb +1 -1
  8. data/cmd/destroy.rb +4 -4
  9. data/cmd/generate.rb +4 -4
  10. data/cmd/manifest.rb +61 -53
  11. data/cmd/run.rb +8 -75
  12. data/doc/Class Reference +130 -121
  13. data/doc/Command Reference +76 -124
  14. data/doc/Syntax Reference +290 -0
  15. data/doc/Tutorial +305 -237
  16. data/lib/tap/app.rb +140 -467
  17. data/lib/tap/constants.rb +2 -2
  18. data/lib/tap/declarations.rb +211 -0
  19. data/lib/tap/env.rb +171 -193
  20. data/lib/tap/exe.rb +100 -21
  21. data/lib/tap/file_task.rb +3 -3
  22. data/lib/tap/generator/base.rb +1 -1
  23. data/lib/tap/generator/destroy.rb +10 -10
  24. data/lib/tap/generator/generate.rb +29 -18
  25. data/lib/tap/generator/generators/command/command_generator.rb +2 -2
  26. data/lib/tap/generator/generators/command/templates/command.erb +2 -2
  27. data/lib/tap/generator/generators/config/config_generator.rb +3 -3
  28. data/lib/tap/generator/generators/config/templates/doc.erb +1 -1
  29. data/lib/tap/generator/generators/file_task/file_task_generator.rb +1 -1
  30. data/lib/tap/generator/generators/file_task/templates/task.erb +1 -1
  31. data/lib/tap/generator/generators/file_task/templates/test.erb +1 -1
  32. data/lib/tap/generator/generators/generator/generator_generator.rb +27 -0
  33. data/lib/tap/generator/generators/generator/templates/task.erb +27 -0
  34. data/lib/tap/generator/generators/root/root_generator.rb +13 -13
  35. data/lib/tap/generator/generators/root/templates/README +0 -0
  36. data/lib/tap/generator/generators/root/templates/Rakefile +2 -2
  37. data/lib/tap/generator/generators/root/templates/gemspec +4 -5
  38. data/lib/tap/generator/generators/root/templates/tapfile +11 -8
  39. data/lib/tap/generator/generators/root/templates/test/tap_test_suite.rb +1 -1
  40. data/lib/tap/generator/generators/task/task_generator.rb +1 -3
  41. data/lib/tap/generator/generators/task/templates/test.erb +1 -3
  42. data/lib/tap/patches/optparse/summarize.rb +62 -0
  43. data/lib/tap/root.rb +41 -29
  44. data/lib/tap/support/aggregator.rb +16 -3
  45. data/lib/tap/support/assignments.rb +10 -9
  46. data/lib/tap/support/audit.rb +58 -64
  47. data/lib/tap/support/class_configuration.rb +33 -44
  48. data/lib/tap/support/combinator.rb +125 -0
  49. data/lib/tap/support/configurable.rb +13 -14
  50. data/lib/tap/support/configurable_class.rb +21 -43
  51. data/lib/tap/support/configuration.rb +55 -9
  52. data/lib/tap/support/constant.rb +87 -13
  53. data/lib/tap/support/constant_manifest.rb +116 -0
  54. data/lib/tap/support/dependencies.rb +54 -0
  55. data/lib/tap/support/dependency.rb +44 -0
  56. data/lib/tap/support/executable.rb +247 -32
  57. data/lib/tap/support/executable_queue.rb +1 -1
  58. data/lib/tap/support/gems/rake.rb +29 -8
  59. data/lib/tap/support/gems.rb +10 -30
  60. data/lib/tap/support/instance_configuration.rb +29 -3
  61. data/lib/tap/support/intern.rb +46 -0
  62. data/lib/tap/support/join.rb +143 -0
  63. data/lib/tap/support/joins/fork.rb +19 -0
  64. data/lib/tap/support/joins/merge.rb +22 -0
  65. data/lib/tap/support/joins/sequence.rb +21 -0
  66. data/lib/tap/support/joins/switch.rb +25 -0
  67. data/lib/tap/support/joins/sync_merge.rb +63 -0
  68. data/lib/tap/support/joins.rb +15 -0
  69. data/lib/tap/support/lazy_attributes.rb +17 -2
  70. data/lib/tap/support/lazydoc/comment.rb +503 -0
  71. data/lib/tap/support/lazydoc/config.rb +17 -0
  72. data/lib/tap/support/lazydoc/definition.rb +36 -0
  73. data/lib/tap/support/lazydoc/document.rb +152 -0
  74. data/lib/tap/support/lazydoc/method.rb +24 -0
  75. data/lib/tap/support/lazydoc.rb +269 -343
  76. data/lib/tap/support/manifest.rb +121 -103
  77. data/lib/tap/support/minimap.rb +90 -0
  78. data/lib/tap/support/node.rb +56 -0
  79. data/lib/tap/support/parser.rb +436 -0
  80. data/lib/tap/support/schema.rb +359 -0
  81. data/lib/tap/support/shell_utils.rb +3 -5
  82. data/lib/tap/support/string_ext.rb +60 -0
  83. data/lib/tap/support/tdoc.rb +7 -2
  84. data/lib/tap/support/templater.rb +30 -16
  85. data/lib/tap/support/validation.rb +77 -8
  86. data/lib/tap/task.rb +431 -143
  87. data/lib/tap/tasks/dump.rb +15 -10
  88. data/lib/tap/tasks/load.rb +112 -0
  89. data/lib/tap/tasks/rake.rb +4 -41
  90. data/lib/tap/test/assertions.rb +38 -0
  91. data/lib/tap/test/env_vars.rb +1 -1
  92. data/lib/tap/test/extensions.rb +79 -0
  93. data/lib/tap/test/file_test.rb +420 -0
  94. data/lib/tap/test/file_test_class.rb +12 -0
  95. data/lib/tap/test/regexp_escape.rb +87 -0
  96. data/lib/tap/test/script_test.rb +46 -0
  97. data/lib/tap/test/script_tester.rb +115 -0
  98. data/lib/tap/test/subset_test.rb +260 -0
  99. data/lib/tap/test/subset_test_class.rb +99 -0
  100. data/lib/tap/test/{tap_methods.rb → tap_test.rb} +45 -43
  101. data/lib/tap/test/utils.rb +231 -0
  102. data/lib/tap/test.rb +53 -26
  103. data/lib/tap.rb +3 -20
  104. metadata +50 -27
  105. data/lib/tap/generator/generators/root/templates/test/tapfile_test.rb +0 -15
  106. data/lib/tap/patches/rake/rake_test_loader.rb +0 -8
  107. data/lib/tap/patches/rake/testtask.rb +0 -57
  108. data/lib/tap/patches/ruby19/backtrace_filter.rb +0 -51
  109. data/lib/tap/patches/ruby19/parsedate.rb +0 -16
  110. data/lib/tap/support/batchable.rb +0 -47
  111. data/lib/tap/support/batchable_class.rb +0 -107
  112. data/lib/tap/support/command_line.rb +0 -98
  113. data/lib/tap/support/comment.rb +0 -270
  114. data/lib/tap/support/constant_utils.rb +0 -127
  115. data/lib/tap/support/declarations.rb +0 -111
  116. data/lib/tap/support/framework.rb +0 -83
  117. data/lib/tap/support/framework_class.rb +0 -180
  118. data/lib/tap/support/run_error.rb +0 -39
  119. data/lib/tap/support/summary.rb +0 -30
  120. data/lib/tap/test/file_methods.rb +0 -377
  121. data/lib/tap/test/script_methods/script_test.rb +0 -98
  122. data/lib/tap/test/script_methods.rb +0 -107
  123. data/lib/tap/test/subset_methods.rb +0 -420
  124. data/lib/tap/workflow.rb +0 -200
@@ -1,420 +0,0 @@
1
- require 'test/unit'
2
- require 'benchmark'
3
- require 'pp'
4
- require 'tap/test/env_vars'
5
-
6
- module Test # :nodoc:
7
- module Unit # :nodoc:
8
- class TestCase
9
- class << self
10
- include Tap::Test::EnvVars
11
-
12
- # Passes conditions to subclass
13
- def inherited(subclass) # :nodoc:
14
- super
15
- subclass_conditions = conditions.inject({}) do |memo, (key, value)|
16
- memo.update(key => (value.dup rescue value))
17
- end
18
- subclass.instance_variable_set(:@conditions, subclass_conditions)
19
- subclass.instance_variable_set(:@run_test_suite, nil)
20
- subclass.instance_variable_set(:@skip_messages, [])
21
-
22
- # subclass_inputs = prompt_inputs.inject({}) do |memo, (key, value)|
23
- # memo.update(key => (value.dup rescue value))
24
- # end
25
- # subclass.instance_variable_set("@prompt_inputs", subclass_inputs)
26
- end
27
-
28
- # Experimental -- The idea is to provide a way to prompt once for inputs
29
- # that get used multiple times in a test. Perhaps create accessors
30
- # automatically?
31
-
32
- # def prompt_inputs
33
- # @prompt_inputs ||= {}
34
- # end
35
-
36
- # def require_inputs(*keys, &block)
37
- # if run_subset?("PROMPT")
38
- # puts "\n#{name} requires inputs -- Enter values or 'skip'."
39
-
40
- # argv = ARGV.dup
41
- # begin
42
- # ARGV.clear
43
- # keys.collect do |key|
44
- # print "#{key}: "
45
- # value = gets.strip
46
- # if value =~ /skip/i
47
- # skip_test "missing inputs"
48
- # break
49
- # end
50
- # prompt_inputs[key] = value
51
- # end
52
- # ensure
53
- # ARGV.clear
54
- # ARGV.concat(argv)
55
- # end
56
- # else
57
- # skip_test "prompt test"
58
- # end
59
- # end
60
-
61
- #
62
- # conditions
63
- #
64
-
65
- # A hash of defined conditions
66
- def conditions
67
- @conditions ||= {}
68
- end
69
-
70
- # Defines a condition block and associated message.
71
- # Raises an error if no condition block is given.
72
- def condition(name, msg=nil, &block)
73
- raise "no condition block given" unless block_given?
74
- conditions[name.to_sym] = [msg, block]
75
- end
76
-
77
- # Returns true if the all blocks for the named conditions return true.
78
- #
79
- # condition(:is_true) { true }
80
- # condition(:is_false) { false }
81
- # satisfied?(:is_true) # => true
82
- # satisfied?(:is_true, :is_false) # => false
83
- #
84
- def satisfied?(*conditions)
85
- unsatisfied_conditions(*conditions).empty?
86
- end
87
-
88
- # Returns an array of the unsatified conditions. Raises
89
- # an error if the named condition has not been defined.
90
- #
91
- # condition(:is_true) { true }
92
- # condition(:is_false) { false }
93
- # unsatisfied_conditions(:is_true, :is_false) # => [:is_false]
94
- #
95
- def unsatisfied_conditions(*conditions)
96
- conditions = self.conditions.keys if conditions.empty?
97
- unsatified = []
98
- conditions.each do |condition|
99
- raise "Unknown condition: #{condition}" unless self.conditions.has_key?(condition)
100
- unsatified << condition unless (self.conditions[condition.to_sym].last.call() ? true : false)
101
- end
102
- unsatified
103
- end
104
-
105
- # Returns true if RUBY_PLATFORM matches one of the specfied
106
- # platforms. Use the prefix 'non_' to specify any plaform
107
- # except the specified platform (ex: 'non_mswin'). Returns
108
- # true if no platforms are specified.
109
- #
110
- # Some common platforms:
111
- # mswin Windows
112
- # darwin Mac
113
- def match_platform?(*platforms)
114
- platforms.each do |platform|
115
- platform.to_s =~ /^(non_)?(.*)/
116
-
117
- non = true if $1
118
- match_platform = !RUBY_PLATFORM.index($2).nil?
119
- return false unless (non && !match_platform) || (!non && match_platform)
120
- end
121
-
122
- true
123
- end
124
-
125
- # Returns true if the subset type or 'ALL' is specified in ENV
126
- def run_subset?(type)
127
- env_true?(type) || env_true?("ALL") ? true : false
128
- end
129
-
130
- #
131
- # Methods for skipping a test suite
132
- #
133
- attr_accessor :run_test_suite
134
-
135
- # Returns run_test_suite, or true if run_test_suite is not set.
136
- def run_test_suite?
137
- run_test_suite.nil? ? true : run_test_suite
138
- end
139
-
140
- # Causes a test suite to be skipped. If a message is given, it will
141
- # print and notify the user the test suite has been skipped.
142
- def skip_test(msg=nil)
143
- self.run_test_suite = false
144
-
145
- # experimental -- perhaps use this so that a test can be skipped
146
- # for multiple reasons?
147
- @skip_messages << msg unless msg.nil?
148
- end
149
-
150
- alias :original_suite :suite
151
-
152
- # Modifies the default suite method to include/exclude tests based on platform.
153
- def suite # :nodoc:
154
- if run_test_suite?
155
- original_suite
156
- else
157
- puts "Skipping #{name}: #{@skip_messages.join(', ')}" unless @skip_messages.empty?
158
- Test::Unit::TestSuite.new(name)
159
- end
160
- end
161
- end
162
- end
163
- end
164
- end
165
-
166
- module Tap
167
- module Test
168
-
169
- # Ideally you always run all of your tests and they all run and pass everywhere. In
170
- # practice it's useful to suppress the execution of some tests -- long running tests,
171
- # tests specific for a given platform, or tests that depend on some condition, such
172
- # as the version of some optional third-party software your code interacts with.
173
- #
174
- # SubsetMethods extends TestCase with methods for defining conditions that can be
175
- # used to conditionally perform some action, or skip a test suite entirely. When
176
- # you include SubsetMethods within a specific TestCase, additional methods are
177
- # made available for filtering tests.
178
- #
179
- # require 'tap/test/subset_methods'
180
- # class Test::Unit::TestCase
181
- # # only true if running on windows
182
- # condition(:windows) { match_platform?('mswin') }
183
- #
184
- # # only true if running on anything but windows
185
- # condition(:non_windows) { match_platform?('non_mswin') }
186
- # end
187
- #
188
- # class WindowsOnlyTest < Test::Unit::TestCase
189
- # skip_test unless satisfied?(:windows)
190
- # end
191
- #
192
- # WindowsOnlyTest will only run on a Windows platform. These conditions can be used
193
- # in specific tests, when only some tests need to be skipped.
194
- #
195
- # class RunOnlyAFewTest < Test::Unit::TestCase
196
- # include SubsetMethods
197
- #
198
- # def test_runs_all_the_time
199
- # assert true
200
- # end
201
- #
202
- # def test_runs_only_if_non_windows_condition_is_true
203
- # condition_test(:non_windows) { assert true }
204
- # end
205
- # end
206
- #
207
- # def test_runs_only_when_ENV_variable_EXTENDED_is_true
208
- # extended_test { assert true }
209
- # end
210
- #
211
- # def test_runs_only_when_ENV_variable_BENCHMARK_is_true
212
- # benchmark_test do |x|
213
- # x.report("init speed") { 10000.times { Object.new } }
214
- # end
215
- # end
216
- #
217
- # def test_runs_only_when_ENV_variable_CUSTOM_is_true
218
- # subset_test('CUSTOM') { assert true }
219
- # end
220
- # end
221
- #
222
- # In the example, the ENV variables EXTENDED, BENCHMARK, and CUSTOM act as flags
223
- # to run specific tests. If you're running your test using Rake, ENV variables
224
- # can be set from the command line like so:
225
- #
226
- # % rake test EXTENDED=true
227
- # % rake test BENCHMARK=true
228
- #
229
- # Since tap can run rake tasks as well, these are equivalent:
230
- #
231
- # % tap run test EXTENDED=true
232
- # % tap run test BENCHMARK=true
233
- #
234
- # In so far as SubsetMethods is concerned, the environment variables are
235
- # case-insensitive. As in the example, additional ENV-variable-dependent
236
- # tests can be defined using the subset_test method. To run all tests that
237
- # get switched using an environment variable, set ALL=true.
238
- #
239
- # # also runs benchmark tests
240
- # % tap run test BenchMark=true
241
- #
242
- # # runs all tests
243
- # % tap run test all=true
244
- #
245
- # === Class Methods
246
- #
247
- # See {Test::Unit::TestCase}[link:classes/Test/Unit/TestCase.html] for documentation of the class methods added by SubsetMethods
248
- module SubsetMethods
249
- include Tap::Test::EnvVars
250
-
251
- def satisfied?(*conditions)
252
- self.class.satisfied?(*conditions)
253
- end
254
-
255
- # Returns true if the subset type or 'ALL' is specified in ENV
256
- def run_subset?(type)
257
- self.class.run_subset?(type)
258
- end
259
-
260
- # Returns true if the input string matches the regexp specified in
261
- # env_var(type). Returns the default value if 'ALL' is specified in
262
- # ENV or type is not specified in ENV.
263
- def match_regexp?(type, str, default=true)
264
- return true if env_true?("ALL")
265
- return default unless env(type)
266
-
267
- str =~ Regexp.new(env(type)) ? true : false
268
- end
269
-
270
- # Platform-specific test. Useful for specifying test that should only
271
- # be run on a subset of platforms. Prints ' ' if the test is not run.
272
- #
273
- # def test_only_on_windows
274
- # platform_test('mswin') { ... }
275
- # end
276
- #
277
- # See TestCase#match_platform? for matching details.
278
- def platform_test(*platforms)
279
- if self.class.match_platform?(*platforms)
280
- yield
281
- else
282
- print ' '
283
- end
284
- end
285
-
286
- # Conditonal test. Only runs if the named conditions are satisfied.
287
- # If no conditons are explicitly set, only runs if all conditions
288
- # are satisfied.
289
- #
290
- # condition(:is_true) { true }
291
- # condition(:is_false) { false }
292
- #
293
- # def test_only_if_true_is_satisfied
294
- # condition_test(:is_true) { # runs }
295
- # end
296
- #
297
- # def test_only_if_all_conditions_are_satisfied
298
- # condition_test { # does not run }
299
- # end
300
- #
301
- # See TestCase#condition for more details.
302
- def condition_test(*conditions)
303
- unsatisfied_conditions = self.class.unsatisfied_conditions(*conditions)
304
- if unsatisfied_conditions.empty?
305
- yield
306
- else
307
- print ' '
308
- end
309
- end
310
-
311
- # Basic method for a subset test. The provided block will run if:
312
- # - The subset type is specified in ENV
313
- # - The subset 'ALL' is specified in ENV
314
- # - The test method name matches the regexp provided in the <TYPE>_TEST ENV variable
315
- #
316
- # Otherwise the block will be skipped and +skip+ will be printed in the test output.
317
- # By default skip is the first letter of +type+.
318
- #
319
- # For example, with these methods:
320
- #
321
- # def test_one
322
- # subset_test('CUSTOM') { assert true }
323
- # end
324
- #
325
- # def test_two
326
- # subset_test('CUSTOM') { assert true }
327
- # end
328
- #
329
- # Condition Tests that get run
330
- # ENV['ALL']=true test_one, test_two
331
- # ENV['CUSTOM']=true test_one, test_two
332
- # ENV['CUSTOM_TEST']=test_ test_one, test_two
333
- # ENV['CUSTOM_TEST']=test_one test_one
334
- # ENV['CUSTOM']=nil no tests get run
335
- #
336
- # If you're running your tests with Rake, ENV variables can be set from the
337
- # command line, so you might use these command line statements:
338
- #
339
- # # all tests
340
- # % rake test all=true
341
- #
342
- # # custom subset tests
343
- # % rake test custom=true
344
- #
345
- # # just test_one
346
- # % rake test custom_test=test_one
347
- #
348
- def subset_test(type, skip=type[0..0].downcase)
349
- type = type.upcase
350
- type_test = "#{type}_TEST"
351
- if run_subset?(type) || env(type_test)
352
- if match_regexp?(type_test, method_name.to_s)
353
- yield
354
- else
355
- print skip
356
- end
357
- else
358
- print skip
359
- end
360
- end
361
-
362
- # Declares a subset_test for the ENV variable 'EXTENDED'.
363
- # Prints 'x' if the test is not run.
364
- #
365
- # def test_some_long_process
366
- # extended_test { ... }
367
- # end
368
- def extended_test(&block)
369
- subset_test("EXTENDED", "x", &block)
370
- end
371
-
372
- # Declares a subset_test for the ENV variable 'BENCHMARK'. If run,
373
- # benchmark_test sets up benchmarking using the Benchmark.bm method
374
- # using the input length and block. Prints 'b' if the test is not run.
375
- #
376
- # include Benchmark
377
- # def test_speed
378
- # benchmark_test(10) {|x| ... }
379
- # end
380
- def benchmark_test(length=10, &block)
381
- subset_test("BENCHMARK") do
382
- puts
383
- puts method_name
384
- Benchmark.bm(length, &block)
385
- end
386
- end
387
-
388
- # Declares a subset_test for the ENV variable 'PROMPT'. When run, prompts
389
- # the user for each input specified in array. Inputs will then be passed
390
- # as a hash to the block. Prints 'p' unless run.
391
- #
392
- # def test_requiring_inputs
393
- # prompt_test(:a, :b, :c) {|a, b, c| ... }
394
- # end
395
- #
396
- # If run, the command line prompt will be like the following:
397
- #
398
- # test_requiring_inputs: Enter values or 'skip'
399
- # a: avalue
400
- # b: bvalue
401
- # c: cvalue
402
- #
403
- # The block recieves ['avalue', 'bvalue', 'cvalue'].
404
- def prompt_test(*keys, &block)
405
- subset_test("PROMPT", "p") do
406
- puts "\n#{method_name} -- Enter values or 'skip'."
407
-
408
- values = keys.collect do |key|
409
- print "#{key}: "
410
- value = gets.strip
411
- flunk "skipped test" if value =~ /skip/i
412
- value
413
- end
414
-
415
- yield(*values)
416
- end
417
- end
418
- end
419
- end
420
- end
data/lib/tap/workflow.rb DELETED
@@ -1,200 +0,0 @@
1
- module Tap
2
-
3
- # Workflow is a specialized type of Task allowing the encapsulation and reuse of
4
- # workflow logic. Workflows are still under construction.
5
- #
6
- # === Workflow Definition
7
- #
8
- # During initialization, Workflow executes the workflow method (by default the
9
- # block provided to Workflow.new) to define the workflow logic. This method
10
- # defines one or more entry_points and zero or more exit points, as well as
11
- # the internal logic for the workflow.
12
- #
13
- # Workflow.new do |w|
14
- # factor = w.config[:factor] || 1
15
- #
16
- # t1 = Task.new {|task, input| input += 1 }
17
- # t2 = Task.new {|task, input| input += 10 }
18
- # t3 = Task.new {|task, input| input *= factor }
19
- #
20
- # w.app.sequence(t1, t2, t3)
21
- # w.entry_point = t1
22
- # w.exit_point = t3
23
- # end
24
- #
25
- # Or equivalently:
26
- #
27
- # class SimpleSequence < Workflow
28
- # config :factor, 1
29
- #
30
- # def workflow
31
- # t1 = Task.new {|task, input| input += 5 }
32
- # t2 = Task.new {|task, input| input += 3 }
33
- # t3 = Task.new {|task, input| input *= factor }
34
- #
35
- # app.sequence(t1, t2, t3)
36
- # self.entry_point = t1
37
- # self.exit_point = t3
38
- # end
39
- # end
40
- #
41
- # To facilitate the specification of entry and exit points, workflow
42
- # can accomodate either single-task assignments or a collection. By
43
- # default both are hashes, but they can be reassigned:
44
- #
45
- # Workflow.new do |w|
46
- # w.entry_point.class # => Hash
47
- # w.exit_point.class # => Hash
48
- # w.entry_point[:main] = Task.new
49
- # end
50
- #
51
- # Workflow.new {|w| w.entry_point = Task.new }
52
- # Workflow.new {|w| w.entry_point = [Task.new, Task.new] }
53
- #
54
- # Access to the group of entry/exit points is standardized to an
55
- # array via the entry_points and exit_points methods.
56
- #
57
- # === Workflow Behavior
58
- #
59
- # The entry points act as an enque batch; when the workflow is enqued, the
60
- # entry points are enqued. The exit points act as an on_complete batch; their
61
- # on_complete blocks are set for workflow.on_complete.
62
- #
63
- # w = SimpleSequence.new
64
- # w.enq(0)
65
- # app.run
66
- # app.results(w.exit_points) # => [8]
67
- #
68
- # The batching of entry and exit points is distinct from workflow.batch itself.
69
- # Workflows can be batched like Tasks, such that all entry points from all
70
- # workflows in a batch are enqued at once.
71
- #
72
- # w1 = SimpleSequence.new nil, :factor => 1
73
- # w2 = w1.initialize_batch_obj nil, :factor => -1
74
- #
75
- # w1.enq(0)
76
- # app.run
77
- # app.results(w1.exit_points, w2.exit_points)) # => [8, -8]
78
- #
79
- class Workflow
80
- include Support::Framework
81
-
82
- class << self
83
- protected
84
-
85
- def define(name, klass=Tap::Task, &block)
86
- instance_var = "@#{name}".to_sym
87
-
88
- define_method(name) do |*args|
89
- raise ArgumentError, "wrong number of arguments (#{args.length} for 1)" if args.length > 1
90
-
91
- instance_name = args[0] || name
92
- instance_variable_set(instance_var, {}) unless instance_variable_defined?(instance_var)
93
- instance_variable_get(instance_var)[instance_name] ||= task(instance_name, klass, &block)
94
- end
95
-
96
- define_method("#{name}=") do |input|
97
- input = {name => input} unless input.kind_of?(Hash)
98
- instance_variable_set(instance_var, input)
99
- end
100
- end
101
- end
102
-
103
- # The entry point for self.
104
- attr_accessor :entry_point
105
-
106
- # The exit point for self.
107
- attr_accessor :exit_point
108
-
109
- # The task block provided during initialization.
110
- attr_reader :task_block
111
-
112
- # Creates a new Task with the specified attributes.
113
- def initialize(config={}, name=nil, app=App.instance, &task_block)
114
- super(config, name, app)
115
- @task_block = (task_block == nil ? default_task_block : task_block)
116
- initialize_workflow
117
- end
118
-
119
- # Initializes a new batch object, running workflow to set the
120
- # instance-specific entry/exit points. Raises an error if
121
- # no entry points are defined.
122
- def initialize_copy(orig)
123
- super
124
- initialize_workflow
125
- end
126
-
127
- def initialize_workflow
128
- @entry_point = {}
129
- @exit_point = {}
130
- workflow
131
- end
132
-
133
- # Returns an array of entry points, determined from entry_point.
134
- def entry_points
135
- case @entry_point
136
- when Hash then @entry_point.values
137
- when Support::Executable then [@entry_point]
138
- when Array then @entry_point
139
- when nil then []
140
- else raise "unable to determine entry points from entry_point: #{@entry_point}"
141
- end
142
- end
143
-
144
- # Returns an array of exit points, determined from exit_point.
145
- def exit_points
146
- case @exit_point
147
- when Hash then @exit_point.values
148
- when Support::Executable then [@exit_point]
149
- when Array then @exit_point
150
- when nil then []
151
- else raise "unable to determine exit points from exit_point: #{@exit_point}"
152
- end
153
- end
154
-
155
- # Enqueues all entry points for self and self.batch to app
156
- # with the inputs. The number of inputs provided should match
157
- # the number of inputs required by all the entry points;
158
- # if the entry points have different input requirements, they
159
- # have to be enqued separately.
160
- def enq(*inputs)
161
- entry_points.each do |task|
162
- app.enq(task, *inputs)
163
- end
164
- end
165
-
166
- batch_function :enq
167
-
168
- # Sets the on_complete_block for all exit points for self and
169
- # self.batch. Use unbatched_on_complete to set the on_complete_block
170
- # for just self.exit_points.
171
- def on_complete(override=false, &block)
172
- exit_points.each do |task|
173
- task.on_complete(override, &block)
174
- end
175
- self
176
- end
177
-
178
- batch_function(:on_complete) {}
179
-
180
- def task(name, klass=Tap::Task, &block)
181
- configs = config[name] || {}
182
- raise ArgumentError, "config '#{name}' is not a hash" unless configs.kind_of?(Hash)
183
- klass.new(configs, name, &block)
184
- end
185
-
186
- # The workflow definition method. By default workflow
187
- # simply calls the task_block. In subclasses, workflow
188
- # should be overridden to provide the workflow definition.
189
- def workflow
190
- task_block.call(self) if task_block
191
- end
192
-
193
- protected
194
-
195
- # Hook to set a default task block. By default, nil.
196
- def default_task_block
197
- nil
198
- end
199
- end
200
- end