tap 0.7.9
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/MIT-LICENSE +21 -0
- data/README +71 -0
- data/Rakefile +117 -0
- data/bin/tap +63 -0
- data/lib/tap.rb +15 -0
- data/lib/tap/app.rb +739 -0
- data/lib/tap/file_task.rb +354 -0
- data/lib/tap/generator.rb +29 -0
- data/lib/tap/generator/generators/config/USAGE +0 -0
- data/lib/tap/generator/generators/config/config_generator.rb +23 -0
- data/lib/tap/generator/generators/config/templates/config.erb +2 -0
- data/lib/tap/generator/generators/file_task/USAGE +0 -0
- data/lib/tap/generator/generators/file_task/file_task_generator.rb +21 -0
- data/lib/tap/generator/generators/file_task/templates/task.erb +27 -0
- data/lib/tap/generator/generators/file_task/templates/test.erb +12 -0
- data/lib/tap/generator/generators/root/USAGE +0 -0
- data/lib/tap/generator/generators/root/root_generator.rb +36 -0
- data/lib/tap/generator/generators/root/templates/Rakefile +48 -0
- data/lib/tap/generator/generators/root/templates/app.yml +19 -0
- data/lib/tap/generator/generators/root/templates/config/process_tap_request.yml +4 -0
- data/lib/tap/generator/generators/root/templates/lib/process_tap_request.rb +26 -0
- data/lib/tap/generator/generators/root/templates/public/images/nav.jpg +0 -0
- data/lib/tap/generator/generators/root/templates/public/stylesheets/color.css +57 -0
- data/lib/tap/generator/generators/root/templates/public/stylesheets/layout.css +108 -0
- data/lib/tap/generator/generators/root/templates/public/stylesheets/normalize.css +40 -0
- data/lib/tap/generator/generators/root/templates/public/stylesheets/typography.css +21 -0
- data/lib/tap/generator/generators/root/templates/server/config/environment.rb +60 -0
- data/lib/tap/generator/generators/root/templates/server/lib/tasks/clear_database_prerequisites.rake +5 -0
- data/lib/tap/generator/generators/root/templates/server/test/test_helper.rb +53 -0
- data/lib/tap/generator/generators/root/templates/test/tap_test_helper.rb +3 -0
- data/lib/tap/generator/generators/root/templates/test/tap_test_suite.rb +4 -0
- data/lib/tap/generator/generators/task/USAGE +0 -0
- data/lib/tap/generator/generators/task/task_generator.rb +21 -0
- data/lib/tap/generator/generators/task/templates/task.erb +21 -0
- data/lib/tap/generator/generators/task/templates/test.erb +29 -0
- data/lib/tap/generator/generators/workflow/USAGE +0 -0
- data/lib/tap/generator/generators/workflow/templates/task.erb +16 -0
- data/lib/tap/generator/generators/workflow/templates/test.erb +7 -0
- data/lib/tap/generator/generators/workflow/workflow_generator.rb +21 -0
- data/lib/tap/generator/options.rb +26 -0
- data/lib/tap/generator/usage.rb +26 -0
- data/lib/tap/root.rb +275 -0
- data/lib/tap/script/console.rb +7 -0
- data/lib/tap/script/destroy.rb +8 -0
- data/lib/tap/script/generate.rb +8 -0
- data/lib/tap/script/run.rb +111 -0
- data/lib/tap/script/server.rb +12 -0
- data/lib/tap/support/audit.rb +415 -0
- data/lib/tap/support/batch_queue.rb +165 -0
- data/lib/tap/support/combinator.rb +114 -0
- data/lib/tap/support/logger.rb +91 -0
- data/lib/tap/support/rap.rb +38 -0
- data/lib/tap/support/run_error.rb +20 -0
- data/lib/tap/support/template.rb +81 -0
- data/lib/tap/support/templater.rb +155 -0
- data/lib/tap/support/versions.rb +63 -0
- data/lib/tap/task.rb +448 -0
- data/lib/tap/test.rb +320 -0
- data/lib/tap/test/env_vars.rb +16 -0
- data/lib/tap/test/inference_methods.rb +298 -0
- data/lib/tap/test/subset_methods.rb +260 -0
- data/lib/tap/version.rb +3 -0
- data/lib/tap/workflow.rb +73 -0
- data/test/app/config/addition_template.yml +6 -0
- data/test/app/config/batch.yml +2 -0
- data/test/app/config/empty.yml +0 -0
- data/test/app/config/erb.yml +1 -0
- data/test/app/config/template.yml +6 -0
- data/test/app/config/version-0.1.yml +1 -0
- data/test/app/config/version.yml +1 -0
- data/test/app/lib/app_test_task.rb +2 -0
- data/test/app_class_test.rb +33 -0
- data/test/app_test.rb +1372 -0
- data/test/file_task/config/batch.yml +2 -0
- data/test/file_task/config/configured.yml +1 -0
- 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 +1041 -0
- data/test/root/alt_lib/alt_module.rb +4 -0
- data/test/root/lib/absolute_alt_filepath.rb +2 -0
- data/test/root/lib/alternative_filepath.rb +2 -0
- data/test/root/lib/another_module.rb +2 -0
- data/test/root/lib/nested/some_module.rb +4 -0
- data/test/root/lib/no_module_included.rb +0 -0
- data/test/root/lib/some/module.rb +4 -0
- data/test/root/lib/some_class.rb +2 -0
- data/test/root/lib/some_module.rb +3 -0
- data/test/root/load_path/load_path_module.rb +2 -0
- data/test/root/load_path/skip_module.rb +2 -0
- data/test/root/mtime/older.txt +0 -0
- data/test/root/unload/full_path.rb +2 -0
- data/test/root/unload/loaded_by_nested.rb +2 -0
- data/test/root/unload/nested/nested_load.rb +6 -0
- data/test/root/unload/nested/nested_with_ext.rb +4 -0
- data/test/root/unload/nested/relative_path.rb +4 -0
- data/test/root/unload/older.rb +2 -0
- data/test/root/unload/unload_base.rb +9 -0
- 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 +483 -0
- data/test/support/audit_test.rb +449 -0
- data/test/support/batch_queue_test.rb +320 -0
- data/test/support/combinator_test.rb +249 -0
- data/test/support/logger_test.rb +31 -0
- data/test/support/template_test.rb +122 -0
- data/test/support/templater/erb.txt +2 -0
- data/test/support/templater/erb.yml +2 -0
- data/test/support/templater/somefile.txt +2 -0
- data/test/support/templater_test.rb +192 -0
- data/test/support/versions_test.rb +71 -0
- data/test/tap_test_helper.rb +4 -0
- data/test/tap_test_suite.rb +4 -0
- data/test/task/config/batch.yml +2 -0
- data/test/task/config/batched.yml +2 -0
- data/test/task/config/configured.yml +1 -0
- data/test/task/config/example.yml +1 -0
- data/test/task/config/overriding.yml +2 -0
- data/test/task/config/task_with_config.yml +1 -0
- data/test/task/config/template.yml +4 -0
- data/test/task_class_test.rb +118 -0
- data/test/task_execute_test.rb +233 -0
- data/test/task_test.rb +424 -0
- data/test/test/inference_methods/test_assert_expected/expected/file.txt +1 -0
- data/test/test/inference_methods/test_assert_expected/expected/folder/file.txt +1 -0
- data/test/test/inference_methods/test_assert_expected/input/file.txt +1 -0
- data/test/test/inference_methods/test_assert_expected/input/folder/file.txt +1 -0
- data/test/test/inference_methods/test_assert_files_exist/input/input_1.txt +0 -0
- data/test/test/inference_methods/test_assert_files_exist/input/input_2.txt +0 -0
- data/test/test/inference_methods/test_file_compare/expected/output_1.txt +3 -0
- data/test/test/inference_methods/test_file_compare/expected/output_2.txt +1 -0
- data/test/test/inference_methods/test_file_compare/input/input_1.txt +3 -0
- data/test/test/inference_methods/test_file_compare/input/input_2.txt +3 -0
- data/test/test/inference_methods/test_infer_glob/expected/file.yml +0 -0
- data/test/test/inference_methods/test_infer_glob/expected/file_1.txt +0 -0
- data/test/test/inference_methods/test_infer_glob/expected/file_2.txt +0 -0
- data/test/test/inference_methods/test_yml_compare/expected/output_1.yml +6 -0
- data/test/test/inference_methods/test_yml_compare/expected/output_2.yml +6 -0
- data/test/test/inference_methods/test_yml_compare/input/input_1.yml +4 -0
- data/test/test/inference_methods/test_yml_compare/input/input_2.yml +4 -0
- data/test/test/inference_methods_test.rb +311 -0
- data/test/test/subset_methods_test.rb +115 -0
- data/test/test_test.rb +233 -0
- data/test/workflow_test.rb +108 -0
- metadata +274 -0
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
require 'tap/support/audit'
|
|
2
|
+
require 'monitor'
|
|
3
|
+
|
|
4
|
+
module Tap
|
|
5
|
+
module Support
|
|
6
|
+
|
|
7
|
+
# BatchQueue allows thread-safe enqueing and dequeing of tasks and inputs for
|
|
8
|
+
# execution. Until the task is dequeued, additional enqueuements will aggregate
|
|
9
|
+
# the inputs into a batch. The queue order is determined solely by the task;
|
|
10
|
+
# all accumulated inputs are dequeued at once.
|
|
11
|
+
#
|
|
12
|
+
# # given tasks t1 and t2:
|
|
13
|
+
# q = BatchQueue.new
|
|
14
|
+
# q.enq t1, 1
|
|
15
|
+
# q.enq t2, :a,:b,:c
|
|
16
|
+
# q.enq t1, 2,3
|
|
17
|
+
#
|
|
18
|
+
# q.deq # => [t1, [1,2,3]]
|
|
19
|
+
# q.deq # => [t2, [:a,:b,:c]]
|
|
20
|
+
# q.deq # => nil
|
|
21
|
+
#
|
|
22
|
+
# All BatchQueue methods are monitored such that they may only be accessed
|
|
23
|
+
# by one thread at a time, in order to ensure that transactions are atomic.
|
|
24
|
+
class BatchQueue
|
|
25
|
+
include MonitorMixin
|
|
26
|
+
|
|
27
|
+
# creates a new BatchQueue
|
|
28
|
+
def initialize
|
|
29
|
+
super
|
|
30
|
+
self.clear
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Clears the BatchQueue of all tasks and inputs.
|
|
34
|
+
def clear
|
|
35
|
+
synchronize do
|
|
36
|
+
self.inputs = {}
|
|
37
|
+
self.tasks = []
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Returns the number of enqueued tasks (same as length)
|
|
42
|
+
def size
|
|
43
|
+
tasks.length
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Returns the number of enqueued tasks (same as size)
|
|
47
|
+
def length
|
|
48
|
+
size
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# True if no tasks are enqueued
|
|
52
|
+
def empty?
|
|
53
|
+
tasks.empty?
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Enqueues the inputs to the task. If the task has not already been added to the
|
|
57
|
+
# queue, then the task will be pushed onto the queue and the inputs registered.
|
|
58
|
+
# Otherwise, the inputs will be added to the registered inputs; the task will
|
|
59
|
+
# not be added to the queue twice. If the task is batched, then each task in the
|
|
60
|
+
# batch will be enqueued in order, with the same inputs (Audit inputs will be forked
|
|
61
|
+
# for each task).
|
|
62
|
+
def enq(task, *inputs)
|
|
63
|
+
synchronize do
|
|
64
|
+
batch = task.batch
|
|
65
|
+
batch.each do |t|
|
|
66
|
+
tasks << t unless tasks.include?(t)
|
|
67
|
+
|
|
68
|
+
inputs = inputs.collect do |result|
|
|
69
|
+
result.kind_of?(Support::Audit) ? result._fork : result
|
|
70
|
+
end if batch.length > 1
|
|
71
|
+
|
|
72
|
+
self.inputs[t] = [] unless self.inputs[t]
|
|
73
|
+
self.inputs[t].concat(inputs)
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Enqueues the task and input, then prioritizes the task to the front of the queue.
|
|
79
|
+
def priority_enq(task, *inputs)
|
|
80
|
+
synchronize do
|
|
81
|
+
enq(task, *inputs)
|
|
82
|
+
prioritize(task)
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Moves the task to the front of the queue. Does not queue a task if the task is not
|
|
87
|
+
# already in the queue. If the task is batched, then each task in the batch will be
|
|
88
|
+
# prioritized in order.
|
|
89
|
+
def prioritize(task)
|
|
90
|
+
synchronize do
|
|
91
|
+
task.batch.reverse_each do |t|
|
|
92
|
+
tasks.unshift(t) if tasks.delete(t)
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Moves the task to the back of the queue. Does not queue a task if the task is not
|
|
98
|
+
# already in the queue. If the task is batched, then each task in the batch will be
|
|
99
|
+
# marginalized in order.
|
|
100
|
+
def marginalize(task)
|
|
101
|
+
synchronize do
|
|
102
|
+
task.batch.each do |t|
|
|
103
|
+
tasks.push(t) if tasks.delete(t)
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# Returns true if the task is executable with the currently registered inputs
|
|
109
|
+
# (as determined by task.executable?).
|
|
110
|
+
def executable?(task)
|
|
111
|
+
synchronize do
|
|
112
|
+
task.executable?(inputs[task])
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# Dequeues the next executable task, or the specified task (even if it is not
|
|
117
|
+
# executable). The task and the current inputs for the task are returned in
|
|
118
|
+
# an array like: [task, inputs]. Returns nil if the task is not in the queue.
|
|
119
|
+
def deq(task=nil)
|
|
120
|
+
synchronize do
|
|
121
|
+
task = peek if task.nil?
|
|
122
|
+
|
|
123
|
+
if tasks.include?(task)
|
|
124
|
+
[tasks.delete(task), inputs.delete(task)]
|
|
125
|
+
else
|
|
126
|
+
nil
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# Returns the next executable task, or nil if no queued tasks are executable.
|
|
132
|
+
# The task is not dequeued.
|
|
133
|
+
def peek
|
|
134
|
+
synchronize do
|
|
135
|
+
tasks.each do |task|
|
|
136
|
+
next unless executable?(task)
|
|
137
|
+
return task
|
|
138
|
+
end
|
|
139
|
+
nil
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# Returns the number of executable tasks in the queue.
|
|
144
|
+
def num_executable
|
|
145
|
+
synchronize do
|
|
146
|
+
tasks.inject(0) do |count, task|
|
|
147
|
+
executable?(task) ? count + 1 : count
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
# Returns the number of non-executable tasks in the queue.
|
|
153
|
+
def num_not_executable
|
|
154
|
+
synchronize do
|
|
155
|
+
size - num_executable
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
protected
|
|
160
|
+
|
|
161
|
+
attr_accessor :inputs, :tasks
|
|
162
|
+
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
end
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
require 'enumerator'
|
|
2
|
+
|
|
3
|
+
module Tap
|
|
4
|
+
module Support
|
|
5
|
+
|
|
6
|
+
# Combinator provides a method for iterating over all possible combinations
|
|
7
|
+
# of items in the input sets.
|
|
8
|
+
#
|
|
9
|
+
# c = Combinator.new [1,2], [3,4]
|
|
10
|
+
# c.collect # => [[1,3], [1,4], [2,3], [2,4]]
|
|
11
|
+
#
|
|
12
|
+
# Combinator works by iteratively combining each element from the first set (a)
|
|
13
|
+
# with each element from the second set (b). When more than two sets are given,
|
|
14
|
+
# a Combinator will bundle all but the first set into a new Combinator which
|
|
15
|
+
# then acts as the second set.
|
|
16
|
+
#
|
|
17
|
+
# # Note: each element in a set is arrayified
|
|
18
|
+
# # to simplify creation of the combination.
|
|
19
|
+
#
|
|
20
|
+
# c = Combinator.new [1,2], [3,4], [5,6]
|
|
21
|
+
# c.a # => [[1],[2]]
|
|
22
|
+
# c.b.class # => Combinator
|
|
23
|
+
# c.b.a # => [[3],[4]]
|
|
24
|
+
# c.b.b # => [[5],[6]]
|
|
25
|
+
#
|
|
26
|
+
# Thus when iterating, each Combinator makes it's pairwise combinations and
|
|
27
|
+
# works outwards to the final multi-set combinations.
|
|
28
|
+
class Combinator
|
|
29
|
+
include Enumerable
|
|
30
|
+
|
|
31
|
+
attr_reader :a, :b
|
|
32
|
+
|
|
33
|
+
# Creates a new Combinator. Input sets must be an Array, or nil.
|
|
34
|
+
# Within the Array any objects are allowed for combination.
|
|
35
|
+
def initialize(*sets)
|
|
36
|
+
@a = make_set(sets.shift)
|
|
37
|
+
@b = make_set(*sets)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Returns the sets used to initialize the Combinator, minus any
|
|
41
|
+
# nil or empty sets.
|
|
42
|
+
def sets
|
|
43
|
+
sets_in(a) + sets_in(b)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# True if the Combinator has no combinations.
|
|
47
|
+
def empty?
|
|
48
|
+
a.empty? && b.empty?
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Returns the number of combinations in the Combinator.
|
|
52
|
+
def length
|
|
53
|
+
if a.empty? || b.empty?
|
|
54
|
+
if !a.empty?
|
|
55
|
+
a.length
|
|
56
|
+
elsif !b.empty?
|
|
57
|
+
b.length
|
|
58
|
+
else
|
|
59
|
+
0
|
|
60
|
+
end
|
|
61
|
+
else
|
|
62
|
+
a.length * b.length
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Passes each combination as an array to the input block.
|
|
67
|
+
def each(&block)
|
|
68
|
+
# if either set is empty, iterate only the non-empty set
|
|
69
|
+
# note, the two-step check on each ensures that if both
|
|
70
|
+
# sets are empty, then neither is iterated.
|
|
71
|
+
if a.empty? || b.empty?
|
|
72
|
+
if !a.empty?
|
|
73
|
+
a.each {|aa| yield aa }
|
|
74
|
+
elsif !b.empty?
|
|
75
|
+
b.each {|bb| yield bb }
|
|
76
|
+
end
|
|
77
|
+
else
|
|
78
|
+
# otherwise, iterate all combinations of both
|
|
79
|
+
a.each do |aa|
|
|
80
|
+
b.each do |bb|
|
|
81
|
+
yield aa + bb
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
private
|
|
88
|
+
|
|
89
|
+
def sets_in(set)
|
|
90
|
+
# recursively de-arrayifies each element in the array
|
|
91
|
+
case set
|
|
92
|
+
when Combinator then set.sets
|
|
93
|
+
when Array then set.empty? ? [] : [set.collect {|s| s.first}]
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def make_set(*sets)
|
|
98
|
+
# recieves an array of arrays or combinators
|
|
99
|
+
return Combinator.new(*sets) if sets.length > 1
|
|
100
|
+
return [] if sets.empty?
|
|
101
|
+
|
|
102
|
+
# recursively arrayifies each element in an array
|
|
103
|
+
set = sets.first
|
|
104
|
+
case set
|
|
105
|
+
when Array then set.collect {|s| [s]}
|
|
106
|
+
when nil then []
|
|
107
|
+
else
|
|
108
|
+
raise ArgumentError.new("sets must be arrays or nil")
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
require 'logger'
|
|
2
|
+
|
|
3
|
+
module Tap
|
|
4
|
+
module Support
|
|
5
|
+
# Logger provides methods to extend Logger objects.
|
|
6
|
+
#
|
|
7
|
+
# The output format is designed to look nice in a command prompt, but provide useful information
|
|
8
|
+
# if directed at a log file. The Logger output format is like:
|
|
9
|
+
#
|
|
10
|
+
# I[datetime] type message
|
|
11
|
+
#
|
|
12
|
+
# The first letter ('I' - INFO) indicates the severity of the message, next comes the datetime of the
|
|
13
|
+
# log, the type of log message, then the message itself. Specify the log type and message like:
|
|
14
|
+
#
|
|
15
|
+
# logger.type "message"
|
|
16
|
+
#
|
|
17
|
+
# Typed logging occurs through +method_missing+. Any type is possible so long as the logger doesn't
|
|
18
|
+
# already have a method of the input type.
|
|
19
|
+
module Logger
|
|
20
|
+
Format = " %s[%s] %18s %s\n"
|
|
21
|
+
|
|
22
|
+
def self.extended(base)
|
|
23
|
+
# On OS X (and maybe other systems), $stdout is not sync-ed.
|
|
24
|
+
# Set sync to true so that writes are immediately flushed
|
|
25
|
+
# to the console.
|
|
26
|
+
base.logdev.dev.sync = true if base.logdev.dev == $stdout
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
attr_writer :format
|
|
30
|
+
def format
|
|
31
|
+
(@format ||= nil) || Format
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Provides direct access to the log device
|
|
35
|
+
def logdev
|
|
36
|
+
@logdev
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def tick(mark=".")
|
|
40
|
+
@logdev.write mark
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def monitor(action="beginning", msg="", &block)
|
|
44
|
+
begin_time = Time.now
|
|
45
|
+
format_add(INFO, msg, action) {|format| format.chomp("\n")}
|
|
46
|
+
|
|
47
|
+
result = yield
|
|
48
|
+
|
|
49
|
+
format_add(INFO, "#{Time.now-begin_time}s", "completed") {|format| "\n" + format}
|
|
50
|
+
result
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def format_add(level, msg, action=nil, &block)
|
|
54
|
+
current_format = self.format
|
|
55
|
+
self.format = yield(current_format)
|
|
56
|
+
add(level, msg, action.to_s)
|
|
57
|
+
self.format = current_format
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Overrides the default format message to produce output like:
|
|
61
|
+
#
|
|
62
|
+
# I[H:M:S] type message
|
|
63
|
+
#
|
|
64
|
+
# If no progname is specified, '--' is used.
|
|
65
|
+
def format_message(severity, timestamp, progname, msg)
|
|
66
|
+
if timestamp.respond_to?(:strftime) && self.datetime_format
|
|
67
|
+
timestamp = timestamp.strftime(self.datetime_format)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
format % [severity[0..0], timestamp, progname || '--' , msg]
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Convenience method for creating section breaks in the output, formatted like:
|
|
74
|
+
#
|
|
75
|
+
# ----- message -----
|
|
76
|
+
def section_break(message)
|
|
77
|
+
@logdev.write(" ----- #{message} -----\n")
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
private
|
|
81
|
+
|
|
82
|
+
INFO = Object::Logger::INFO
|
|
83
|
+
|
|
84
|
+
# Specifies that any unknown method calls will be treated as logging calls where the method
|
|
85
|
+
# is the log type, and the first argument is the message. Types are underscored before logging.
|
|
86
|
+
def method_missing(method, *args, &block)
|
|
87
|
+
add(INFO, args.first, method.to_s.underscore, &block)
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
module Tap
|
|
2
|
+
module Support
|
|
3
|
+
|
|
4
|
+
# Defines the minimum methods to run (ie to queue and to execute)
|
|
5
|
+
# an object from Tap::App, aside from execute which must be
|
|
6
|
+
# provided by the object itself. Does not permit use in workflow
|
|
7
|
+
# methods (requires results, condition, and on_complete)
|
|
8
|
+
#
|
|
9
|
+
# == Advice
|
|
10
|
+
#
|
|
11
|
+
# If you ever want to multithread the object, execute should be
|
|
12
|
+
# limited to one thread at a time, for example by wrapping it in
|
|
13
|
+
# synchronize from the MonitorMixin. You will probably get away
|
|
14
|
+
# without doing so unless you're adding the object to the queue
|
|
15
|
+
# multiple times in succession -- but be on the lookout for
|
|
16
|
+
# unexpected results due to unsynchronized execution.
|
|
17
|
+
module Rap
|
|
18
|
+
attr_accessor :multithread
|
|
19
|
+
|
|
20
|
+
def multithread?
|
|
21
|
+
multithread
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def executable?(inputs)
|
|
25
|
+
true
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def batch
|
|
29
|
+
@batch ||= [self]
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def batched?
|
|
33
|
+
batch.length > 1
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
module Tap
|
|
2
|
+
module Support
|
|
3
|
+
class RunError < RuntimeError
|
|
4
|
+
attr_reader :original_error, :terminate_errors
|
|
5
|
+
|
|
6
|
+
def initialize(original_error, terminate_errors)
|
|
7
|
+
@original_error = original_error
|
|
8
|
+
@terminate_errors = terminate_errors
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def message
|
|
12
|
+
"#{original_error.class} #{original_error.message}"
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def backtrace
|
|
16
|
+
original_error.backtrace
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
require 'tap/support/combinator'
|
|
2
|
+
|
|
3
|
+
module Tap
|
|
4
|
+
module Support
|
|
5
|
+
|
|
6
|
+
# Template provides the default methods for making hash templates using Templater.
|
|
7
|
+
# Methods are provided to make variations of the template, as well as to pattern
|
|
8
|
+
# templates based on combinations of possible values.
|
|
9
|
+
#
|
|
10
|
+
module Template
|
|
11
|
+
attr_accessor :templater
|
|
12
|
+
|
|
13
|
+
# Provides a standard way of creating a variations of a template and takes an
|
|
14
|
+
# array of hashes with the specified variations. The template is treated as the
|
|
15
|
+
# default and variations are pushed (<<) to the templater. The template is
|
|
16
|
+
# cleared upon complete, so that it will be removed if used in the context of
|
|
17
|
+
# a Templater.
|
|
18
|
+
#
|
|
19
|
+
# t = {"default" => "default value"}.extend Template
|
|
20
|
+
# t.templater = []
|
|
21
|
+
# t.variations([
|
|
22
|
+
# {"default" => "non-default value"},
|
|
23
|
+
# {"another" => "value"}])
|
|
24
|
+
#
|
|
25
|
+
# t.templater
|
|
26
|
+
# # => [{"default" => "non-default value"},
|
|
27
|
+
# {"default" => "default value", "another" => "value"}]
|
|
28
|
+
#
|
|
29
|
+
def variations(array)
|
|
30
|
+
variations!(array)
|
|
31
|
+
self.clear
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Same as variations but does NOT clear the template (ie the template will
|
|
35
|
+
# be kept as specified, effectively acting as another variant). Note that
|
|
36
|
+
# variations! is called by the 'variations!!' key.
|
|
37
|
+
def variations!(array)
|
|
38
|
+
array.each { |var| templater << self.merge(var) }
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Provides a standard way of creating variations of a template based on all
|
|
42
|
+
# combinations of the keys in the input hash. The template itself is treated
|
|
43
|
+
# as the default and variations are pushed (<<) to the templater. The template
|
|
44
|
+
# is cleared upon complete, so that it will be removed if used in the context of
|
|
45
|
+
# a Templater.
|
|
46
|
+
#
|
|
47
|
+
# t = {"default" => "default value"}.extend Template
|
|
48
|
+
# t.templater = []
|
|
49
|
+
# t.combinations(
|
|
50
|
+
# "letter" => ["a", "b"],
|
|
51
|
+
# "number" => [1, 2])
|
|
52
|
+
#
|
|
53
|
+
# t.templater
|
|
54
|
+
# # => [{"default" => "default value", "letter" => "a", "number" => 1},
|
|
55
|
+
# {"default" => "default value", "letter" => "a", "number" => 2},
|
|
56
|
+
# {"default" => "default value", "letter" => "b", "number" => 1},
|
|
57
|
+
# {"default" => "default value", "letter" => "b", "number" => 2}]
|
|
58
|
+
def combinations(hash)
|
|
59
|
+
combinations!(hash)
|
|
60
|
+
self.clear
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Same as combinations but does NOT clear the template (ie the template will
|
|
64
|
+
# be kept as specified, effectively acting as another variant). Note that
|
|
65
|
+
# combinations! is called by the 'combinations!!' key.
|
|
66
|
+
def combinations!(hash)
|
|
67
|
+
keys, values = hash.to_a.transpose
|
|
68
|
+
values.collect! do |value|
|
|
69
|
+
value.kind_of?(Array) ? value : [value]
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
Combinator.new(*values).each do |c|
|
|
73
|
+
template = {}
|
|
74
|
+
keys.each_with_index {|key,i| template[key] = c[i] }
|
|
75
|
+
|
|
76
|
+
templater << self.merge(template)
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|