tap 0.7.9 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History +28 -0
- data/MIT-LICENSE +1 -1
- data/README +71 -43
- data/Rakefile +81 -64
- data/Tutorial +235 -0
- data/bin/tap +80 -44
- data/lib/tap.rb +41 -12
- data/lib/tap/app.rb +243 -246
- data/lib/tap/file_task.rb +357 -118
- data/lib/tap/generator.rb +88 -29
- data/lib/tap/generator/generators/config/config_generator.rb +4 -2
- data/lib/tap/generator/generators/config/templates/config.erb +1 -2
- data/lib/tap/generator/generators/file_task/file_task_generator.rb +3 -18
- data/lib/tap/generator/generators/file_task/templates/task.erb +22 -15
- data/lib/tap/generator/generators/file_task/templates/test.erb +13 -2
- data/{test/test/inference_methods/test_assert_files_exist/input/input_1.txt → lib/tap/generator/generators/generator/USAGE} +0 -0
- data/lib/tap/generator/generators/generator/generator_generator.rb +21 -0
- data/lib/tap/generator/generators/generator/templates/generator.erb +23 -0
- data/lib/tap/generator/generators/generator/templates/usage.erb +1 -0
- data/{test/test/inference_methods/test_assert_files_exist/input/input_2.txt → lib/tap/generator/generators/package/USAGE} +0 -0
- data/lib/tap/generator/generators/package/package_generator.rb +38 -0
- data/lib/tap/generator/generators/package/templates/package.erb +186 -0
- data/lib/tap/generator/generators/root/root_generator.rb +14 -9
- data/lib/tap/generator/generators/root/templates/Rakefile +20 -14
- data/{test/test/inference_methods/test_infer_glob/expected/file.yml → lib/tap/generator/generators/root/templates/ReadMe.txt} +0 -0
- data/lib/tap/generator/generators/root/templates/tap.yml +82 -0
- data/lib/tap/generator/generators/root/templates/test/tap_test_helper.rb +0 -1
- data/lib/tap/generator/generators/root/templates/test/tap_test_suite.rb +2 -1
- data/{test/test/inference_methods/test_infer_glob/expected/file_1.txt → lib/tap/generator/generators/script/USAGE} +0 -0
- data/lib/tap/generator/generators/script/script_generator.rb +17 -0
- data/lib/tap/generator/generators/script/templates/script.erb +42 -0
- data/lib/tap/generator/generators/task/task_generator.rb +1 -1
- data/lib/tap/generator/generators/task/templates/task.erb +24 -16
- data/lib/tap/generator/generators/task/templates/test.erb +13 -17
- data/lib/tap/generator/generators/workflow/templates/task.erb +10 -10
- data/lib/tap/generator/generators/workflow/templates/test.erb +1 -1
- data/lib/tap/generator/generators/workflow/workflow_generator.rb +3 -18
- data/lib/tap/root.rb +108 -146
- data/lib/tap/script.rb +362 -0
- data/lib/tap/script/console.rb +28 -0
- data/lib/tap/script/destroy.rb +13 -1
- data/lib/tap/script/generate.rb +13 -1
- data/lib/tap/script/run.rb +100 -57
- data/lib/tap/support/batch_queue.rb +0 -3
- data/lib/tap/support/logger.rb +6 -3
- data/lib/tap/support/rake.rb +54 -0
- data/lib/tap/support/task_configuration.rb +169 -0
- data/lib/tap/support/tdoc.rb +198 -0
- data/lib/tap/support/tdoc/config_attr.rb +338 -0
- data/lib/tap/support/tdoc/tdoc_html_generator.rb +38 -0
- data/lib/tap/support/tdoc/tdoc_html_template.rb +42 -0
- data/lib/tap/support/versions.rb +33 -1
- data/lib/tap/task.rb +339 -227
- data/lib/tap/test.rb +86 -128
- data/lib/tap/test/env_vars.rb +16 -5
- data/lib/tap/test/file_methods.rb +373 -0
- data/lib/tap/test/subset_methods.rb +299 -180
- data/lib/tap/version.rb +2 -1
- data/lib/tap/workflow.rb +2 -0
- data/test/app/lib/app_test_task.rb +1 -0
- data/test/app_test.rb +327 -83
- data/test/check/binding_eval.rb +23 -0
- data/test/check/define_method_check.rb +22 -0
- data/test/check/dependencies_check.rb +175 -0
- data/test/check/inheritance_check.rb +22 -0
- data/test/file_task_test.rb +524 -291
- data/test/{test/inference_methods/test_infer_glob/expected/file_2.txt → root/glob/one.txt} +0 -0
- data/test/root/glob/two.txt +0 -0
- data/test/root_test.rb +330 -262
- data/test/script_test.rb +194 -0
- data/test/support/audit_test.rb +5 -2
- data/test/support/combinator_test.rb +10 -10
- data/test/support/rake_test.rb +35 -0
- data/test/support/task_configuration_test.rb +272 -0
- data/test/support/tdoc_test.rb +363 -0
- data/test/support/templater_test.rb +2 -2
- data/test/support/versions_test.rb +32 -0
- data/test/tap_test_helper.rb +39 -0
- data/test/task_base_test.rb +115 -0
- data/test/task_class_test.rb +56 -4
- data/test/task_execute_test.rb +29 -0
- data/test/task_test.rb +89 -70
- data/test/test/env_vars_test.rb +48 -0
- data/test/test/{inference_methods → file_methods}/test_assert_expected/expected/file.txt +0 -0
- data/test/test/{inference_methods → file_methods}/test_assert_expected/expected/folder/file.txt +0 -0
- data/test/test/{inference_methods → file_methods}/test_assert_expected/input/file.txt +0 -0
- data/test/test/{inference_methods → file_methods}/test_assert_expected/input/folder/file.txt +0 -0
- data/test/test/file_methods/test_assert_files_exist/input/input_1.txt +0 -0
- data/test/test/file_methods/test_assert_files_exist/input/input_2.txt +0 -0
- data/test/test/file_methods/test_assert_output_files_equal/expected/one.txt +1 -0
- data/test/test/file_methods/test_assert_output_files_equal/expected/two.txt +1 -0
- data/test/test/file_methods/test_assert_output_files_equal/input/one.txt +1 -0
- data/test/test/file_methods/test_assert_output_files_equal/input/two.txt +1 -0
- data/test/test/{inference_methods → file_methods}/test_file_compare/expected/output_1.txt +0 -0
- data/test/test/{inference_methods → file_methods}/test_file_compare/expected/output_2.txt +0 -0
- data/test/test/{inference_methods → file_methods}/test_file_compare/input/input_1.txt +0 -0
- data/test/test/{inference_methods → file_methods}/test_file_compare/input/input_2.txt +0 -0
- data/test/test/file_methods/test_infer_glob/expected/file.yml +0 -0
- data/test/test/file_methods/test_infer_glob/expected/file_1.txt +0 -0
- data/test/test/file_methods/test_infer_glob/expected/file_2.txt +0 -0
- data/test/test/file_methods/test_method_glob/expected/file.yml +0 -0
- data/test/test/file_methods/test_method_glob/expected/file_1.txt +0 -0
- data/test/test/file_methods/test_method_glob/expected/file_2.txt +0 -0
- data/test/test/{inference_methods → file_methods}/test_yml_compare/expected/output_1.yml +0 -0
- data/test/test/{inference_methods → file_methods}/test_yml_compare/expected/output_2.yml +0 -0
- data/test/test/{inference_methods → file_methods}/test_yml_compare/input/input_1.yml +0 -0
- data/test/test/{inference_methods → file_methods}/test_yml_compare/input/input_2.yml +0 -0
- data/test/test/file_methods_test.rb +204 -0
- data/test/test/subset_methods_test.rb +93 -33
- data/test/test/test_assert_expected_result_files/expected/task/name/a.txt +1 -0
- data/test/test/test_assert_expected_result_files/expected/task/name/b.txt +1 -0
- data/test/test/test_assert_expected_result_files/input/a.txt +1 -0
- data/test/test/test_assert_expected_result_files/input/b.txt +1 -0
- data/test/test/test_file_task_test/expected/one.txt +1 -0
- data/test/test/test_file_task_test/expected/two.txt +1 -0
- data/test/test/test_file_task_test/input/one.txt +1 -0
- data/test/test/test_file_task_test/input/two.txt +1 -0
- data/test/test_test.rb +143 -3
- data/test/workflow_test.rb +2 -0
- data/vendor/rails_generator.rb +56 -0
- data/vendor/rails_generator/base.rb +263 -0
- data/vendor/rails_generator/commands.rb +581 -0
- data/vendor/rails_generator/generated_attribute.rb +42 -0
- data/vendor/rails_generator/lookup.rb +209 -0
- data/vendor/rails_generator/manifest.rb +53 -0
- data/vendor/rails_generator/options.rb +143 -0
- data/vendor/rails_generator/scripts.rb +83 -0
- data/vendor/rails_generator/scripts/destroy.rb +7 -0
- data/vendor/rails_generator/scripts/generate.rb +7 -0
- data/vendor/rails_generator/scripts/update.rb +12 -0
- data/vendor/rails_generator/simple_logger.rb +46 -0
- data/vendor/rails_generator/spec.rb +44 -0
- metadata +180 -196
- data/lib/tap/generator/generators/root/templates/app.yml +0 -19
- data/lib/tap/generator/generators/root/templates/config/process_tap_request.yml +0 -4
- data/lib/tap/generator/generators/root/templates/lib/process_tap_request.rb +0 -26
- data/lib/tap/generator/generators/root/templates/public/images/nav.jpg +0 -0
- data/lib/tap/generator/generators/root/templates/public/stylesheets/color.css +0 -57
- data/lib/tap/generator/generators/root/templates/public/stylesheets/layout.css +0 -108
- data/lib/tap/generator/generators/root/templates/public/stylesheets/normalize.css +0 -40
- data/lib/tap/generator/generators/root/templates/public/stylesheets/typography.css +0 -21
- data/lib/tap/generator/generators/root/templates/server/config/environment.rb +0 -60
- data/lib/tap/generator/generators/root/templates/server/lib/tasks/clear_database_prerequisites.rake +0 -5
- data/lib/tap/generator/generators/root/templates/server/test/test_helper.rb +0 -53
- data/lib/tap/script/server.rb +0 -12
- data/lib/tap/support/rap.rb +0 -38
- data/lib/tap/test/inference_methods.rb +0 -298
- data/test/task/config/task_with_config.yml +0 -1
- data/test/test/inference_methods_test.rb +0 -311
data/lib/tap/task.rb
CHANGED
@@ -1,32 +1,31 @@
|
|
1
|
-
require 'monitor'
|
2
|
-
|
3
1
|
module Tap
|
4
|
-
|
2
|
+
|
5
3
|
# == Overview
|
6
4
|
#
|
7
5
|
# Tasks are the basic executable unit of Tap. When an App executes
|
8
|
-
# a Task, the Task will be passed
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
#
|
13
|
-
# method, and an on_complete_block.
|
6
|
+
# a Task, the Task will be passed inputs which it then processes into
|
7
|
+
# results. Tasks are joined into workflows using condition blocks
|
8
|
+
# defining when a set of inputs are ready for execution, and where to
|
9
|
+
# pass results when the Task completes. Tasks fundamentally consist
|
10
|
+
# of a condition_block, a process method, and an on_complete_block.
|
14
11
|
#
|
15
12
|
# Tasks are configurable. By default each task will be configured
|
16
|
-
# with the class
|
17
|
-
# defined.
|
13
|
+
# with the default class configurations, which can be set when the
|
14
|
+
# class is defined.
|
18
15
|
#
|
19
16
|
# class ConfiguredTask < Tap::Task
|
20
|
-
#
|
17
|
+
# config :one, 'one'
|
18
|
+
# config :two, 'two'
|
21
19
|
# end
|
22
20
|
#
|
23
21
|
# t = ConfiguredTask.new
|
24
22
|
# t.config # => {:one => 'one', :two => 'two'}
|
25
23
|
#
|
26
|
-
# Tasks also have a name (based on the class by default)
|
27
|
-
#
|
28
|
-
#
|
29
|
-
#
|
24
|
+
# Tasks also have a name (based on the class name by default). The
|
25
|
+
# task name is used as a relative filepath from application directories
|
26
|
+
# to assoicated files. During initialization, the task name is used
|
27
|
+
# to lookup configurations from config_file. Additional and overriding
|
28
|
+
# configurations may be provided during initialization.
|
30
29
|
#
|
31
30
|
# t.name # => "configured_task"
|
32
31
|
# t.app[:config] # => "/path/to/app/config"
|
@@ -42,38 +41,47 @@ module Tap
|
|
42
41
|
#
|
43
42
|
# === Batches
|
44
43
|
#
|
45
|
-
# Tasks are designed to facilitate batch processing of inputs.
|
46
|
-
# a task queues inputs to itself,
|
47
|
-
#
|
48
|
-
#
|
49
|
-
#
|
44
|
+
# Tasks are designed to facilitate batch processing of inputs. Each
|
45
|
+
# time a task queues inputs to itself, the inputs are collected into a
|
46
|
+
# batch. Upon execution, all inputs are passed to the task at once.
|
47
|
+
# If the task is iterative (the default), then each of these inputs is
|
48
|
+
# processed individually. Otherwise, all inputs are processed as a single
|
49
|
+
# group:
|
50
50
|
#
|
51
51
|
# runlist = []
|
52
52
|
# t1 = Task.new {|task, input| runlist << input}
|
53
|
-
# t1.
|
54
|
-
# t1.
|
53
|
+
# t1.enq 1
|
54
|
+
# t1.enq 2,3
|
55
55
|
# t1.app.run
|
56
|
-
#
|
57
56
|
# runlist # => [1,2,3]
|
58
57
|
#
|
59
|
-
#
|
60
|
-
#
|
61
|
-
#
|
58
|
+
# runlist = []
|
59
|
+
# t1.iterate = false
|
60
|
+
# t1.enq 1
|
61
|
+
# t1.enq 2,3
|
62
|
+
# t1.app.run
|
63
|
+
# runlist # => [[1,2,3]]
|
64
|
+
#
|
65
|
+
# Additionally, tasks are designed to facilitate processing using a batch
|
66
|
+
# of related tasks. Often these batches consist of the same task class,
|
67
|
+
# but with a variety of configurations, but they can be assembled in other
|
68
|
+
# ways. When a task is queued, each task in the batch will be queued:
|
62
69
|
#
|
63
70
|
# runlist = []
|
71
|
+
# t1 = Task.new {|task, input| runlist << input}
|
64
72
|
# t1.batch # => [t1]
|
65
73
|
# t2 = t1.create_batch_task
|
66
74
|
# t1.batch # => [t1, t2]
|
67
75
|
#
|
68
|
-
# t1.
|
69
|
-
# t2.
|
76
|
+
# t1.enq 1
|
77
|
+
# t2.enq 2,3
|
70
78
|
# t1.app.run
|
71
|
-
#
|
72
79
|
# runlist # => [1,2,3, 1,2,3]
|
73
80
|
#
|
74
|
-
#
|
75
|
-
#
|
76
|
-
# of
|
81
|
+
# Here runlist reflects that t1 and t2 were run in succession with the same [1,2,3]
|
82
|
+
# inputs. Configuration batches are automatically generated when task.config_file
|
83
|
+
# specifies an array of configurations; each configuration is translated into a
|
84
|
+
# batched task.
|
77
85
|
#
|
78
86
|
# # [/path/to/app/config/batch.yml]
|
79
87
|
# # - one: ONE
|
@@ -91,11 +99,10 @@ module Tap
|
|
91
99
|
# t2.config_template # => {:one => 'ANOTHER ONE'}
|
92
100
|
# t2.config # => {:one => 'ANOTHER ONE', :two => 'two'}
|
93
101
|
#
|
94
|
-
#
|
95
|
-
#
|
96
|
-
#
|
97
|
-
#
|
98
|
-
# array of templates.
|
102
|
+
# Task processes configuration files to make the definition of configuration
|
103
|
+
# templates more DRY and powerful (see Tap::Support::Templater for more
|
104
|
+
# details). Here the default ConfiguredTask configurations are overridden
|
105
|
+
# by variations created by the variations! tag:
|
99
106
|
#
|
100
107
|
# # [/path/to/app/config/template.yml]
|
101
108
|
# # one: ONE
|
@@ -113,47 +120,63 @@ module Tap
|
|
113
120
|
# All together, these features make it easy to process a batch of inputs
|
114
121
|
# using a batch of configurations -- all encapsulated in a workflow-ready
|
115
122
|
# architecture.
|
123
|
+
#
|
124
|
+
# == Non-Task Tasks!
|
125
|
+
#
|
126
|
+
# The methods definining the essential behavior of a Task are encapsulated in
|
127
|
+
# the Tap::Task::Base module. Using this module, Non-Task classes can be defined,
|
128
|
+
# and Non-Task objects extended to behave like Tasks; ie they can be enqued, run,
|
129
|
+
# and incorporated into workflows.
|
130
|
+
#
|
131
|
+
# See Tap::Task::Base for more details (esp for objects with a process method),
|
132
|
+
# as well as Tap::Support::Rake, which makes Rake tasks behave like Tap tasks.
|
133
|
+
#
|
116
134
|
class Task < Monitor
|
117
|
-
|
118
|
-
|
135
|
+
write_inheritable_attribute(:configurations, Support::TaskConfiguration.new)
|
136
|
+
class_inheritable_reader(:configurations)
|
119
137
|
|
120
138
|
write_inheritable_attribute(:iterative, true)
|
121
139
|
class_inheritable_reader(:iterative)
|
122
|
-
|
140
|
+
|
141
|
+
write_inheritable_attribute(:source_files, [])
|
142
|
+
class_inheritable_reader(:source_files)
|
143
|
+
|
123
144
|
class << self
|
124
|
-
|
125
|
-
#
|
126
|
-
#
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
#
|
132
|
-
# :key => 'value'},
|
133
|
-
# :make_accessors => true)
|
134
|
-
# end
|
135
|
-
#
|
136
|
-
# t = SomeTask.new
|
137
|
-
# t.key # => 'value'
|
138
|
-
# t.key = 'another'
|
139
|
-
# t.config # => {:key => 'another'}
|
145
|
+
|
146
|
+
# Currently an experimental way of identifying source files for TDoc
|
147
|
+
# documentation.
|
148
|
+
def source_file(arg) # :nodoc:
|
149
|
+
source_files << arg
|
150
|
+
end
|
151
|
+
|
152
|
+
# Declares a configuration without any accessors.
|
140
153
|
#
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
154
|
+
# With no keys specified, sets config to make no accessors
|
155
|
+
# for each new configuration.
|
156
|
+
def declare_config(*keys)
|
157
|
+
if keys.empty?
|
158
|
+
self.config_mode = :none
|
159
|
+
else
|
160
|
+
keys.each do |key|
|
161
|
+
configurations.declare(key, self)
|
162
|
+
end
|
163
|
+
end
|
148
164
|
end
|
149
165
|
|
150
166
|
# Creates a configuration writer for the input keys. Works like
|
151
167
|
# attr_writer, except the value is written to config, rather than
|
152
|
-
# a local variable.
|
168
|
+
# a local variable. In addition, the config will be validated
|
169
|
+
# using validate_config upon setting the value.
|
170
|
+
#
|
171
|
+
# With no keys specified, sets config to create config_writer
|
172
|
+
# for each new configuration.
|
153
173
|
def config_writer(*keys)
|
154
|
-
keys.
|
155
|
-
|
156
|
-
|
174
|
+
if keys.empty?
|
175
|
+
self.config_mode = :config_writer
|
176
|
+
else
|
177
|
+
keys.each do |key|
|
178
|
+
configurations.declare(key, self)
|
179
|
+
define_config_writer(key)
|
157
180
|
end
|
158
181
|
end
|
159
182
|
end
|
@@ -161,10 +184,16 @@ module Tap
|
|
161
184
|
# Creates a configuration reader for the input keys. Works like
|
162
185
|
# attr_reader, except the value is read from config, rather than
|
163
186
|
# a local variable.
|
187
|
+
#
|
188
|
+
# With no keys specified, sets config to create a config_reader
|
189
|
+
# for each new configuration.
|
164
190
|
def config_reader(*keys)
|
165
|
-
keys.
|
166
|
-
|
167
|
-
|
191
|
+
if keys.empty?
|
192
|
+
self.config_mode = :config_reader
|
193
|
+
else
|
194
|
+
keys.each do |key|
|
195
|
+
configurations.declare(key, self)
|
196
|
+
define_config_reader(key)
|
168
197
|
end
|
169
198
|
end
|
170
199
|
end
|
@@ -172,27 +201,248 @@ module Tap
|
|
172
201
|
# Creates configuration accessors for the input keys. Works like
|
173
202
|
# attr_accessor, except the value is read from and written to config,
|
174
203
|
# rather than a local variable.
|
204
|
+
#
|
205
|
+
# With no keys specified, sets config to create a config_accessor
|
206
|
+
# for each new configuration.
|
175
207
|
def config_accessor(*keys)
|
176
|
-
|
177
|
-
|
208
|
+
if keys.empty?
|
209
|
+
self.config_mode = :config_accessor
|
210
|
+
else
|
211
|
+
keys.each do |key|
|
212
|
+
configurations.declare(key, self)
|
213
|
+
define_config_reader(key)
|
214
|
+
define_config_writer(key)
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
# Sets a class configuration. Configurations are inherited, but can
|
220
|
+
# be overridden or added in subclasses. Accessors are created by
|
221
|
+
# default, but this behavior can be modified by use of the other
|
222
|
+
# config methods.
|
223
|
+
#
|
224
|
+
# class SampleTask < Tap::Task
|
225
|
+
# config :key, 'value'
|
226
|
+
#
|
227
|
+
# config_reader
|
228
|
+
# config :reader_only
|
229
|
+
# end
|
230
|
+
#
|
231
|
+
# t = SampleTask.new
|
232
|
+
# t.respond_to?(:reader_only) # => true
|
233
|
+
# t.respond_to?(:reader_only=) # => false
|
234
|
+
#
|
235
|
+
# t.config # => {:key => 'value', :reader_only => nil}
|
236
|
+
# t.key # => 'value'
|
237
|
+
# t.key = 'another'
|
238
|
+
# t.config # => {:key => 'another', :reader_only => nil}
|
239
|
+
def config(key, value=nil, attributes={})
|
240
|
+
declare_config(key)
|
241
|
+
configurations.set(key, value, attributes)
|
242
|
+
|
243
|
+
case config_mode
|
244
|
+
when nil, :config_accessor then config_accessor(key)
|
245
|
+
when :config_writer then config_writer(key)
|
246
|
+
when :config_reader then config_reader(key)
|
247
|
+
end
|
178
248
|
end
|
179
249
|
|
180
250
|
# Sets the default iteration behavior for a task class
|
181
|
-
# to iterate inputs during task execution.
|
251
|
+
# to iterate inputs during task execution. As a result,
|
252
|
+
# tasks will NOT execute without inputs.
|
182
253
|
def iterate
|
183
254
|
self.write_inheritable_attribute(:iterative, true)
|
184
255
|
end
|
185
256
|
|
186
257
|
# Sets the default iteration behavior for a task class
|
187
|
-
# to not iterate inputs during task execution.
|
258
|
+
# to not iterate inputs during task execution. As a result,
|
259
|
+
# tasks can execute without inputs.
|
188
260
|
def do_not_iterate
|
189
261
|
self.write_inheritable_attribute(:iterative, false)
|
190
262
|
end
|
263
|
+
|
264
|
+
protected
|
265
|
+
|
266
|
+
attr_accessor :config_mode
|
267
|
+
|
268
|
+
private
|
269
|
+
|
270
|
+
def define_config_reader(key) # :nodoc:
|
271
|
+
define_method(key) do
|
272
|
+
config[key]
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
def define_config_writer(key) # :nodoc:
|
277
|
+
define_method("#{key}=") do |value|
|
278
|
+
config[key] = value
|
279
|
+
validate_config(key, value)
|
280
|
+
value
|
281
|
+
end
|
282
|
+
end
|
191
283
|
end
|
192
|
-
|
193
|
-
attr_reader :name, :config, :config_template, :app, :batch, :condition_block, :task_block, :on_complete_block, :results
|
194
|
-
attr_accessor :multithread, :iterate
|
195
284
|
|
285
|
+
# Tap::Task::Base encapsulates the methods definining the essential
|
286
|
+
# behavior of a Task.
|
287
|
+
module Base
|
288
|
+
attr_reader :app, :batch, :condition_block, :on_complete_block, :results
|
289
|
+
attr_accessor :multithread, :iterate
|
290
|
+
|
291
|
+
# Creates a new Task with the specified attributes.
|
292
|
+
def self.extended(base)
|
293
|
+
base.extend MonitorMixin
|
294
|
+
|
295
|
+
base.instance_variable_set("@app", App.instance) if base.app.nil?
|
296
|
+
base.instance_variable_set("@batch", [base]) if base.batch.nil?
|
297
|
+
base.instance_variable_set("@results", []) if base.results.nil?
|
298
|
+
base.instance_variable_set("@condition_block", nil) if base.condition_block.nil?
|
299
|
+
base.instance_variable_set("@on_complete_block", nil) if base.on_complete_block.nil?
|
300
|
+
base.instance_variable_set("@multithread", false) if base.multithread.nil?
|
301
|
+
base.instance_variable_set("@iterate", false) if base.iterate.nil?
|
302
|
+
end
|
303
|
+
|
304
|
+
# Returns true if the batch size is greater than one
|
305
|
+
# (the one being the current task itself).
|
306
|
+
def batched?
|
307
|
+
batch.length > 1
|
308
|
+
end
|
309
|
+
|
310
|
+
# Returns the index of the current task in batch.
|
311
|
+
def batch_index
|
312
|
+
batch.index(self)
|
313
|
+
end
|
314
|
+
|
315
|
+
# Returns true if multithread is true.
|
316
|
+
#
|
317
|
+
# (NOTE -- multithreading is currently experimental!)
|
318
|
+
def multithread?
|
319
|
+
multithread
|
320
|
+
end
|
321
|
+
|
322
|
+
# Returns true if iterate is true. If iterate has not been set for the task,
|
323
|
+
# then iterate? returns the value of the class iterative variable.
|
324
|
+
def iterate?
|
325
|
+
iterate
|
326
|
+
end
|
327
|
+
|
328
|
+
# Sets a condition block for the task. Raises an error if condition_block is
|
329
|
+
# already set, unless override = true.
|
330
|
+
#
|
331
|
+
# Note that the block will recieve the audited_inputs of the task
|
332
|
+
# (see Audit for more information).
|
333
|
+
def condition(override=false, &block) # :yields: self, audited_inputs
|
334
|
+
raise "Condition for task already set: #{self}" unless condition_block.nil? || override
|
335
|
+
self.condition_block = block
|
336
|
+
end
|
337
|
+
|
338
|
+
# Sets a block to execute when the task completes execution. Raises an error
|
339
|
+
# if on_complete_block is already set, unless override = true.
|
340
|
+
#
|
341
|
+
# Note that the block will recieve the results of the task, ie an array of
|
342
|
+
# audited values and not values themselves (see Audit for more information).
|
343
|
+
def on_complete(override=false, &block) # :yields: results
|
344
|
+
raise "On complete for task already set: #{self}" unless on_complete_block.nil? || override
|
345
|
+
self.on_complete_block = block
|
346
|
+
end
|
347
|
+
|
348
|
+
# Returns true if no condition block is specified, or the condition
|
349
|
+
# block evaluates to true with the given inputs.
|
350
|
+
def executable?(inputs)
|
351
|
+
condition_block ? condition_block.call(self, inputs) : true
|
352
|
+
end
|
353
|
+
|
354
|
+
# Execute the actions associated with this task. Returns an array of Audits;
|
355
|
+
# one for each input value. Raises an error if the task is not executable with
|
356
|
+
# the inputs, and executes the on_complete block when finished.
|
357
|
+
#
|
358
|
+
# Execute is synchronized such that a task can only execute on one thread at a
|
359
|
+
# time.
|
360
|
+
def execute(*inputs)
|
361
|
+
synchronize do
|
362
|
+
check_terminate
|
363
|
+
|
364
|
+
audited_inputs = if iterate?
|
365
|
+
Support::Audit.register(*inputs)
|
366
|
+
else
|
367
|
+
Support::Audit.merge(*inputs)
|
368
|
+
end
|
369
|
+
|
370
|
+
self.results = nil
|
371
|
+
raise "Condition not met." unless executable?(audited_inputs)
|
372
|
+
|
373
|
+
before_execute
|
374
|
+
begin
|
375
|
+
self.results = if iterate?
|
376
|
+
audited_inputs.collect do |audited_input|
|
377
|
+
on_execute(audited_input)
|
378
|
+
end
|
379
|
+
else
|
380
|
+
[on_execute(audited_inputs)]
|
381
|
+
end
|
382
|
+
rescue
|
383
|
+
on_execute_error($!)
|
384
|
+
end
|
385
|
+
after_execute
|
386
|
+
|
387
|
+
on_complete_block.call(results) if on_complete_block
|
388
|
+
results
|
389
|
+
end
|
390
|
+
end
|
391
|
+
|
392
|
+
# Raises a TerminateError if the application is in state
|
393
|
+
# TERMINATE, as a mechanism to gracefully terminate threads.
|
394
|
+
#--
|
395
|
+
# Only occurs if the task is multithreaded.
|
396
|
+
#++
|
397
|
+
#
|
398
|
+
# check_terminate is called before each execution, but can
|
399
|
+
# be manually called at any time to provide a breakpoint
|
400
|
+
# in long executions.
|
401
|
+
def check_terminate
|
402
|
+
#if multithread? && app.state == App::State::TERMINATE
|
403
|
+
if app.state == App::State::TERMINATE
|
404
|
+
raise App::TerminateError.new
|
405
|
+
end
|
406
|
+
end
|
407
|
+
|
408
|
+
protected
|
409
|
+
|
410
|
+
attr_writer :app, :batch, :condition_block, :on_complete_block, :results
|
411
|
+
|
412
|
+
# Hook to execute code before inputs are processed.
|
413
|
+
def before_execute() end
|
414
|
+
|
415
|
+
# Hook for overridding the execution code that generates the
|
416
|
+
# task results. The input will be an audited version of the
|
417
|
+
# inputs array provided to execute, or, if iterate? is true,
|
418
|
+
# an audited version of each individual input provided to
|
419
|
+
# execute. on_execute should return the audited_inputs.
|
420
|
+
#
|
421
|
+
# By default, on_execute sends the current value of the audited
|
422
|
+
# input process and then records the output with self as the source.
|
423
|
+
#
|
424
|
+
# It is not advised to override the default on_execute unless
|
425
|
+
# absolutely necessary. Consider adjusting the behavior of
|
426
|
+
# process first.
|
427
|
+
def on_execute(audited_inputs)
|
428
|
+
output = process(audited_inputs._current)
|
429
|
+
audited_inputs._record(self, output)
|
430
|
+
end
|
431
|
+
|
432
|
+
# Hook to execute code after inputs are processed, but before the on_complete block.
|
433
|
+
def after_execute() end
|
434
|
+
|
435
|
+
# Hook to handle unhandled errors from processing inputs on a task level.
|
436
|
+
# By default on_execute_error simply re-raises the unhandled error.
|
437
|
+
def on_execute_error(err)
|
438
|
+
raise err
|
439
|
+
end
|
440
|
+
end
|
441
|
+
|
442
|
+
include Base
|
443
|
+
|
444
|
+
attr_reader :name, :config, :config_template, :task_block
|
445
|
+
|
196
446
|
# Creates a new Task with the specified attributes.
|
197
447
|
def initialize(name=nil, config=nil, app=App.instance, &task_block)
|
198
448
|
super() # required for Monitor
|
@@ -237,28 +487,6 @@ module Tap
|
|
237
487
|
task
|
238
488
|
end
|
239
489
|
|
240
|
-
# Returns true if the batch size is greater than one
|
241
|
-
# (the one being the current task itself).
|
242
|
-
def batched?
|
243
|
-
batch.length > 1
|
244
|
-
end
|
245
|
-
|
246
|
-
# Returns the index of the current task in batch.
|
247
|
-
def batch_index
|
248
|
-
batch.index(self)
|
249
|
-
end
|
250
|
-
|
251
|
-
# Returns true if multithread is true.
|
252
|
-
def multithread?
|
253
|
-
multithread
|
254
|
-
end
|
255
|
-
|
256
|
-
# Returns true if iterate is true. If iterate has not been set for the task,
|
257
|
-
# then iterate? returns the value of the class iterative variable.
|
258
|
-
def iterate?
|
259
|
-
iterate
|
260
|
-
end
|
261
|
-
|
262
490
|
# Returns the path to the configuration file used to make the task configuration
|
263
491
|
# templates. By default this is the YAML file for the task name relative to the
|
264
492
|
# application config directory:
|
@@ -275,77 +503,26 @@ module Tap
|
|
275
503
|
end
|
276
504
|
|
277
505
|
# Configures the task with the given configuration overrides. A Task has three
|
278
|
-
# configuration sources: the class
|
279
|
-
# config_file, and the inputs to this method. Configurations from these
|
506
|
+
# configuration sources: the default class configurations, the config_template
|
507
|
+
# loaded from config_file, and the inputs to this method. Configurations from these
|
280
508
|
# sources are merged as: default_config.merge(config_template).merge(overrides)
|
281
509
|
#
|
282
510
|
# Configurations are symbolized before they are merged. If overrides is nil, then
|
283
|
-
# they will be treated as an empty hash.
|
511
|
+
# they will be treated as an empty hash. All configurations are validated using
|
512
|
+
# validate_config.
|
284
513
|
def config=(overrides)
|
285
514
|
overrides = overrides.nil? ? {} : overrides.symbolize_keys
|
286
|
-
@config = self.class.
|
287
|
-
|
515
|
+
@config = self.class.configurations.default.merge(config_template).merge(overrides)
|
516
|
+
self.config.each_pair {|key, value| validate_config(key, value) }
|
288
517
|
self.config
|
289
518
|
end
|
290
519
|
|
291
|
-
# Sets a condition block for the task. Raises an error if condition_block is
|
292
|
-
# already set, unless override = true.
|
293
|
-
def condition(override=false, &block) # :yields: self, inputs
|
294
|
-
raise "Condition for task already set: #{name}" unless condition_block.nil? || override
|
295
|
-
self.condition_block = block
|
296
|
-
end
|
297
|
-
|
298
|
-
# Sets a block to execute when the task completes execution. Raises an error
|
299
|
-
# if on_complete_block is already set, unless override = true.
|
300
|
-
def on_complete(override=false, &block) # :yields: results
|
301
|
-
raise "On complete for task already set: #{name}" unless on_complete_block.nil? || override
|
302
|
-
self.on_complete_block = block
|
303
|
-
end
|
304
|
-
|
305
|
-
# Returns true if no condition block is specified, or the condition
|
306
|
-
# block evaluates to true with the given inputs.
|
307
|
-
def executable?(inputs)
|
308
|
-
condition_block ? condition_block.call(self, inputs) : true
|
309
|
-
end
|
310
|
-
|
311
|
-
# Execute the actions associated with this task. Returns an array of Audits;
|
312
|
-
# one for each input value. Raises an error if the task is not executable with
|
313
|
-
# the inputs, and executes the on_complete block when finished.
|
314
|
-
#
|
315
|
-
# Execute is synchronized such that a task can only execute on one thread at a
|
316
|
-
# time.
|
317
|
-
def execute(*inputs)
|
318
|
-
synchronize do
|
319
|
-
self.results = nil
|
320
|
-
raise "Condition not met." unless executable?(inputs)
|
321
|
-
|
322
|
-
inputs = case
|
323
|
-
when inputs.empty? then inputs
|
324
|
-
when iterate? then Support::Audit.register(*inputs)
|
325
|
-
else [Support::Audit.merge(*inputs)]
|
326
|
-
end
|
327
|
-
|
328
|
-
before_execute
|
329
|
-
begin
|
330
|
-
self.results = inputs.each do |input|
|
331
|
-
output = process( input._current )
|
332
|
-
input._record(self, output)
|
333
|
-
end
|
334
|
-
rescue
|
335
|
-
on_execute_error($!)
|
336
|
-
end
|
337
|
-
after_execute
|
338
|
-
|
339
|
-
on_complete_block.call(results) if on_complete_block
|
340
|
-
results
|
341
|
-
end
|
342
|
-
end
|
343
|
-
|
344
520
|
# The method for processing inputs into outputs. Override this method in
|
345
521
|
# subclasses to provide class-specific process logic. By default the
|
346
522
|
# input is passed to the task_block (provided during initialization).
|
347
523
|
# Simply returns the input if no task_block is set.
|
348
524
|
def process(input)
|
525
|
+
check_terminate
|
349
526
|
task_block.nil? ? input : task_block.call(self, input)
|
350
527
|
end
|
351
528
|
|
@@ -362,20 +539,12 @@ module Tap
|
|
362
539
|
|
363
540
|
protected
|
364
541
|
|
365
|
-
attr_writer :name, :config_template, :
|
366
|
-
|
367
|
-
# Hook to execute code before inputs are processed.
|
368
|
-
def before_execute() end
|
542
|
+
attr_writer :name, :config_template, :task_block
|
369
543
|
|
370
|
-
# Hook to
|
371
|
-
def
|
372
|
-
|
373
|
-
# Hook to handle unhandled errors from processing inputs on a task level.
|
374
|
-
# By default on_execute_error simply re-raises the unhandled error.
|
375
|
-
def on_execute_error(err)
|
376
|
-
raise err
|
544
|
+
# Hook to validate configurations.
|
545
|
+
def validate_config(key, value)
|
377
546
|
end
|
378
|
-
|
547
|
+
|
379
548
|
# Hook to provide class-specific processing of the config_file template
|
380
549
|
# (ie app.config_template(config_file)). By default the template is
|
381
550
|
# processed using Support::Templater.make_templates.
|
@@ -388,61 +557,4 @@ module Tap
|
|
388
557
|
end
|
389
558
|
|
390
559
|
end
|
391
|
-
end
|
392
|
-
|
393
|
-
class Hold # :nodoc:
|
394
|
-
attr_accessor :help_doc
|
395
|
-
|
396
|
-
def register_doc(*section_headers)
|
397
|
-
section_headers << "Command Line Usage" if section_headers.empty?
|
398
|
-
self.help_doc = [caller.first.sub(/:\d+$/, ''), section_headers.collect {|section| section.strip}]
|
399
|
-
end
|
400
|
-
|
401
|
-
def help
|
402
|
-
return "No help available for #{self}" unless help_doc
|
403
|
-
|
404
|
-
lines = []
|
405
|
-
help_file, sections = help_doc
|
406
|
-
File.open(help_file) do |file|
|
407
|
-
in_section = nil
|
408
|
-
while line = file.gets
|
409
|
-
next unless line =~ /^#(.*)/
|
410
|
-
line = $1.strip
|
411
|
-
|
412
|
-
if line =~ /^=+(.*)/
|
413
|
-
line = $1.strip
|
414
|
-
in_section = sections.delete(line)
|
415
|
-
line += ":"
|
416
|
-
else
|
417
|
-
line = " " + line
|
418
|
-
end
|
419
|
-
|
420
|
-
lines << line if in_section
|
421
|
-
end
|
422
|
-
end
|
423
|
-
|
424
|
-
return lines.join("\n")
|
425
|
-
end
|
426
|
-
|
427
|
-
# Queues self to app with the input.
|
428
|
-
#def <<(input)
|
429
|
-
#queue(input)
|
430
|
-
#end
|
431
|
-
|
432
|
-
# Ticks the monitor by printing '.', or the specified message.
|
433
|
-
# Only ticks if called from within a monitor block.
|
434
|
-
#def tick_monitor(action=nil, msg="", level=Logger::INFO)
|
435
|
-
# app.tick_monitor(action, msg, level)
|
436
|
-
#end
|
437
|
-
|
438
|
-
# Prints the hash of statistics if called within a monitor block.
|
439
|
-
#def monitor_stats(hash)
|
440
|
-
# app.monitor_stats(hash)
|
441
|
-
#end
|
442
|
-
|
443
|
-
# Times the execution of the block and enables the various monitor
|
444
|
-
# methods (tick_monitor, monitor_stats).
|
445
|
-
# def monitor(action="beginning", msg="", &block)
|
446
|
-
# app.monitor(action,msg, &block)
|
447
|
-
#end
|
448
560
|
end
|