tap 0.9.1 → 0.10.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.
- data/History +37 -30
- data/MIT-LICENSE +1 -1
- data/README +92 -44
- data/bin/tap +62 -75
- data/cmd/console.rb +42 -0
- data/cmd/destroy.rb +16 -0
- data/cmd/generate.rb +16 -0
- data/cmd/run.rb +126 -0
- data/doc/Class Reference +362 -0
- data/doc/Command Reference +153 -0
- data/doc/Tutorial +237 -0
- data/lib/tap.rb +6 -45
- data/lib/tap/app.rb +126 -500
- data/lib/tap/constants.rb +2 -29
- data/lib/tap/env.rb +555 -250
- data/lib/tap/file_task.rb +60 -103
- data/lib/tap/generator/base.rb +109 -0
- data/lib/tap/generator/destroy.rb +37 -0
- data/lib/tap/generator/generate.rb +61 -0
- data/lib/tap/generator/generators/command/command_generator.rb +16 -12
- data/lib/tap/generator/generators/command/templates/command.erb +13 -19
- data/lib/tap/generator/generators/config/config_generator.rb +18 -27
- data/lib/tap/generator/generators/config/templates/doc.erb +12 -0
- data/lib/tap/generator/generators/config/templates/nodoc.erb +8 -0
- data/lib/tap/generator/generators/file_task/file_task_generator.rb +16 -11
- data/lib/tap/generator/generators/file_task/templates/file.txt +11 -2
- data/lib/tap/generator/generators/file_task/templates/result.yml +6 -0
- data/lib/tap/generator/generators/file_task/templates/task.erb +24 -31
- data/lib/tap/generator/generators/file_task/templates/test.erb +18 -22
- data/lib/tap/generator/generators/root/root_generator.rb +45 -31
- data/lib/tap/generator/generators/root/templates/Rakefile +64 -41
- data/lib/tap/generator/generators/root/templates/gemspec +27 -0
- data/lib/tap/generator/generators/root/templates/tapfile +8 -0
- data/lib/tap/generator/generators/root/templates/test/tap_test_helper.rb +0 -0
- data/lib/tap/generator/generators/root/templates/test/tap_test_suite.rb +1 -1
- data/lib/tap/generator/generators/root/templates/test/tapfile_test.rb +15 -0
- data/lib/tap/generator/generators/task/task_generator.rb +21 -28
- data/lib/tap/generator/generators/task/templates/task.erb +13 -23
- data/lib/tap/generator/generators/task/templates/test.erb +15 -18
- data/lib/tap/generator/manifest.rb +14 -0
- data/lib/tap/patches/rake/rake_test_loader.rb +0 -0
- data/lib/tap/patches/rake/testtask.rb +0 -0
- data/lib/tap/patches/ruby19/backtrace_filter.rb +0 -0
- data/lib/tap/patches/ruby19/parsedate.rb +0 -0
- data/lib/tap/root.rb +260 -21
- data/lib/tap/support/aggregator.rb +11 -11
- data/lib/tap/support/assignments.rb +172 -0
- data/lib/tap/support/audit.rb +20 -18
- data/lib/tap/support/batchable.rb +21 -10
- data/lib/tap/support/batchable_class.rb +107 -0
- data/lib/tap/support/class_configuration.rb +154 -239
- data/lib/tap/support/command_line.rb +97 -102
- data/lib/tap/support/comment.rb +270 -0
- data/lib/tap/support/configurable.rb +86 -65
- data/lib/tap/support/configurable_class.rb +296 -0
- data/lib/tap/support/configuration.rb +122 -0
- data/lib/tap/support/constant.rb +70 -0
- data/lib/tap/support/constant_utils.rb +127 -0
- data/lib/tap/support/declarations.rb +111 -0
- data/lib/tap/support/executable.rb +30 -17
- data/lib/tap/support/executable_queue.rb +0 -0
- data/lib/tap/support/framework.rb +71 -0
- data/lib/tap/support/framework_class.rb +199 -0
- data/lib/tap/support/instance_configuration.rb +147 -0
- data/lib/tap/support/lazydoc.rb +428 -0
- data/lib/tap/support/manifest.rb +89 -0
- data/lib/tap/support/run_error.rb +0 -0
- data/lib/tap/support/shell_utils.rb +33 -9
- data/lib/tap/support/summary.rb +30 -0
- data/lib/tap/support/tdoc.rb +339 -134
- data/lib/tap/support/tdoc/tdoc_html_generator.rb +0 -0
- data/lib/tap/support/tdoc/tdoc_html_template.rb +0 -0
- data/lib/tap/support/templater.rb +180 -0
- data/lib/tap/support/validation.rb +409 -76
- data/lib/tap/support/versions.rb +5 -3
- data/lib/tap/task.rb +78 -174
- data/lib/tap/tasks/dump.rb +56 -0
- data/lib/tap/tasks/rake.rb +93 -0
- data/lib/tap/test.rb +3 -3
- data/lib/tap/test/env_vars.rb +2 -2
- data/lib/tap/test/file_methods.rb +19 -20
- data/lib/tap/test/script_methods.rb +144 -0
- data/lib/tap/test/subset_methods.rb +1 -1
- data/lib/tap/test/tap_methods.rb +28 -62
- data/lib/tap/workflow.rb +22 -39
- metadata +48 -179
- data/Basic Overview +0 -151
- data/Command Reference +0 -99
- data/Rakefile +0 -127
- data/Tutorial +0 -287
- data/lib/tap/cmd/console.rb +0 -31
- data/lib/tap/cmd/destroy.rb +0 -20
- data/lib/tap/cmd/generate.rb +0 -20
- data/lib/tap/cmd/run.rb +0 -151
- data/lib/tap/dump.rb +0 -57
- data/lib/tap/generator.rb +0 -91
- data/lib/tap/generator/generators/command/USAGE +0 -6
- data/lib/tap/generator/generators/config/USAGE +0 -21
- data/lib/tap/generator/generators/config/templates/config.erb +0 -1
- data/lib/tap/generator/generators/file_task/USAGE +0 -3
- data/lib/tap/generator/generators/file_task/templates/file.yml +0 -3
- data/lib/tap/generator/generators/generator/USAGE +0 -0
- data/lib/tap/generator/generators/generator/generator_generator.rb +0 -21
- data/lib/tap/generator/generators/generator/templates/generator.erb +0 -32
- data/lib/tap/generator/generators/generator/templates/usage.erb +0 -1
- data/lib/tap/generator/generators/root/USAGE +0 -0
- data/lib/tap/generator/generators/root/templates/ReadMe.txt +0 -0
- data/lib/tap/generator/generators/root/templates/tap.yml +0 -80
- data/lib/tap/generator/generators/task/USAGE +0 -3
- data/lib/tap/generator/generators/workflow/USAGE +0 -0
- data/lib/tap/generator/generators/workflow/templates/task.erb +0 -16
- data/lib/tap/generator/generators/workflow/templates/test.erb +0 -7
- data/lib/tap/generator/generators/workflow/workflow_generator.rb +0 -6
- data/lib/tap/generator/options.rb +0 -26
- data/lib/tap/generator/usage.rb +0 -26
- data/lib/tap/support/batchable_methods.rb +0 -34
- data/lib/tap/support/command_line_methods.rb +0 -76
- data/lib/tap/support/configurable_methods.rb +0 -224
- data/lib/tap/support/logger.rb +0 -88
- data/lib/tap/support/rake.rb +0 -43
- data/lib/tap/support/tdoc/config_attr.rb +0 -362
- data/test/app/config/another/task.yml +0 -1
- data/test/app/config/batch.yml +0 -2
- data/test/app/config/empty.yml +0 -0
- data/test/app/config/erb.yml +0 -2
- data/test/app/config/some/task.yml +0 -1
- data/test/app/config/template.yml +0 -2
- data/test/app/config/version-0.1.yml +0 -1
- data/test/app/config/version.yml +0 -1
- data/test/app/lib/app_test_task.rb +0 -3
- data/test/app_test.rb +0 -1849
- data/test/env/test_configure/recurse_a.yml +0 -2
- data/test/env/test_configure/recurse_b.yml +0 -2
- data/test/env/test_configure/tap.yml +0 -23
- data/test/env/test_load_env_config/dir/tap.yml +0 -3
- data/test/env/test_load_env_config/recurse_a.yml +0 -2
- data/test/env/test_load_env_config/recurse_b.yml +0 -2
- data/test/env/test_load_env_config/tap.yml +0 -3
- data/test/env_test.rb +0 -198
- data/test/file_task/config/batch.yml +0 -2
- data/test/file_task/config/configured.yml +0 -1
- data/test/file_task/old_file_one.txt +0 -0
- data/test/file_task/old_file_two.txt +0 -0
- data/test/file_task_test.rb +0 -1291
- data/test/root/alt_lib/alt_module.rb +0 -4
- data/test/root/file.txt +0 -0
- data/test/root/glob/one.txt +0 -0
- data/test/root/glob/two.txt +0 -0
- data/test/root/lib/absolute_alt_filepath.rb +0 -2
- data/test/root/lib/alternative_filepath.rb +0 -2
- data/test/root/lib/another_module.rb +0 -2
- data/test/root/lib/nested/some_module.rb +0 -4
- data/test/root/lib/no_module_included.rb +0 -0
- data/test/root/lib/some/module.rb +0 -4
- data/test/root/lib/some_class.rb +0 -2
- data/test/root/lib/some_module.rb +0 -3
- data/test/root/load_path/load_path_module.rb +0 -2
- data/test/root/load_path/skip_module.rb +0 -2
- data/test/root/mtime/older.txt +0 -0
- data/test/root/unload/full_path.rb +0 -2
- data/test/root/unload/loaded_by_nested.rb +0 -2
- data/test/root/unload/nested/nested_load.rb +0 -6
- data/test/root/unload/nested/nested_with_ext.rb +0 -4
- data/test/root/unload/nested/relative_path.rb +0 -4
- data/test/root/unload/older.rb +0 -2
- data/test/root/unload/unload_base.rb +0 -9
- data/test/root/versions/another.yml +0 -0
- data/test/root/versions/file-0.1.2.yml +0 -0
- data/test/root/versions/file-0.1.yml +0 -0
- data/test/root/versions/file.yml +0 -0
- data/test/root_test.rb +0 -718
- data/test/support/aggregator_test.rb +0 -99
- data/test/support/audit_test.rb +0 -445
- data/test/support/batchable_test.rb +0 -74
- data/test/support/class_configuration_test.rb +0 -331
- data/test/support/command_line_test.rb +0 -58
- data/test/support/configurable/config/configured.yml +0 -2
- data/test/support/configurable_test.rb +0 -295
- data/test/support/executable_queue_test.rb +0 -103
- data/test/support/executable_test.rb +0 -38
- data/test/support/logger_test.rb +0 -31
- data/test/support/rake_test.rb +0 -37
- data/test/support/shell_utils_test.rb +0 -24
- data/test/support/tdoc_test.rb +0 -370
- data/test/support/validation_test.rb +0 -54
- data/test/support/versions_test.rb +0 -103
- data/test/tap_test_helper.rb +0 -57
- data/test/tap_test_suite.rb +0 -7
- data/test/task/config/batch.yml +0 -2
- data/test/task/config/batched.yml +0 -2
- data/test/task/config/configured.yml +0 -1
- data/test/task/config/example.yml +0 -1
- data/test/task_base_test.rb +0 -24
- data/test/task_syntax_test.rb +0 -300
- data/test/task_test.rb +0 -320
- data/test/test/env_vars_test.rb +0 -48
- data/test/test/file_methods/test_assert_files/expected/one.txt +0 -1
- data/test/test/file_methods/test_assert_files/expected/two.txt +0 -1
- data/test/test/file_methods/test_assert_files/input/one.txt +0 -1
- data/test/test/file_methods/test_assert_files/input/two.txt +0 -1
- data/test/test/file_methods/test_assert_files_can_have_no_expected_files_if_specified/input/one.txt +0 -1
- data/test/test/file_methods/test_assert_files_can_have_no_expected_files_if_specified/input/two.txt +0 -1
- data/test/test/file_methods/test_assert_files_fails_for_different_content/expected/one.txt +0 -1
- data/test/test/file_methods/test_assert_files_fails_for_different_content/expected/two.txt +0 -1
- data/test/test/file_methods/test_assert_files_fails_for_different_content/input/one.txt +0 -1
- data/test/test/file_methods/test_assert_files_fails_for_different_content/input/two.txt +0 -1
- data/test/test/file_methods/test_assert_files_fails_for_missing_expected_file/expected/one.txt +0 -1
- data/test/test/file_methods/test_assert_files_fails_for_missing_expected_file/input/one.txt +0 -1
- data/test/test/file_methods/test_assert_files_fails_for_missing_expected_file/input/two.txt +0 -1
- data/test/test/file_methods/test_assert_files_fails_for_missing_output_file/expected/one.txt +0 -1
- data/test/test/file_methods/test_assert_files_fails_for_missing_output_file/expected/two.txt +0 -1
- data/test/test/file_methods/test_assert_files_fails_for_missing_output_file/input/one.txt +0 -1
- data/test/test/file_methods/test_assert_files_fails_for_missing_output_file/input/two.txt +0 -1
- data/test/test/file_methods/test_assert_files_fails_for_no_expected_files/input/one.txt +0 -1
- data/test/test/file_methods/test_assert_files_fails_for_no_expected_files/input/two.txt +0 -1
- 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/file_methods_doc/test_sub/expected/one.txt +0 -1
- data/test/test/file_methods_doc/test_sub/expected/two.txt +0 -1
- data/test/test/file_methods_doc/test_sub/input/one.txt +0 -1
- data/test/test/file_methods_doc/test_sub/input/two.txt +0 -1
- data/test/test/file_methods_doc_test.rb +0 -29
- data/test/test/file_methods_test.rb +0 -275
- data/test/test/subset_methods_test.rb +0 -171
- data/test/test/tap_methods/test_assert_files/expected/task/name/a.txt +0 -1
- data/test/test/tap_methods/test_assert_files/expected/task/name/b.txt +0 -1
- data/test/test/tap_methods/test_assert_files/input/a.txt +0 -1
- data/test/test/tap_methods/test_assert_files/input/b.txt +0 -1
- data/test/test/tap_methods_test.rb +0 -399
- data/test/workflow_test.rb +0 -120
- data/vendor/rails_generator.rb +0 -56
- data/vendor/rails_generator/base.rb +0 -263
- data/vendor/rails_generator/commands.rb +0 -581
- data/vendor/rails_generator/generated_attribute.rb +0 -42
- data/vendor/rails_generator/lookup.rb +0 -209
- data/vendor/rails_generator/manifest.rb +0 -53
- data/vendor/rails_generator/options.rb +0 -143
- data/vendor/rails_generator/scripts.rb +0 -83
- data/vendor/rails_generator/scripts/destroy.rb +0 -7
- data/vendor/rails_generator/scripts/generate.rb +0 -7
- data/vendor/rails_generator/scripts/update.rb +0 -12
- data/vendor/rails_generator/simple_logger.rb +0 -46
- data/vendor/rails_generator/spec.rb +0 -44
data/lib/tap/app.rb
CHANGED
|
@@ -1,61 +1,33 @@
|
|
|
1
|
+
require 'logger'
|
|
2
|
+
require 'tap/support/run_error'
|
|
3
|
+
require 'tap/support/aggregator'
|
|
4
|
+
require 'tap/support/executable_queue'
|
|
5
|
+
|
|
1
6
|
module Tap
|
|
2
7
|
|
|
3
|
-
# = Overview
|
|
4
|
-
#
|
|
5
8
|
# App coordinates the setup and running of tasks, and provides an interface
|
|
6
9
|
# to the application directory structure. App is convenient for use within
|
|
7
|
-
# scripts
|
|
8
|
-
#
|
|
9
|
-
# === Task Setup
|
|
10
|
-
#
|
|
11
|
-
# All tasks have an App (by default App.instance) which helps initialize the
|
|
12
|
-
# task by loading configuration templates from the config directory. Say
|
|
13
|
-
# we had the following configuration files:
|
|
14
|
-
#
|
|
15
|
-
# [/path/to/app/config/some/task.yml]
|
|
16
|
-
# key: one
|
|
17
|
-
#
|
|
18
|
-
# [/path/to/app/config/another/task.yml]
|
|
19
|
-
# key: two
|
|
20
|
-
#
|
|
21
|
-
# Tasks initialized with the names 'some/task' and 'another/task' will
|
|
22
|
-
# be cofigured by App like this:
|
|
23
|
-
#
|
|
24
|
-
# app = App.instance
|
|
25
|
-
# app.root # => '/path/to/app'
|
|
26
|
-
# app[:config] # => '/path/to/app/config'
|
|
27
|
-
#
|
|
28
|
-
# some_task = Task.new 'some/task'
|
|
29
|
-
# some_task.app # => App.instance
|
|
30
|
-
# some_task.config_file # => '/path/to/app/config/some/task.yml'
|
|
31
|
-
# some_task.config # => {:key => 'one'}
|
|
32
|
-
#
|
|
33
|
-
# another_task = Task.new 'another/task'
|
|
34
|
-
# another_task.app # => App.instance
|
|
35
|
-
# another_task.config_file # => '/path/to/app/config/another/task.yml'
|
|
36
|
-
# another_task.config # => {:key => 'two'}
|
|
37
|
-
#
|
|
38
|
-
# If app[:config] referenced a different directory then the tasks would be
|
|
39
|
-
# initialized from files relative to that location.
|
|
40
|
-
#
|
|
41
|
-
# (see Tap::Root for more details)
|
|
10
|
+
# scripts and, with Env, provides the basis for the 'tap' command line
|
|
11
|
+
# application.
|
|
42
12
|
#
|
|
43
13
|
# === Running Tasks
|
|
44
14
|
#
|
|
45
|
-
#
|
|
46
|
-
# resources like the logger
|
|
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:
|
|
47
18
|
#
|
|
48
19
|
# t1 = Task.new {|task, input| input += 1 }
|
|
49
|
-
# t1.enq
|
|
50
|
-
#
|
|
20
|
+
# t1.enq(0)
|
|
21
|
+
# app.enq(t1, 1)
|
|
51
22
|
#
|
|
52
23
|
# app.run
|
|
53
|
-
# app.results(t1) # => [1,
|
|
24
|
+
# app.results(t1) # => [1, 2]
|
|
54
25
|
#
|
|
55
|
-
# When a task completes,
|
|
56
|
-
#
|
|
57
|
-
#
|
|
58
|
-
#
|
|
26
|
+
# When a task completes, the results will either be passed to the task
|
|
27
|
+
# <tt>on_complete</tt> block (if set) or be collected into an Aggregator;
|
|
28
|
+
# aggregated results may be accessed per-task, as shown above. Task
|
|
29
|
+
# <tt>on_complete</tt> blocks typically enque other tasks, allowing the
|
|
30
|
+
# construction of workflows:
|
|
59
31
|
#
|
|
60
32
|
# # clear the previous results
|
|
61
33
|
# app.aggregator.clear
|
|
@@ -73,41 +45,25 @@ module Tap
|
|
|
73
45
|
# Here t1 has no results because the on_complete block passed them to t2 in
|
|
74
46
|
# a simple sequence.
|
|
75
47
|
#
|
|
76
|
-
#
|
|
77
|
-
#
|
|
78
|
-
# Running a task really consists of calling a method. For tasks, the method is
|
|
79
|
-
# basically the block you provide to Task.new, although execution is mediated by
|
|
80
|
-
# Tap::Task#execute and Tap::Task#process so that the block receives the task
|
|
81
|
-
# as a standard input. In subclasses, the method corresponds to the subclass
|
|
82
|
-
# 'process' method.
|
|
83
|
-
#
|
|
84
|
-
# # the block is called to add one to the input
|
|
85
|
-
# Task.new {|task, input| input += 1 }
|
|
86
|
-
#
|
|
87
|
-
# # same thing, but now in a subclass
|
|
88
|
-
# class AddOne < Tap::Task
|
|
89
|
-
# def process(input) input += 1 end
|
|
90
|
-
# end
|
|
48
|
+
# ==== Batching
|
|
91
49
|
#
|
|
92
|
-
#
|
|
93
|
-
#
|
|
94
|
-
# methods of several tasks are enqued at the same time, allowing you to feed the
|
|
95
|
-
# same inputs to multiple methods at once.
|
|
50
|
+
# Tasks can be batched, allowing the same input to be enqued to multiple
|
|
51
|
+
# tasks at once.
|
|
96
52
|
#
|
|
97
53
|
# t1 = Task.new {|task, input| input += 1 }
|
|
98
54
|
# t2 = Task.new {|task, input| input += 10 }
|
|
99
55
|
# Task.batch(t1, t2) # => [t1, t2]
|
|
100
56
|
#
|
|
101
57
|
# t1.enq 0
|
|
102
|
-
# t2.enq 10
|
|
103
58
|
#
|
|
104
59
|
# app.run
|
|
105
|
-
# app.results(t1) # => [1
|
|
106
|
-
# app.results(t2) # => [10
|
|
60
|
+
# app.results(t1) # => [1]
|
|
61
|
+
# app.results(t2) # => [10]
|
|
107
62
|
#
|
|
108
|
-
#
|
|
109
|
-
#
|
|
110
|
-
#
|
|
63
|
+
# ==== Multithreading
|
|
64
|
+
#
|
|
65
|
+
# App supports multithreading; multithreaded tasks execute cosynchronously,
|
|
66
|
+
# each on their own thread.
|
|
111
67
|
#
|
|
112
68
|
# lock = Mutex.new
|
|
113
69
|
# array = []
|
|
@@ -123,25 +79,20 @@ module Tap
|
|
|
123
79
|
# array.length # => 2
|
|
124
80
|
# array[0] == array[1] # => false
|
|
125
81
|
#
|
|
126
|
-
#
|
|
127
|
-
#
|
|
128
|
-
#
|
|
129
|
-
#
|
|
82
|
+
# Naturally, it is up to you to make sure each task is thread safe. Note
|
|
83
|
+
# that for the most part Tap::App is NOT thread safe; only run and
|
|
84
|
+
# run-related methods (ready, stop, terminate, info) are synchronized.
|
|
85
|
+
# Methods enq and results act on thread-safe objects ExecutableQueue and
|
|
86
|
+
# Aggregator, and should be ok to use from multiple threads.
|
|
130
87
|
#
|
|
131
|
-
#
|
|
132
|
-
# Task::Base.initialize(array, :push)
|
|
88
|
+
# ==== Executables
|
|
133
89
|
#
|
|
134
|
-
#
|
|
135
|
-
#
|
|
90
|
+
# App can use any Executable object in place of a task. One way to initialize
|
|
91
|
+
# an Executable for a method is to use the Object#_method defined by Tap. The
|
|
92
|
+
# result can be enqued and incorporated into workflows, but they cannot be
|
|
93
|
+
# batched.
|
|
136
94
|
#
|
|
137
|
-
#
|
|
138
|
-
# app.run
|
|
139
|
-
# array # => [1, 2]
|
|
140
|
-
#
|
|
141
|
-
# Lastly, if you can't or don't want to turn your object into a task, Tap defines
|
|
142
|
-
# Object#_method to generate executable objects that can be enqued and
|
|
143
|
-
# incorporated into workflows, although they cannot be batched. The mq
|
|
144
|
-
# (method enq) method generates and enques the method in one step.
|
|
95
|
+
# The mq (method enq) method generates and enques the method in one step.
|
|
145
96
|
#
|
|
146
97
|
# array = []
|
|
147
98
|
# m = array._method(:push)
|
|
@@ -153,22 +104,20 @@ module Tap
|
|
|
153
104
|
# app.run
|
|
154
105
|
# array # => [1, 2]
|
|
155
106
|
#
|
|
156
|
-
# App keeps running as long as it finds methods in the queue, or until it is stopped
|
|
157
|
-
# or terminated.
|
|
158
|
-
#
|
|
159
|
-
# (see Tap::Support::Executable, Tap::Task, and Tap::Task::Base for more details)
|
|
160
|
-
#
|
|
161
107
|
# === Auditing
|
|
162
108
|
#
|
|
163
|
-
# All results generated by methods are audited to track how a given
|
|
164
|
-
# evolves during a workflow.
|
|
109
|
+
# All results generated by executable methods are audited to track how a given
|
|
110
|
+
# input evolves during a workflow.
|
|
165
111
|
#
|
|
166
112
|
# To illustrate auditing, consider a workflow that uses the 'add_one' method
|
|
167
113
|
# to add one to an input until the result is 3, then adds five more with the
|
|
168
114
|
# 'add_five' method. The final result should always be 8.
|
|
169
115
|
#
|
|
170
|
-
# t1 = Tap::Task.new
|
|
171
|
-
#
|
|
116
|
+
# t1 = Tap::Task.new {|task, input| input += 1 }
|
|
117
|
+
# t1.name = "add_one"
|
|
118
|
+
#
|
|
119
|
+
# t2 = Tap::Task.new {|task, input| input += 5 }
|
|
120
|
+
# t2.name = "add_five"
|
|
172
121
|
#
|
|
173
122
|
# t1.on_complete do |_result|
|
|
174
123
|
# # _result is the audit; use the _current method
|
|
@@ -219,7 +168,7 @@ module Tap
|
|
|
219
168
|
# See Tap::Support::Audit for more details.
|
|
220
169
|
class App < Root
|
|
221
170
|
include MonitorMixin
|
|
222
|
-
|
|
171
|
+
|
|
223
172
|
class << self
|
|
224
173
|
# Sets the current app instance
|
|
225
174
|
attr_writer :instance
|
|
@@ -230,28 +179,25 @@ module Tap
|
|
|
230
179
|
@instance ||= App.new
|
|
231
180
|
end
|
|
232
181
|
end
|
|
233
|
-
|
|
234
|
-
#
|
|
235
|
-
attr_reader :options
|
|
236
|
-
|
|
237
|
-
# The shared logger.
|
|
182
|
+
|
|
183
|
+
# The shared logger
|
|
238
184
|
attr_reader :logger
|
|
239
185
|
|
|
240
|
-
# The application queue
|
|
186
|
+
# The application queue
|
|
241
187
|
attr_reader :queue
|
|
242
188
|
|
|
243
|
-
# The state of the application (see App::State)
|
|
189
|
+
# The state of the application (see App::State)
|
|
244
190
|
attr_reader :state
|
|
245
191
|
|
|
246
|
-
# A hash of (task_name, task_class_name) pairs mapping names to
|
|
247
|
-
# classes for instantiating tasks that have a non-default name.
|
|
248
|
-
# See task_class_name for more details.
|
|
249
|
-
attr_accessor :map
|
|
250
|
-
|
|
251
192
|
# A Tap::Support::Aggregator to collect the results of
|
|
252
|
-
# methods that have no on_complete block
|
|
193
|
+
# methods that have no <tt>on_complete</tt> block
|
|
253
194
|
attr_reader :aggregator
|
|
254
195
|
|
|
196
|
+
config :max_threads, 10, &c.integer # For multithread execution
|
|
197
|
+
config :debug, false, &c.flag # Flag debugging
|
|
198
|
+
config :force, false, &c.flag # Force execution at checkpoints
|
|
199
|
+
config :quiet, false, &c.flag # Suppress logging
|
|
200
|
+
|
|
255
201
|
# The constants defining the possible App states.
|
|
256
202
|
module State
|
|
257
203
|
READY = 0
|
|
@@ -271,11 +217,8 @@ module Tap
|
|
|
271
217
|
end
|
|
272
218
|
end
|
|
273
219
|
|
|
274
|
-
DEFAULT_MAX_THREADS = 10
|
|
275
|
-
|
|
276
220
|
# Creates a new App with the given configuration.
|
|
277
|
-
|
|
278
|
-
def initialize(config={})
|
|
221
|
+
def initialize(config={}, logger=DEFAULT_LOGGER)
|
|
279
222
|
super()
|
|
280
223
|
|
|
281
224
|
@state = State::READY
|
|
@@ -285,356 +228,43 @@ module Tap
|
|
|
285
228
|
|
|
286
229
|
@queue = Support::ExecutableQueue.new
|
|
287
230
|
@aggregator = Support::Aggregator.new
|
|
288
|
-
|
|
289
|
-
# defaults must be provided for options and logging to ensure
|
|
290
|
-
# that they will be initialized by reconfigure
|
|
291
|
-
self.reconfigure( {
|
|
292
|
-
:options => {}, :logger => {}, :map => {}
|
|
293
|
-
}.merge(config) )
|
|
294
|
-
end
|
|
295
|
-
|
|
296
|
-
# Clears the queue and aggregator.
|
|
297
|
-
#def clear(options={})
|
|
298
|
-
# # syncrhonize?
|
|
299
|
-
# ready
|
|
300
|
-
# raise "cannot clear unless state == READY" unless state == State::READY
|
|
301
|
-
#
|
|
302
|
-
# queue.clear
|
|
303
|
-
# aggregator.clear
|
|
304
|
-
#end
|
|
305
|
-
|
|
306
|
-
# True if options.debug or the global variable $DEBUG is true.
|
|
307
|
-
def debug?
|
|
308
|
-
options.debug || $DEBUG ? true : false
|
|
309
|
-
end
|
|
310
|
-
|
|
311
|
-
# Returns the configuration of self.
|
|
312
|
-
def config
|
|
313
|
-
{:root => self.root,
|
|
314
|
-
:directories => self.directories,
|
|
315
|
-
:absolute_paths => self.absolute_paths,
|
|
316
|
-
:options => self.options.marshal_dump,
|
|
317
|
-
:logger => {
|
|
318
|
-
:device => self.logger.logdev.dev,
|
|
319
|
-
:level => self.logger.level,
|
|
320
|
-
:datetime_format => self.logger.datetime_format}}
|
|
321
|
-
end
|
|
322
|
-
|
|
323
|
-
# Reconfigures self with the input configurations; other configurations are not affected.
|
|
324
|
-
#
|
|
325
|
-
# app = Tap::App.new :root => "/root", :directories => {:dir => 'path/to/dir'}
|
|
326
|
-
# app.reconfigure(
|
|
327
|
-
# :root => "./new/root",
|
|
328
|
-
# :logger => {:level => Logger::DEBUG})
|
|
329
|
-
#
|
|
330
|
-
# app.root # => File.expand_path("./new/root")
|
|
331
|
-
# app[:dir] # => File.expand_path("./new/root/path/to/dir")
|
|
332
|
-
# app.logger.level # => Logger::DEBUG
|
|
333
|
-
#
|
|
334
|
-
# Available configurations:
|
|
335
|
-
# root:: resets the root directory of self using root=
|
|
336
|
-
# directories:: resets directory aliases using directories= (note ALL
|
|
337
|
-
# aliases are reset. use app[dir]= to set a single alias)
|
|
338
|
-
# absolute_paths:: resets absolute path aliases using absolute_paths= (note ALL
|
|
339
|
-
# aliases are reset. use app[dir]= to set a single alias)
|
|
340
|
-
# options:: resets the application options (note ALL options are reset.
|
|
341
|
-
# use app.options.opt= to set a single option)
|
|
342
|
-
# logger:: creates and sets a new logger from the configuration
|
|
343
|
-
#
|
|
344
|
-
# Available logger configurations and defaults:
|
|
345
|
-
# device:: STDOUT
|
|
346
|
-
# level:: INFO (1)
|
|
347
|
-
# datetime_format:: %H:%M:%S
|
|
348
|
-
#
|
|
349
|
-
# Unknown configurations raise an error.
|
|
350
|
-
def reconfigure(config={})
|
|
351
|
-
config = config.symbolize_keys
|
|
352
|
-
|
|
353
|
-
# ensure critical keys are evaluated in the proper order
|
|
354
|
-
keys = [:root, :directories, :absolute_paths, :options]
|
|
355
|
-
config.keys.each do |key|
|
|
356
|
-
keys << key unless keys.include?(key)
|
|
357
|
-
end
|
|
358
|
-
|
|
359
|
-
keys.each do |key|
|
|
360
|
-
next unless config.has_key?(key)
|
|
361
|
-
value = config[key]
|
|
362
|
-
|
|
363
|
-
case key
|
|
364
|
-
when :root
|
|
365
|
-
self.root = value
|
|
366
|
-
when :directories
|
|
367
|
-
self.directories = value
|
|
368
|
-
when :absolute_paths
|
|
369
|
-
self.absolute_paths = value
|
|
370
|
-
when :options
|
|
371
|
-
@options = OpenStruct.new
|
|
372
|
-
value.each_pair {|k,v| options.send("#{k}=", v) }
|
|
373
|
-
when :logger
|
|
374
|
-
log_config = {
|
|
375
|
-
:device => STDOUT,
|
|
376
|
-
:level => 'INFO',
|
|
377
|
-
:datetime_format => '%H:%M:%S'
|
|
378
|
-
}.merge(value.symbolize_keys)
|
|
379
|
-
|
|
380
|
-
logger = Logger.new(log_config[:device])
|
|
381
|
-
logger.level = log_config[:level].kind_of?(String) ? Logger.const_get(log_config[:level]) : log_config[:level]
|
|
382
|
-
logger.datetime_format = log_config[:datetime_format]
|
|
383
|
-
self.logger = logger
|
|
384
|
-
when :map
|
|
385
|
-
self.map = value
|
|
386
|
-
else
|
|
387
|
-
unless handle_configuation(key, value)
|
|
388
|
-
if block_given?
|
|
389
|
-
yield(key, value)
|
|
390
|
-
else
|
|
391
|
-
raise ArgumentError.new("Unknown configuration: #{key}")
|
|
392
|
-
end
|
|
393
|
-
end
|
|
394
|
-
end
|
|
395
|
-
end
|
|
396
231
|
|
|
397
|
-
|
|
232
|
+
initialize_config(config)
|
|
233
|
+
self.logger = logger
|
|
398
234
|
end
|
|
399
235
|
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
unloaded = []
|
|
405
|
-
|
|
406
|
-
# echos the behavior of Dependencies.clear,
|
|
407
|
-
# but collects unloaded constants
|
|
408
|
-
Dependencies.loaded.clear
|
|
409
|
-
Dependencies.autoloaded_constants.each do |const|
|
|
410
|
-
Dependencies.remove_constant const
|
|
411
|
-
unloaded << const
|
|
412
|
-
end
|
|
413
|
-
Dependencies.autoloaded_constants.clear
|
|
414
|
-
Dependencies.explicitly_unloadable_constants.each do |const|
|
|
415
|
-
Dependencies.remove_constant const
|
|
416
|
-
unloaded << const
|
|
417
|
-
end
|
|
418
|
-
|
|
419
|
-
unloaded
|
|
236
|
+
DEFAULT_LOGGER = Logger.new(STDOUT)
|
|
237
|
+
DEFAULT_LOGGER.level = Logger::INFO
|
|
238
|
+
DEFAULT_LOGGER.formatter = lambda do |severity, time, progname, msg|
|
|
239
|
+
" %s[%s] %18s %s\n" % [severity[0,1], time.strftime('%H:%M:%S') , progname || '--' , msg]
|
|
420
240
|
end
|
|
421
241
|
|
|
422
|
-
#
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
# raises a LookupError.
|
|
426
|
-
def lookup_const(const_name)
|
|
427
|
-
return const_name if const_name.kind_of?(Module)
|
|
428
|
-
|
|
429
|
-
begin
|
|
430
|
-
const_name = const_name.camelize
|
|
431
|
-
|
|
432
|
-
case RUBY_VERSION
|
|
433
|
-
when /^1.9/
|
|
434
|
-
|
|
435
|
-
# a check is necessary to maintain the 1.8 behavior
|
|
436
|
-
# of lookup_const in 1.9, where ancestor constants
|
|
437
|
-
# may be returned by a direct evaluation
|
|
438
|
-
const_name.split("::").inject(Object) do |current, const|
|
|
439
|
-
const = const.to_sym
|
|
440
|
-
|
|
441
|
-
current.const_get(const).tap do |c|
|
|
442
|
-
unless current.const_defined?(const, false)
|
|
443
|
-
raise NameError.new("uninitialized constant #{const_name}")
|
|
444
|
-
end
|
|
445
|
-
end
|
|
446
|
-
end
|
|
447
|
-
|
|
448
|
-
else
|
|
449
|
-
const_name.constantize
|
|
450
|
-
end
|
|
451
|
-
|
|
452
|
-
rescue(NameError)
|
|
453
|
-
if block_given?
|
|
454
|
-
yield
|
|
455
|
-
else
|
|
456
|
-
raise LookupError.new("unknown constant: #{const_name}")
|
|
457
|
-
end
|
|
458
|
-
end
|
|
242
|
+
# True if debug or the global variable $DEBUG is true.
|
|
243
|
+
def debug?
|
|
244
|
+
debug || $DEBUG
|
|
459
245
|
end
|
|
460
246
|
|
|
461
|
-
#
|
|
462
|
-
#
|
|
463
|
-
#
|
|
464
|
-
|
|
465
|
-
# Sets the current logger. The logger is extended with Support::Logger to provide
|
|
466
|
-
# additional logging capabilities. The logger level is set to Logger::DEBUG if
|
|
467
|
-
# the global variable $DEBUG is true.
|
|
247
|
+
# Sets the current logger. The logger level is set to Logger::DEBUG if
|
|
248
|
+
# debug? is true.
|
|
468
249
|
def logger=(logger)
|
|
250
|
+
unless logger.nil?
|
|
251
|
+
logger.level = Logger::DEBUG if debug?
|
|
252
|
+
end
|
|
253
|
+
|
|
469
254
|
@logger = logger
|
|
470
|
-
@logger.extend Support::Logger unless @logger.nil?
|
|
471
|
-
@logger.level = Logger::DEBUG if $DEBUG
|
|
472
|
-
@logger
|
|
473
255
|
end
|
|
474
256
|
|
|
475
257
|
# Logs the action and message at the input level (default INFO).
|
|
476
|
-
# Logging is suppressed if
|
|
258
|
+
# Logging is suppressed if quiet is true.
|
|
477
259
|
def log(action, msg="", level=Logger::INFO)
|
|
478
|
-
logger.add(level, msg, action.to_s) unless
|
|
479
|
-
end
|
|
480
|
-
|
|
481
|
-
# EXPERIMENTAL
|
|
482
|
-
#
|
|
483
|
-
# Formatted log. Works like log, but passes the current log format to the
|
|
484
|
-
# block and uses whatever format the block returns. The format recieves
|
|
485
|
-
# the following arguments like so:
|
|
486
|
-
#
|
|
487
|
-
# format % [severity, timestamp, (action || '--'), msg]
|
|
488
|
-
#
|
|
489
|
-
# By default, if you don't specify a block, flog just chomps a newline off
|
|
490
|
-
# the format, so your log will be inline.
|
|
491
|
-
#
|
|
492
|
-
# BUG: Not thread safe at the moment.
|
|
493
|
-
def flog(action="", msg="", level=Logger::INFO) # :yields: format
|
|
494
|
-
unless options.quiet
|
|
495
|
-
logger.format_add(level, msg, action) do |format|
|
|
496
|
-
block_given? ? yield(format) : format.chomp("\n")
|
|
497
|
-
end
|
|
498
|
-
end
|
|
499
|
-
end
|
|
500
|
-
|
|
501
|
-
#
|
|
502
|
-
# Task methods
|
|
503
|
-
#
|
|
504
|
-
|
|
505
|
-
# Instantiates the specifed task with config (if provided). The task
|
|
506
|
-
# class is determined by task_class.
|
|
507
|
-
#
|
|
508
|
-
# t = app.task('tap/file_task')
|
|
509
|
-
# t.class # => Tap::FileTask
|
|
510
|
-
# t.name # => 'tap/file_task'
|
|
511
|
-
#
|
|
512
|
-
# app.map = {"mapped-task" => "Tap::FileTask"}
|
|
513
|
-
# t = app.task('mapped-task-1.0', :key => 'value')
|
|
514
|
-
# t.class # => Tap::FileTask
|
|
515
|
-
# t.name # => "mapped-task-1.0"
|
|
516
|
-
# t.config[:key] # => 'value'
|
|
517
|
-
#
|
|
518
|
-
# A new task is instantiated for each call to task; tasks may share the
|
|
519
|
-
# same name.
|
|
520
|
-
def task(task_name, config={}, &block)
|
|
521
|
-
task_class(task_name).new(task_name, config, &block)
|
|
522
|
-
end
|
|
523
|
-
|
|
524
|
-
# Looks up the specifed task class. Names are mapped to task classes
|
|
525
|
-
# using task_class_name.
|
|
526
|
-
#
|
|
527
|
-
# t_class = app.task_class('tap/file_task')
|
|
528
|
-
# t_class # => Tap::FileTask
|
|
529
|
-
#
|
|
530
|
-
# app.map = {"mapped-task" => "Tap::FileTask"}
|
|
531
|
-
# t_class = app.task_class('mapped-task-1.0')
|
|
532
|
-
# t_class # => Tap::FileTask
|
|
533
|
-
#
|
|
534
|
-
# Notes:
|
|
535
|
-
# - The task class will be auto-loaded using Dependencies, if needed.
|
|
536
|
-
# - A LookupError is raised if the task class cannot be found.
|
|
537
|
-
def task_class(task_name)
|
|
538
|
-
lookup_const task_class_name(task_name) do
|
|
539
|
-
raise LookupError.new("unknown task '#{task_name}'")
|
|
540
|
-
end
|
|
541
|
-
end
|
|
542
|
-
|
|
543
|
-
# Returns the class name of the specified task. If the task
|
|
544
|
-
# descriptor is a string, the class name is the de-versioned,
|
|
545
|
-
# descriptor, or the class name as specified in map by the
|
|
546
|
-
# de-versioned descriptor.
|
|
547
|
-
#
|
|
548
|
-
# app.map = {"mapped-task" => "Tap::FileTask"}
|
|
549
|
-
# app.task_class_name('some/task_class') # => "some/task_class"
|
|
550
|
-
# app.task_class_name('mapped-task-1.0') # => "Tap::FileTask"
|
|
551
|
-
#
|
|
552
|
-
# If td is a type of Tap::Task::Base, then task_class_name
|
|
553
|
-
# returns td.class.to_s
|
|
554
|
-
#
|
|
555
|
-
# t1 = Task.new
|
|
556
|
-
# app.task_class_name(t1) # => "Tap::Task"
|
|
557
|
-
#
|
|
558
|
-
# t2 = Object.new.extend Tap::Task::Base
|
|
559
|
-
# app.task_class_name(t2) # => "Object"
|
|
560
|
-
#
|
|
561
|
-
def task_class_name(td)
|
|
562
|
-
case td
|
|
563
|
-
when Tap::Task::Base then td.class.to_s
|
|
564
|
-
else
|
|
565
|
-
# de-version and resolve using map
|
|
566
|
-
name, version = deversion(td.to_s)
|
|
567
|
-
map.has_key?(name) ? map[name].to_s : name
|
|
568
|
-
end
|
|
569
|
-
end
|
|
570
|
-
|
|
571
|
-
# Iteratively passes the block the configuration templates for the specified file.
|
|
572
|
-
# Ultimately these templates specify configurations for tasks, as well batched tasks,
|
|
573
|
-
# linked to to self. If no block is specified, each_config_template collects the
|
|
574
|
-
# templates and returns them as an array.
|
|
575
|
-
#
|
|
576
|
-
# To make templates, the contents of the file are processed using ERB, then loaded
|
|
577
|
-
# as YAML. ERB for the config files is evaluated in a binding that contains
|
|
578
|
-
# references to self (app) and the input filepath.
|
|
579
|
-
#
|
|
580
|
-
# # [simple.yml]
|
|
581
|
-
# # key: value
|
|
582
|
-
#
|
|
583
|
-
# app.each_config_template("simple.yml") # => [{"key" => "value"}]
|
|
584
|
-
#
|
|
585
|
-
# # [erb.yml]
|
|
586
|
-
# # app: <%= app.object_id %>
|
|
587
|
-
# # filepath: <%= filepath %>
|
|
588
|
-
#
|
|
589
|
-
# app.each_config_template("erb.yml") # => [{"app" => app.object_id, "filepath" => "erb.yml"}]
|
|
590
|
-
#
|
|
591
|
-
# Batched tasks can be specified by providing an array of hashes.
|
|
592
|
-
#
|
|
593
|
-
# # [batched_with_erb.yml]
|
|
594
|
-
# # - key: <%= 1 %>
|
|
595
|
-
# # - key: <%= 1 + 1 %>
|
|
596
|
-
#
|
|
597
|
-
# app.each_config_template("batched_with_erb.yml") # => [{"key" => 1}, {"key" => 2}]
|
|
598
|
-
#
|
|
599
|
-
# If no config templates can be loaded (as when the filepath does not exist, or
|
|
600
|
-
# the file is empty), each_config_template passes the block a single empty template.
|
|
601
|
-
def each_config_template(filepath) # :yields: template
|
|
602
|
-
unless block_given?
|
|
603
|
-
templates = []
|
|
604
|
-
each_config_template(filepath) {|template| templates << template}
|
|
605
|
-
return templates
|
|
606
|
-
end
|
|
607
|
-
|
|
608
|
-
if filepath == nil
|
|
609
|
-
yield({})
|
|
610
|
-
else
|
|
611
|
-
templates = if !File.exists?(filepath) || File.directory?(filepath)
|
|
612
|
-
nil
|
|
613
|
-
else
|
|
614
|
-
# create the reference to app for templating
|
|
615
|
-
app = self
|
|
616
|
-
input = ERB.new(File.read(filepath)).result(binding)
|
|
617
|
-
YAML.load(input)
|
|
618
|
-
end
|
|
619
|
-
|
|
620
|
-
case templates
|
|
621
|
-
when Array
|
|
622
|
-
templates.each do |template|
|
|
623
|
-
yield(template)
|
|
624
|
-
end
|
|
625
|
-
when Hash
|
|
626
|
-
yield(templates)
|
|
627
|
-
else
|
|
628
|
-
yield({})
|
|
629
|
-
end
|
|
630
|
-
end
|
|
260
|
+
logger.add(level, msg, action.to_s) unless quiet
|
|
631
261
|
end
|
|
632
262
|
|
|
633
263
|
# Returns the configuration filepath for the specified task name,
|
|
634
264
|
# File.join(app['config'], task_name + ".yml"). Returns nil if
|
|
635
|
-
# task_name
|
|
636
|
-
def config_filepath(
|
|
637
|
-
|
|
265
|
+
# task_name is nil.
|
|
266
|
+
def config_filepath(name)
|
|
267
|
+
name == nil ? nil : filepath('config', "#{name}.yml")
|
|
638
268
|
end
|
|
639
269
|
|
|
640
270
|
#
|
|
@@ -659,41 +289,45 @@ module Tap
|
|
|
659
289
|
end
|
|
660
290
|
end
|
|
661
291
|
|
|
662
|
-
#
|
|
663
|
-
#
|
|
664
|
-
# at a time. If run is called when
|
|
292
|
+
# Sequentially executes the methods (ie Executable objects) in queue; run
|
|
293
|
+
# continues until the queue is empty and then returns self. An app can
|
|
294
|
+
# only run on one thread at a time. If run is called when already running,
|
|
295
|
+
# run returns immediately.
|
|
665
296
|
#
|
|
666
297
|
# === The Run Cycle
|
|
667
|
-
#
|
|
668
|
-
#
|
|
669
|
-
#
|
|
670
|
-
#
|
|
671
|
-
#
|
|
672
|
-
#
|
|
673
|
-
#
|
|
674
|
-
#
|
|
675
|
-
#
|
|
676
|
-
#
|
|
677
|
-
#
|
|
678
|
-
#
|
|
679
|
-
#
|
|
680
|
-
#
|
|
681
|
-
#
|
|
682
|
-
#
|
|
683
|
-
#
|
|
684
|
-
#
|
|
685
|
-
#
|
|
686
|
-
#
|
|
687
|
-
#
|
|
688
|
-
#
|
|
689
|
-
#
|
|
690
|
-
#
|
|
691
|
-
#
|
|
692
|
-
#
|
|
693
|
-
#
|
|
694
|
-
#
|
|
695
|
-
#
|
|
696
|
-
# is
|
|
298
|
+
# Run can execute methods in sequential or multithreaded mode. In sequential
|
|
299
|
+
# mode, run executes enqued methods in order and on the current thread. Run
|
|
300
|
+
# continues until it reaches a method marked with multithread = true, at which
|
|
301
|
+
# point run switches into multithreading mode.
|
|
302
|
+
#
|
|
303
|
+
# When multithreading, run shifts methods off of the queue and executes each
|
|
304
|
+
# on their own thread (launching up to max_threads threads at one time).
|
|
305
|
+
# Multithread execution continues until run reaches a non-multithread method,
|
|
306
|
+
# at which point run blocks, waits for the threads to complete, and switches
|
|
307
|
+
# back into sequential mode.
|
|
308
|
+
#
|
|
309
|
+
# Run never executes multithreaded and non-multithreaded methods at the same
|
|
310
|
+
# time.
|
|
311
|
+
#
|
|
312
|
+
# ==== Checks
|
|
313
|
+
# Run checks the state of self before executing a method. If the state is
|
|
314
|
+
# changed to State::STOP, then no more methods will be executed; currently
|
|
315
|
+
# running methods will continute to completion. If the state is changed to
|
|
316
|
+
# State::TERMINATE then no more methods will be executed and currently running
|
|
317
|
+
# methods will be discontinued as described below.
|
|
318
|
+
#
|
|
319
|
+
# ==== Error Handling and Termination
|
|
320
|
+
# When unhandled errors arise during run, run enters a termination routine.
|
|
321
|
+
# During termination a TerminationError is raised in each executing method so
|
|
322
|
+
# that the method exits or begins executing its internal error handling code
|
|
323
|
+
# (perhaps performing rollbacks).
|
|
324
|
+
#
|
|
325
|
+
# The TerminationError can ONLY be raised by the method itself, usually via a
|
|
326
|
+
# call to Tap::Support::Framework#check_terminate. <tt>check_terminate</tt>
|
|
327
|
+
# is available to all Framework objects (ex Task and Workflow), but not to
|
|
328
|
+
# Executable methods generated by _method. These methods need to check the
|
|
329
|
+
# state of app themselves; otherwise they will continue on to completion even
|
|
330
|
+
# when app is in State::TERMINATE.
|
|
697
331
|
#
|
|
698
332
|
# # this task will loop until app.terminate
|
|
699
333
|
# Task.new {|task| while(true) task.check_terminate end }
|
|
@@ -703,11 +337,12 @@ module Tap
|
|
|
703
337
|
#
|
|
704
338
|
# Additional errors that arise during termination are collected and packaged
|
|
705
339
|
# with the orignal error into a RunError. By default all errors are logged
|
|
706
|
-
# and run exits. If debug?
|
|
707
|
-
# handling.
|
|
340
|
+
# and run exits. If debug? is true, then the RunError will be raised for
|
|
341
|
+
# further handling.
|
|
708
342
|
#
|
|
709
|
-
# Note: the method that caused the original unhandled error is no longer
|
|
710
|
-
# when termination begins and thus will not recieve a
|
|
343
|
+
# Note: the method that caused the original unhandled error is no longer
|
|
344
|
+
# executing when termination begins and thus will not recieve a
|
|
345
|
+
# TerminationError.
|
|
711
346
|
def run
|
|
712
347
|
synchronize do
|
|
713
348
|
return self unless self.ready.state == State::READY
|
|
@@ -717,7 +352,6 @@ module Tap
|
|
|
717
352
|
end
|
|
718
353
|
|
|
719
354
|
# generate threading variables
|
|
720
|
-
max_threads = options.max_threads || DEFAULT_MAX_THREADS
|
|
721
355
|
self.thread_queue = max_threads > 0 ? Queue.new : nil
|
|
722
356
|
|
|
723
357
|
# TODO: log starting run
|
|
@@ -777,7 +411,7 @@ module Tap
|
|
|
777
411
|
# termination.
|
|
778
412
|
clear_thread_queue
|
|
779
413
|
errors = [$!] + clear_threads(false)
|
|
780
|
-
errors.delete_if {|error|
|
|
414
|
+
errors.delete_if {|error| error.kind_of?(TerminateError) }
|
|
781
415
|
|
|
782
416
|
# handle the errors accordingly
|
|
783
417
|
case
|
|
@@ -822,7 +456,7 @@ module Tap
|
|
|
822
456
|
# error handling code, perhaps performing rollbacks.
|
|
823
457
|
#
|
|
824
458
|
# Termination checks can be manually specified in a task
|
|
825
|
-
# using the check_terminate method (see Tap::Task
|
|
459
|
+
# using the check_terminate method (see Tap::Task#check_terminate).
|
|
826
460
|
# Termination checks automatically occur before each task execution.
|
|
827
461
|
#
|
|
828
462
|
# Does nothing if state == State::READY.
|
|
@@ -852,17 +486,13 @@ module Tap
|
|
|
852
486
|
end
|
|
853
487
|
end
|
|
854
488
|
|
|
855
|
-
#
|
|
856
|
-
# workflow related
|
|
857
|
-
#
|
|
858
|
-
|
|
859
489
|
# Enques the task with the inputs. If the task is batched, then each
|
|
860
490
|
# task in task.batch will be enqued with the inputs. Returns task.
|
|
861
491
|
#
|
|
862
492
|
# An Executable may provided instead of a task.
|
|
863
493
|
def enq(task, *inputs)
|
|
864
494
|
case task
|
|
865
|
-
when Tap::Task::
|
|
495
|
+
when Tap::Task, Tap::Workflow
|
|
866
496
|
raise "not assigned to enqueing app: #{task}" unless task.app == self
|
|
867
497
|
task.enq(*inputs)
|
|
868
498
|
when Support::Executable
|
|
@@ -898,7 +528,7 @@ module Tap
|
|
|
898
528
|
current_task = next_task
|
|
899
529
|
end
|
|
900
530
|
end
|
|
901
|
-
|
|
531
|
+
|
|
902
532
|
# Sets a fork workflow pattern for the tasks such that each of the
|
|
903
533
|
# targets will be enqueued with the results of the source when the
|
|
904
534
|
# source completes. Batched tasks will have the pattern set for each
|
|
@@ -914,7 +544,7 @@ module Tap
|
|
|
914
544
|
end
|
|
915
545
|
end
|
|
916
546
|
end
|
|
917
|
-
|
|
547
|
+
|
|
918
548
|
# Sets a merge workflow pattern for the tasks such that the results
|
|
919
549
|
# of each source will be enqueued to the target when the source
|
|
920
550
|
# completes. Batched tasks will have the pattern set for each
|
|
@@ -961,7 +591,7 @@ module Tap
|
|
|
961
591
|
protected
|
|
962
592
|
|
|
963
593
|
# A hook for handling unknown configurations in subclasses, called from
|
|
964
|
-
#
|
|
594
|
+
# configure. If handle_configuration evaluates to false, then configure
|
|
965
595
|
# raises an error.
|
|
966
596
|
def handle_configuation(key, value)
|
|
967
597
|
false
|
|
@@ -977,7 +607,7 @@ module Tap
|
|
|
977
607
|
attr_accessor :threads
|
|
978
608
|
|
|
979
609
|
# A Queue containing multithread tasks waiting to be run
|
|
980
|
-
# on the execution threads. Nil if
|
|
610
|
+
# on the execution threads. Nil if max_threads == 0
|
|
981
611
|
attr_accessor :thread_queue
|
|
982
612
|
|
|
983
613
|
private
|
|
@@ -1082,10 +712,6 @@ module Tap
|
|
|
1082
712
|
end
|
|
1083
713
|
end
|
|
1084
714
|
|
|
1085
|
-
# LookupErrors are raised for errors during dependency lookup
|
|
1086
|
-
class LookupError < RuntimeError
|
|
1087
|
-
end
|
|
1088
|
-
|
|
1089
715
|
# TerminateErrors are raised to kill executing tasks when terminate
|
|
1090
716
|
# is called on an running App. They are handled by the run rescue code.
|
|
1091
717
|
class TerminateError < RuntimeError
|