tap 0.18.0 → 0.19.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 +21 -0
- data/MIT-LICENSE +17 -15
- data/README +13 -30
- data/bin/tap +19 -24
- data/cmd/console.rb +1 -12
- data/cmd/manifest.rb +14 -19
- data/cmd/run.rb +96 -86
- data/doc/API +194 -54
- data/doc/Examples/Command Line +27 -1
- data/lib/tap.rb +2 -1
- data/lib/tap/app.rb +613 -166
- data/lib/tap/app/api.rb +115 -0
- data/lib/tap/app/queue.rb +36 -37
- data/lib/tap/app/state.rb +2 -1
- data/lib/tap/env.rb +454 -270
- data/lib/tap/env/constant.rb +83 -33
- data/lib/tap/env/context.rb +61 -0
- data/lib/tap/env/manifest.rb +140 -50
- data/lib/tap/env/minimap.rb +55 -39
- data/lib/tap/join.rb +71 -53
- data/lib/tap/joins/sync.rb +3 -1
- data/lib/tap/middleware.rb +4 -25
- data/lib/tap/middlewares/debugger.rb +75 -0
- data/lib/tap/parser.rb +268 -0
- data/lib/tap/prompt.rb +36 -0
- data/lib/tap/root.rb +3 -3
- data/lib/tap/signals.rb +26 -0
- data/lib/tap/signals/class_methods.rb +222 -0
- data/lib/tap/signals/help.rb +40 -0
- data/lib/tap/signals/module_methods.rb +20 -0
- data/lib/tap/signals/signal.rb +68 -0
- data/lib/tap/task.rb +28 -79
- data/lib/tap/tasks/dump.rb +6 -0
- data/lib/tap/tasks/load.rb +9 -37
- data/lib/tap/templater.rb +12 -1
- data/lib/tap/version.rb +1 -1
- metadata +22 -16
- data/doc/Class Reference +0 -330
- data/lib/tap/exe.rb +0 -130
- data/lib/tap/schema.rb +0 -374
- data/lib/tap/schema/parser.rb +0 -425
- data/lib/tap/schema/utils.rb +0 -56
data/doc/Class Reference
DELETED
@@ -1,330 +0,0 @@
|
|
1
|
-
= Class Reference
|
2
|
-
|
3
|
-
This is a ground-up overview of the main classes and modules used by Tap,
|
4
|
-
specifically Tasks, Apps, and Envs.
|
5
|
-
|
6
|
-
== Tasks
|
7
|
-
|
8
|
-
==== Tap::App::Node
|
9
|
-
|
10
|
-
http://tap.rubyforge.org/images/Node.png
|
11
|
-
|
12
|
-
Nodes are the building blocks of workflows. Nodes are essentially a method
|
13
|
-
with an array of joins to call when the method completes. Any object
|
14
|
-
responding to <tt>call</tt> may be turned into a node, so any method may be
|
15
|
-
used in a workflow.
|
16
|
-
|
17
|
-
Tasks are constructed so that <tt>call</tt> forwards arguments to
|
18
|
-
<tt>process</tt>. This allows hooks and callbacks to be inserted as needed in
|
19
|
-
subclasses.
|
20
|
-
|
21
|
-
==== {Configurable}[http://tap.rubyforge.org/configurable/]
|
22
|
-
|
23
|
-
http://tap.rubyforge.org/images/Configurable.png
|
24
|
-
|
25
|
-
Tap uses the {Configurable}[http://tap.rubyforge.org/configurable/] module to
|
26
|
-
declare class configurations and make them available on the command line.
|
27
|
-
Configurations provide a shorthand to define attributes with a default value.
|
28
|
-
For instance:
|
29
|
-
|
30
|
-
class ConfigClass
|
31
|
-
include Configurable
|
32
|
-
|
33
|
-
config :key, 'value' do |input|
|
34
|
-
input.upcase
|
35
|
-
end
|
36
|
-
|
37
|
-
def initialize
|
38
|
-
initialize_config
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
Is basically the same as:
|
43
|
-
|
44
|
-
class RegularClass
|
45
|
-
attr_reader :key
|
46
|
-
|
47
|
-
def key=(input)
|
48
|
-
@key = input.upcase
|
49
|
-
end
|
50
|
-
|
51
|
-
def initialize
|
52
|
-
self.key = 'value'
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
Configurations may be accessed through a hash-like config object, as you can
|
57
|
-
see here:
|
58
|
-
|
59
|
-
c = ConfigClass.new
|
60
|
-
c.key # => 'VALUE'
|
61
|
-
|
62
|
-
c.config[:key] = 'new value'
|
63
|
-
c.key # => 'NEW VALUE'
|
64
|
-
|
65
|
-
c.key = 'another value'
|
66
|
-
c.config[:key] # => 'ANOTHER VALUE'
|
67
|
-
|
68
|
-
This setup is both fast and convenient. See the
|
69
|
-
{Configurable}[http://tap.rubyforge.org/configurable] documentation for more
|
70
|
-
information.
|
71
|
-
|
72
|
-
==== {Configurable::Validation}[http://tap.rubyforge.org/configurable/classes/Configurable/Validation.html]
|
73
|
-
|
74
|
-
When configurations are set from the command line, the config writer
|
75
|
-
inevitably receives a string, even though you may want a non-string input. The
|
76
|
-
{Validation}[http://tap.rubyforge.org/configurable/classes/Configurable/Validation.html]
|
77
|
-
module provides standard blocks to validate and transform inputs. These blocks
|
78
|
-
may be accessed through the shortcut method <tt>c</tt> (ex: <tt>c.integer</tt>
|
79
|
-
or <tt>c.regexp</tt>). Validation blocks generally load string inputs as
|
80
|
-
YAML and validate that the result is the correct class; non-string inputs are
|
81
|
-
simply validated.
|
82
|
-
|
83
|
-
class ValidatingClass
|
84
|
-
include Configurable
|
85
|
-
|
86
|
-
config :int, 1, &c.integer # assures the input is an integer
|
87
|
-
config :int_or_nil, 1, &c.integer_or_nil # integer or nil only
|
88
|
-
config :array, [], &c.array # you get the idea
|
89
|
-
end
|
90
|
-
|
91
|
-
vc = ValidatingClass.new
|
92
|
-
|
93
|
-
vc.array = [:a, :b, :c]
|
94
|
-
vc.array # => [:a, :b, :c]
|
95
|
-
|
96
|
-
vc.array = "[1, 2, 3]"
|
97
|
-
vc.array # => [1, 2, 3]
|
98
|
-
|
99
|
-
vc.array = "string" # !> ValidationError
|
100
|
-
|
101
|
-
Validation blocks sometimes imply metadata. For instance <tt>c.flag</tt> makes
|
102
|
-
a config into a flag on the command line.
|
103
|
-
|
104
|
-
==== {Lazydoc}[http://tap.rubyforge.org/lazydoc]
|
105
|
-
|
106
|
-
{Lazydoc}[http://tap.rubyforge.org/lazydoc] fits into the space between live
|
107
|
-
code and code documentation. Lazydoc scans files to pull documentation into
|
108
|
-
the object space, and uses a syntax like this:
|
109
|
-
|
110
|
-
# Constant::key value
|
111
|
-
# comment
|
112
|
-
|
113
|
-
For example:
|
114
|
-
|
115
|
-
[lazydoc_file.rb]
|
116
|
-
# Const::Name::key value
|
117
|
-
#
|
118
|
-
# This is an extended,
|
119
|
-
# multiline comment.
|
120
|
-
#
|
121
|
-
|
122
|
-
require 'tap'
|
123
|
-
|
124
|
-
lazydoc = Lazydoc[__FILE__]
|
125
|
-
lazydoc.resolve
|
126
|
-
|
127
|
-
lazydoc['Const::Name']['key'].value # => "value"
|
128
|
-
lazydoc['Const::Name']['key'].comment # => "This is an extended, multiline comment."
|
129
|
-
|
130
|
-
Lazydoc can also register specific lines for documentation, such as method
|
131
|
-
definitions or configurations. Tap uses Lazydoc to identify files that contain
|
132
|
-
resources like tasks and joins, to extract config documentation, and to infer
|
133
|
-
the arguments a task receives from the command line. Here is an example of
|
134
|
-
information gleaned from a task definition:
|
135
|
-
|
136
|
-
# Sample::task a summary of the task
|
137
|
-
class Sample < Tap::Task
|
138
|
-
config :key, 'value' # a simple configuration
|
139
|
-
|
140
|
-
def process(a, b='B', *c)
|
141
|
-
end
|
142
|
-
end
|
143
|
-
|
144
|
-
Sample::desc.to_s # => "a summary of the task"
|
145
|
-
Sample::args.to_s # => "A B='B' C..."
|
146
|
-
|
147
|
-
key = Sample.configurations[:key]
|
148
|
-
key.attributes[:desc].to_s # => "a simple configuration"
|
149
|
-
|
150
|
-
See the {Lazydoc}[http://tap.rubyforge.org/lazydoc] documentation for more
|
151
|
-
information.
|
152
|
-
|
153
|
-
=== Tap::Task
|
154
|
-
|
155
|
-
http://tap.rubyforge.org/images/Task.png
|
156
|
-
|
157
|
-
Tasks are the bread and butter of Tap. Tasks serve as nodes in workflows and
|
158
|
-
map to the command line as miniature applications. Tasks are also useful as
|
159
|
-
ordinary objects.
|
160
|
-
|
161
|
-
Tasks may be dynamically generated on an app using the <tt>task</tt> method. This provides a quick way of sketching out a workflow.
|
162
|
-
|
163
|
-
app = Tap::App.instance
|
164
|
-
t = app.task {|task| 1 + 2 }
|
165
|
-
t.process # => 3
|
166
|
-
|
167
|
-
t = app.task {|task, x, y| x + y }
|
168
|
-
t.process(1, 2) # => 3
|
169
|
-
|
170
|
-
Tasks can be configured and joined into workflows.
|
171
|
-
|
172
|
-
runlist = []
|
173
|
-
results = []
|
174
|
-
|
175
|
-
t1 = app.task(:key => 'one') do |task, input|
|
176
|
-
runlist << task
|
177
|
-
"#{input}:#{task.config[:key]}"
|
178
|
-
end
|
179
|
-
|
180
|
-
t2 = app.task do |task, input|
|
181
|
-
runlist << task
|
182
|
-
"#{input}:two"
|
183
|
-
end
|
184
|
-
|
185
|
-
t2.on_complete do |result|
|
186
|
-
results << result
|
187
|
-
end
|
188
|
-
|
189
|
-
t1.sequence(t2)
|
190
|
-
|
191
|
-
Results may be collected by the underlying Tap::App.
|
192
|
-
|
193
|
-
app.enq(t1)
|
194
|
-
app.run
|
195
|
-
|
196
|
-
runlist # => [t1, t2]
|
197
|
-
results # => ["input:one:two"]
|
198
|
-
|
199
|
-
== Apps
|
200
|
-
|
201
|
-
==== Tap::App::Queue
|
202
|
-
|
203
|
-
http://tap.rubyforge.org/images/Queue.png
|
204
|
-
|
205
|
-
Apps coordinate the execution of nodes through a queue. The queue is just a
|
206
|
-
stack of nodes and inputs; during a run the nodes are sequentially executed
|
207
|
-
with the inputs.
|
208
|
-
|
209
|
-
=== Tap::App
|
210
|
-
|
211
|
-
http://tap.rubyforge.org/images/App.png
|
212
|
-
|
213
|
-
Instances of Tap::App coordinate the execution of nodes.
|
214
|
-
|
215
|
-
Task initialization requires an App, which is by default Tap::App.instance.
|
216
|
-
Tasks use their app for logging, dependency-resolution, checks, and to enque
|
217
|
-
themselves.
|
218
|
-
|
219
|
-
results = []
|
220
|
-
app = Tap::App.new {|result| results << result }
|
221
|
-
t = app.task {|task, *inputs| inputs }
|
222
|
-
t.enq(1)
|
223
|
-
t.enq(2,3)
|
224
|
-
|
225
|
-
app.queue.to_a # => [[t, [1]], [t, [2,3]]]
|
226
|
-
app.run
|
227
|
-
results # => [[1], [2,3]]
|
228
|
-
|
229
|
-
== Envs
|
230
|
-
|
231
|
-
==== Tap::Root
|
232
|
-
|
233
|
-
http://tap.rubyforge.org/images/Root.png
|
234
|
-
|
235
|
-
A Root represents the base of a directory structure. Roots allow you to alias
|
236
|
-
relative paths, basically allowing you to develop code for a conceptual
|
237
|
-
directory structure that can be defined later.
|
238
|
-
|
239
|
-
root = Tap::Root.new '/path/to/root'
|
240
|
-
root.root # => '/path/to/root'
|
241
|
-
root['config'] # => '/path/to/root/config'
|
242
|
-
root.path('config', 'sample.yml') # => '/path/to/root/config/sample.yml'
|
243
|
-
|
244
|
-
While simple, this ability to alias paths is useful, powerful, and forms the
|
245
|
-
basis of the Tap environment.
|
246
|
-
|
247
|
-
==== Tap::Env
|
248
|
-
|
249
|
-
http://tap.rubyforge.org/images/Env.png
|
250
|
-
|
251
|
-
Envs generate manifests of various resources (tasks, generators, etc) and
|
252
|
-
provide methods to uniquely identify resources using minimized base paths.
|
253
|
-
In this directory structure:
|
254
|
-
|
255
|
-
path
|
256
|
-
`- to
|
257
|
-
|- another
|
258
|
-
| `- file.rb
|
259
|
-
|- file-0.1.0.rb
|
260
|
-
|- file-0.2.0.rb
|
261
|
-
`- file.rb
|
262
|
-
|
263
|
-
The minimal paths that uniquely identify these files are (respectively):
|
264
|
-
|
265
|
-
'another/file'
|
266
|
-
'file-0.1.0'
|
267
|
-
'file-0.2.0'
|
268
|
-
'file.rb'
|
269
|
-
|
270
|
-
Envs facilitate mapping a minimal path to an actual path, and hence to a
|
271
|
-
resource. Envs can be nested so that manifests span multiple directories.
|
272
|
-
Indeed, this is how tap accesses tasks and generators within gems; the gem
|
273
|
-
directories are initialized as Envs and nested within the Env for the working
|
274
|
-
directory.
|
275
|
-
|
276
|
-
http://tap.rubyforge.org/images/Nested-Env.png
|
277
|
-
|
278
|
-
To prevent conflicts between similarly-named resources under different Envs,
|
279
|
-
Env allows selection of Envs, also by minimized paths. Say you installed the
|
280
|
-
'tap-tasks' gem.
|
281
|
-
|
282
|
-
% tap manifest
|
283
|
-
--------------------------------------------------------------------------------
|
284
|
-
Desktop: (/Users/username/Desktop)
|
285
|
-
--------------------------------------------------------------------------------
|
286
|
-
tap-tasks: (/Library/Ruby/Gems/1.8/gems/tap-tasks-0.1.0)
|
287
|
-
tasks
|
288
|
-
argv (lib/tap/tasks/argv.rb)
|
289
|
-
inspect (lib/tap/tasks/dump/inspect.rb)
|
290
|
-
dump/yaml (lib/tap/tasks/dump/yaml.rb)
|
291
|
-
load/yaml (lib/tap/tasks/load/yaml.rb)
|
292
|
-
--------------------------------------------------------------------------------
|
293
|
-
tap: (/Library/Ruby/Gems/1.8/gems/tap-0.12.4)
|
294
|
-
commands
|
295
|
-
console (cmd/console.rb)
|
296
|
-
destroy (cmd/destroy.rb)
|
297
|
-
generate (cmd/generate.rb)
|
298
|
-
manifest (cmd/manifest.rb)
|
299
|
-
run (cmd/run.rb)
|
300
|
-
generators
|
301
|
-
command (lib/tap/generator/generators/command/command_generator.rb)
|
302
|
-
config (lib/tap/generator/generators/config/config_generator.rb)
|
303
|
-
generator (lib/tap/generator/generators/generator/generator_generator.rb)
|
304
|
-
root (lib/tap/generator/generators/root/root_generator.rb)
|
305
|
-
task (lib/tap/generator/generators/task/task_generator.rb)
|
306
|
-
tasks
|
307
|
-
dump (lib/tap/dump.rb)
|
308
|
-
load (lib/tap/load.rb)
|
309
|
-
--------------------------------------------------------------------------------
|
310
|
-
|
311
|
-
Desktop
|
312
|
-
|- tap-tasks
|
313
|
-
`- tap
|
314
|
-
|
315
|
-
In this printout of the manifest, you can see the resources available to tap
|
316
|
-
on the Desktop (none), in the tap-tasks gem, and in tap itself. In most cases
|
317
|
-
the minipath of any of the tasks is sufficient for identification:
|
318
|
-
|
319
|
-
% tap run -- load --: dump/yaml
|
320
|
-
|
321
|
-
If there were a conflict, you'd have to specify the environment minipath like:
|
322
|
-
|
323
|
-
% tap run -- tap:load --: tap-tasks:dump/yaml
|
324
|
-
|
325
|
-
==== Tap::Exe
|
326
|
-
|
327
|
-
http://tap.rubyforge.org/images/Run-Env.png
|
328
|
-
|
329
|
-
Tap::Exe adds methods for building and executing workflows from command line
|
330
|
-
inputs.
|
data/lib/tap/exe.rb
DELETED
@@ -1,130 +0,0 @@
|
|
1
|
-
require 'tap/env'
|
2
|
-
require 'tap/task'
|
3
|
-
require 'tap/schema'
|
4
|
-
|
5
|
-
module Tap
|
6
|
-
module Exe
|
7
|
-
|
8
|
-
# Adapted from Gem.find_home
|
9
|
-
# def self.user_home
|
10
|
-
# ['HOME', 'USERPROFILE'].each do |homekey|
|
11
|
-
# return ENV[homekey] if ENV[homekey]
|
12
|
-
# end
|
13
|
-
#
|
14
|
-
# if ENV['HOMEDRIVE'] && ENV['HOMEPATH'] then
|
15
|
-
# return "#{ENV['HOMEDRIVE']}#{ENV['HOMEPATH']}"
|
16
|
-
# end
|
17
|
-
#
|
18
|
-
# begin
|
19
|
-
# File.expand_path("~")
|
20
|
-
# rescue
|
21
|
-
# File::ALT_SEPARATOR ? "C:/" : "/"
|
22
|
-
# end
|
23
|
-
# end
|
24
|
-
|
25
|
-
# Setup an execution environment.
|
26
|
-
def self.setup(options={}, argv=ARGV, env=ENV)
|
27
|
-
if argv[-1] == "-d-"
|
28
|
-
argv.pop
|
29
|
-
$DEBUG = true
|
30
|
-
end
|
31
|
-
|
32
|
-
options = {
|
33
|
-
:dir => Dir.pwd,
|
34
|
-
:config_file => CONFIG_FILE
|
35
|
-
}.merge(options)
|
36
|
-
|
37
|
-
# load configurations
|
38
|
-
dir = options.delete(:dir)
|
39
|
-
config_file = options.delete(:config_file)
|
40
|
-
user_config_file = config_file ? File.join(dir, config_file) : nil
|
41
|
-
|
42
|
-
user = Env.load_config(user_config_file)
|
43
|
-
global = {}
|
44
|
-
env.each_pair do |key, value|
|
45
|
-
if key =~ /\ATAP_(.*)\z/
|
46
|
-
global[$1.downcase] = value
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
config = {
|
51
|
-
'root' => dir,
|
52
|
-
'gems' => :all
|
53
|
-
}.merge(global).merge(user).merge(options)
|
54
|
-
|
55
|
-
# keys must be symbolize as they are immediately
|
56
|
-
# used to initialize the Env configs
|
57
|
-
config = config.inject({}) do |options, (key, value)|
|
58
|
-
options[key.to_sym || key] = value
|
59
|
-
options
|
60
|
-
end
|
61
|
-
|
62
|
-
# instantiate
|
63
|
-
exe = Env.new(config, :basename => config_file).extend(Exe)
|
64
|
-
|
65
|
-
exe.register('command') do |env|
|
66
|
-
env.root.glob(:cmd, "**/*.rb")
|
67
|
-
end
|
68
|
-
|
69
|
-
# add the tap env if necessary
|
70
|
-
unless exe.any? {|env| env.root.root == TAP_HOME }
|
71
|
-
exe.push Env.new(TAP_HOME, exe.context)
|
72
|
-
end
|
73
|
-
|
74
|
-
exe
|
75
|
-
end
|
76
|
-
|
77
|
-
# The config file path
|
78
|
-
CONFIG_FILE = "tap.yml"
|
79
|
-
|
80
|
-
# The home directory for Tap
|
81
|
-
TAP_HOME = File.expand_path("#{File.dirname(__FILE__)}/../..")
|
82
|
-
|
83
|
-
def launch(argv=ARGV)
|
84
|
-
case command = argv.shift.to_s
|
85
|
-
when '', '--help'
|
86
|
-
yield
|
87
|
-
else
|
88
|
-
if path = seek('command', command)
|
89
|
-
load path # run the command, if it exists
|
90
|
-
else
|
91
|
-
puts "Unknown command: '#{command}'"
|
92
|
-
puts "Type 'tap --help' for usage information."
|
93
|
-
end
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
97
|
-
def self.set_signals(app)
|
98
|
-
# info signal -- Note: some systems do
|
99
|
-
# not support the INFO signal
|
100
|
-
# (windows, fedora, at least)
|
101
|
-
signals = Signal.list.keys
|
102
|
-
Signal.trap("INFO") do
|
103
|
-
puts app.info
|
104
|
-
end if signals.include?("INFO")
|
105
|
-
|
106
|
-
# interuption signal
|
107
|
-
Signal.trap("INT") do
|
108
|
-
puts " interrupted!"
|
109
|
-
# prompt for decision
|
110
|
-
while true
|
111
|
-
print "stop, terminate, exit, or resume? (s/t/e/r):"
|
112
|
-
case gets.strip
|
113
|
-
when /s(top)?/i
|
114
|
-
app.stop
|
115
|
-
break
|
116
|
-
when /t(erminate)?/i
|
117
|
-
app.terminate
|
118
|
-
break
|
119
|
-
when /e(xit)?/i
|
120
|
-
exit
|
121
|
-
when /r(esume)?/i
|
122
|
-
break
|
123
|
-
else
|
124
|
-
puts "unexpected response..."
|
125
|
-
end
|
126
|
-
end
|
127
|
-
end if signals.include?("INT")
|
128
|
-
end
|
129
|
-
end
|
130
|
-
end
|