tap 0.19.0 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History +100 -45
- data/MIT-LICENSE +1 -1
- data/README +95 -51
- data/bin/tap +11 -57
- data/bin/tapexe +84 -0
- data/doc/API +91 -139
- data/doc/Configuration +93 -0
- data/doc/Examples/Command Line +10 -42
- data/doc/Examples/Tapfile +124 -0
- data/doc/Ruby to Ruby +87 -0
- data/doc/Workflow Syntax +185 -0
- data/lib/tap.rb +74 -5
- data/lib/tap/app.rb +217 -310
- data/lib/tap/app/api.rb +44 -23
- data/lib/tap/app/queue.rb +11 -12
- data/lib/tap/app/stack.rb +4 -4
- data/lib/tap/declarations.rb +200 -0
- data/lib/tap/declarations/context.rb +31 -0
- data/lib/tap/declarations/description.rb +33 -0
- data/lib/tap/env.rb +133 -779
- data/lib/tap/env/cache.rb +87 -0
- data/lib/tap/env/constant.rb +94 -39
- data/lib/tap/env/path.rb +71 -0
- data/lib/tap/join.rb +42 -78
- data/lib/tap/joins/gate.rb +85 -0
- data/lib/tap/joins/switch.rb +4 -2
- data/lib/tap/joins/sync.rb +3 -3
- data/lib/tap/middleware.rb +5 -5
- data/lib/tap/middlewares/debugger.rb +18 -58
- data/lib/tap/parser.rb +115 -183
- data/lib/tap/root.rb +162 -239
- data/lib/tap/signal.rb +72 -0
- data/lib/tap/signals.rb +20 -2
- data/lib/tap/signals/class_methods.rb +38 -43
- data/lib/tap/signals/configure.rb +19 -0
- data/lib/tap/signals/help.rb +5 -7
- data/lib/tap/signals/load.rb +49 -0
- data/lib/tap/signals/module_methods.rb +1 -0
- data/lib/tap/task.rb +46 -275
- data/lib/tap/tasks/dump.rb +21 -16
- data/lib/tap/tasks/list.rb +184 -0
- data/lib/tap/tasks/load.rb +4 -4
- data/lib/tap/tasks/prompt.rb +128 -0
- data/lib/tap/tasks/signal.rb +42 -0
- data/lib/tap/tasks/singleton.rb +35 -0
- data/lib/tap/tasks/stream.rb +64 -0
- data/lib/tap/utils.rb +83 -0
- data/lib/tap/version.rb +2 -2
- data/lib/tap/workflow.rb +124 -0
- data/tap.yml +0 -0
- metadata +59 -24
- data/cmd/console.rb +0 -43
- data/cmd/manifest.rb +0 -118
- data/cmd/run.rb +0 -145
- data/doc/Examples/Workflow +0 -40
- data/lib/tap/app/node.rb +0 -29
- data/lib/tap/env/context.rb +0 -61
- data/lib/tap/env/gems.rb +0 -63
- data/lib/tap/env/manifest.rb +0 -179
- data/lib/tap/env/minimap.rb +0 -308
- data/lib/tap/intern.rb +0 -50
- data/lib/tap/joins.rb +0 -9
- data/lib/tap/prompt.rb +0 -36
- data/lib/tap/root/utils.rb +0 -220
- data/lib/tap/root/versions.rb +0 -138
- data/lib/tap/signals/signal.rb +0 -68
data/lib/tap/app/api.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
|
-
require '
|
2
|
-
require 'tap/signals/help'
|
1
|
+
require 'tap/app'
|
3
2
|
|
4
3
|
module Tap
|
5
4
|
class App
|
@@ -30,7 +29,7 @@ module Tap
|
|
30
29
|
#
|
31
30
|
# The parse method uses parser by default, so subclasses can simply
|
32
31
|
# modify parser and ensure parse still works correctly.
|
33
|
-
def parser
|
32
|
+
def parser(app)
|
34
33
|
opts = ConfigParser.new
|
35
34
|
|
36
35
|
unless configurations.empty?
|
@@ -42,34 +41,44 @@ module Tap
|
|
42
41
|
opts.separator "options:"
|
43
42
|
|
44
43
|
# add option to print help
|
45
|
-
opts.on("
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
44
|
+
opts.on("--help", "Print this help") do
|
45
|
+
lines = ["#{self}#{desc.empty? ? '' : ' -- '}#{desc.to_s}"]
|
46
|
+
lines << help
|
47
|
+
lines << "usage: tap #{to_s.underscore} #{respond_to?(:args) ? args : nil}"
|
48
|
+
lines << nil
|
49
|
+
lines << opts
|
50
|
+
raise lines.join("\n")
|
50
51
|
end
|
51
52
|
|
52
53
|
opts
|
53
54
|
end
|
54
55
|
|
56
|
+
def parse(argv=ARGV, app=Tap::App.current, &block)
|
57
|
+
parse!(argv.dup, app, &block)
|
58
|
+
end
|
59
|
+
|
55
60
|
# Parses the argv into an instance of self. Internally parse parses
|
56
61
|
# an argh then calls build, but there is no requirement that this
|
57
62
|
# occurs in subclasses.
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
argv = parser.parse!(argv, :add_defaults => false)
|
63
|
+
#
|
64
|
+
# Returns the instance. If a block is given, the instance and any
|
65
|
+
# remaining arguments will be yielded to it.
|
66
|
+
def parse!(argv=ARGV, app=Tap::App.current)
|
67
|
+
parser = self.parser(app)
|
68
|
+
args = parser.parse!(argv, :add_defaults => false)
|
69
|
+
obj = build(convert_to_spec(parser, args), app)
|
66
70
|
|
67
|
-
|
71
|
+
if block_given?
|
72
|
+
yield(obj, args)
|
73
|
+
else
|
74
|
+
parser.warn_ignored_args(args)
|
75
|
+
obj
|
76
|
+
end
|
68
77
|
end
|
69
|
-
|
78
|
+
|
70
79
|
# Returns an instance of self. By default build calls new with the
|
71
80
|
# configurations specified by spec['config'], and app.
|
72
|
-
def build(spec={}, app=Tap::App.
|
81
|
+
def build(spec={}, app=Tap::App.current)
|
73
82
|
new(spec['config'] || {}, app)
|
74
83
|
end
|
75
84
|
|
@@ -85,17 +94,23 @@ module Tap
|
|
85
94
|
|
86
95
|
lines.join("\n")
|
87
96
|
end
|
97
|
+
|
98
|
+
protected
|
99
|
+
|
100
|
+
def convert_to_spec(parser, args)
|
101
|
+
{'config' => parser.nested_config}
|
102
|
+
end
|
88
103
|
end
|
89
104
|
|
90
105
|
include Configurable
|
91
106
|
include Signals
|
92
107
|
|
93
|
-
|
108
|
+
define_signal :help, Help # signals help
|
94
109
|
|
95
|
-
# The
|
110
|
+
# The app for self
|
96
111
|
attr_reader :app
|
97
112
|
|
98
|
-
def initialize(config={}, app=Tap::App.
|
113
|
+
def initialize(config={}, app=Tap::App.current)
|
99
114
|
@app = app
|
100
115
|
initialize_config(config)
|
101
116
|
end
|
@@ -108,7 +123,13 @@ module Tap
|
|
108
123
|
# config is a stringified representation of the configurations for self.
|
109
124
|
def to_spec
|
110
125
|
config = self.config.to_hash {|hash, key, value| hash[key.to_s] = value }
|
111
|
-
{'config' => config}
|
126
|
+
config.empty? ? {} : {'config' => config}
|
127
|
+
end
|
128
|
+
|
129
|
+
# Provides an abbreviated version of the default inspect, with only the
|
130
|
+
# class, object_id, and configurations listed.
|
131
|
+
def inspect
|
132
|
+
"#<#{self.class.to_s}:#{object_id} #{config.to_hash.inspect} >"
|
112
133
|
end
|
113
134
|
end
|
114
135
|
end
|
data/lib/tap/app/queue.rb
CHANGED
@@ -3,16 +3,16 @@ require 'monitor'
|
|
3
3
|
module Tap
|
4
4
|
class App
|
5
5
|
|
6
|
-
# Queue allows thread-safe enqueing and dequeing of
|
6
|
+
# Queue allows thread-safe enqueing and dequeing of tasks and inputs for
|
7
7
|
# execution.
|
8
8
|
#
|
9
9
|
# === API
|
10
10
|
#
|
11
11
|
# The following methods are required in alternative implementations of an
|
12
|
-
# applicaton queue, where a job is a [
|
12
|
+
# applicaton queue, where a job is a [task, input] array:
|
13
13
|
#
|
14
|
-
# enq(
|
15
|
-
# unshift(
|
14
|
+
# enq(task, input) # pushes the job onto the queue
|
15
|
+
# unshift(task, input) # unshifts the job onto the queue
|
16
16
|
# deq # shifts a job off the queue
|
17
17
|
# size # returns the number of jobs in the queue
|
18
18
|
# clear # clears the queue, returns current jobs
|
@@ -29,22 +29,21 @@ module Tap
|
|
29
29
|
@queue = []
|
30
30
|
end
|
31
31
|
|
32
|
-
# Enqueues the
|
33
|
-
def enq(
|
32
|
+
# Enqueues the task and input.
|
33
|
+
def enq(task, input)
|
34
34
|
synchronize do
|
35
|
-
@queue.push [
|
35
|
+
@queue.push [task, input]
|
36
36
|
end
|
37
37
|
end
|
38
38
|
|
39
|
-
# Enqueues the
|
40
|
-
def unshift(
|
39
|
+
# Enqueues the task and input, but to the top of the queue.
|
40
|
+
def unshift(task, input)
|
41
41
|
synchronize do
|
42
|
-
@queue.unshift [
|
42
|
+
@queue.unshift [task, input]
|
43
43
|
end
|
44
44
|
end
|
45
45
|
|
46
|
-
# Dequeues the next job
|
47
|
-
# the queue is empty.
|
46
|
+
# Dequeues the next job. Returns nil if the queue is empty.
|
48
47
|
def deq
|
49
48
|
synchronize { @queue.shift }
|
50
49
|
end
|
data/lib/tap/app/stack.rb
CHANGED
@@ -11,13 +11,13 @@ module Tap
|
|
11
11
|
@app = app
|
12
12
|
end
|
13
13
|
|
14
|
-
# Checks app for termination and then calls the
|
14
|
+
# Checks app for termination and then calls the task with the input:
|
15
15
|
#
|
16
|
-
#
|
16
|
+
# task.call(input)
|
17
17
|
#
|
18
|
-
def call(
|
18
|
+
def call(task, input)
|
19
19
|
app.check_terminate
|
20
|
-
|
20
|
+
task.call(input)
|
21
21
|
end
|
22
22
|
end
|
23
23
|
end
|
@@ -0,0 +1,200 @@
|
|
1
|
+
require 'tap/join'
|
2
|
+
require 'tap/workflow'
|
3
|
+
require 'tap/declarations/description'
|
4
|
+
require 'tap/declarations/context'
|
5
|
+
require 'tap/parser'
|
6
|
+
require 'tap/tasks/singleton'
|
7
|
+
|
8
|
+
module Tap
|
9
|
+
module Declarations
|
10
|
+
def env
|
11
|
+
app.env
|
12
|
+
end
|
13
|
+
|
14
|
+
# Returns a new node that executes block on call.
|
15
|
+
def node(var=nil, &node) # :yields: *args
|
16
|
+
def node.joins; @joins ||= []; end
|
17
|
+
app.set(var, node) if var
|
18
|
+
node
|
19
|
+
end
|
20
|
+
|
21
|
+
# Generates a join between the inputs and outputs. Join resolves the
|
22
|
+
# class using env and initializes a new instance with the configs and
|
23
|
+
# self.
|
24
|
+
def join(inputs, outputs, config={}, clas=Tap::Join, &block)
|
25
|
+
inputs = [inputs] unless inputs.kind_of?(Array)
|
26
|
+
outputs = [outputs] unless outputs.kind_of?(Array)
|
27
|
+
|
28
|
+
obj = app.init(clas, config, app)
|
29
|
+
obj.join(inputs, outputs, &block)
|
30
|
+
obj
|
31
|
+
end
|
32
|
+
|
33
|
+
# Sets the description for use by the next task declaration.
|
34
|
+
def desc(str)
|
35
|
+
@desc = Lazydoc.register_caller(Description)
|
36
|
+
@desc.desc = str
|
37
|
+
@desc
|
38
|
+
end
|
39
|
+
|
40
|
+
def singleton(&block)
|
41
|
+
baseclass(Tap::Tasks::Singleton, &block)
|
42
|
+
end
|
43
|
+
|
44
|
+
def baseclass(baseclass=Tap::Task)
|
45
|
+
current = @baseclass
|
46
|
+
begin
|
47
|
+
@baseclass = env.constant(baseclass) unless baseclass.nil?
|
48
|
+
yield if block_given?
|
49
|
+
ensure
|
50
|
+
@baseclass = current if block_given?
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# Nests tasks within the named module for the duration of the block.
|
55
|
+
# Namespaces may be nested.
|
56
|
+
def namespace(namespace)
|
57
|
+
current = @namespace
|
58
|
+
begin
|
59
|
+
unless namespace.nil? || namespace.kind_of?(Module)
|
60
|
+
const_name = namespace.to_s.camelize
|
61
|
+
unless current.const_defined?(const_name)
|
62
|
+
current.const_set(const_name, Module.new)
|
63
|
+
end
|
64
|
+
namespace = current.const_get(const_name)
|
65
|
+
end
|
66
|
+
|
67
|
+
@namespace = namespace unless namespace.nil?
|
68
|
+
yield if block_given?
|
69
|
+
ensure
|
70
|
+
@namespace = current if block_given?
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def declare(baseclass, const_name, configs={}, &block)
|
75
|
+
const_name = const_name.to_s.camelize
|
76
|
+
subclass = Class.new(env.constant(baseclass))
|
77
|
+
@namespace.const_set(const_name, subclass)
|
78
|
+
|
79
|
+
# define configs
|
80
|
+
configs.each_pair do |key, value|
|
81
|
+
# specifying a desc prevents lazydoc registration of these lines
|
82
|
+
opts = {:desc => ""}
|
83
|
+
opts[:short] = key if key.to_s.length == 1
|
84
|
+
config_block = Configurable::Validation.guess(value)
|
85
|
+
subclass.send(:config, key, value, opts, &config_block)
|
86
|
+
end
|
87
|
+
|
88
|
+
# define process
|
89
|
+
if block
|
90
|
+
# determine arity, correcting for the self arg
|
91
|
+
arity = block.arity
|
92
|
+
arity -= arity > 0 ? 1 : -1
|
93
|
+
signature = Array.new(arity < 0 ? arity.abs - 1 : arity, 'arg')
|
94
|
+
signature << '*args' if arity < 0
|
95
|
+
|
96
|
+
# prevents assessment of process args by lazydoc
|
97
|
+
subclass.const_attrs[:process] = signature.join(' ')
|
98
|
+
subclass.send(:define_method, :process) do |*args|
|
99
|
+
block.call(self, *args)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
# register documentation
|
104
|
+
constant = env.set(subclass, nil)
|
105
|
+
|
106
|
+
if @desc
|
107
|
+
subclass.desc = @desc
|
108
|
+
constant.register_as(subclass.type, @desc)
|
109
|
+
@desc = nil
|
110
|
+
end
|
111
|
+
|
112
|
+
subclass
|
113
|
+
end
|
114
|
+
|
115
|
+
def task(const_name, configs={}, baseclass=@baseclass, &block)
|
116
|
+
@desc ||= Lazydoc.register_caller(Description)
|
117
|
+
const_name, prerequisites = parse_prerequisites(const_name)
|
118
|
+
|
119
|
+
if prerequisites.nil?
|
120
|
+
return declare(baseclass, const_name, configs, &block)
|
121
|
+
end
|
122
|
+
|
123
|
+
tasc = declare(Tap::Workflow, const_name, configs) do |workflow|
|
124
|
+
psr = Parser.new
|
125
|
+
args = psr.parse!(prerequisites)
|
126
|
+
warn "ignoring args: #{args.inspect}" unless args.empty?
|
127
|
+
psr.build_to(app)
|
128
|
+
|
129
|
+
obj = init("#{const_name.to_s.underscore}/task", workflow.config.to_hash)
|
130
|
+
setup = lambda {|input| exe(obj, input) }
|
131
|
+
|
132
|
+
[setup, obj]
|
133
|
+
end
|
134
|
+
|
135
|
+
namespace(const_name) do
|
136
|
+
declare(baseclass, 'Task', configs, &block)
|
137
|
+
end
|
138
|
+
|
139
|
+
tasc
|
140
|
+
end
|
141
|
+
|
142
|
+
def work(const_name, definition, configs={}, baseclass=Tap::Workflow, &block)
|
143
|
+
unless definition.kind_of?(String)
|
144
|
+
raise "workflow definition must be a string: #{definition.inspect}"
|
145
|
+
end
|
146
|
+
|
147
|
+
@desc ||= Lazydoc.register_caller(Description)
|
148
|
+
block ||= lambda {|config| node(0) }
|
149
|
+
task({const_name => definition}, configs, baseclass, &block)
|
150
|
+
end
|
151
|
+
|
152
|
+
protected
|
153
|
+
|
154
|
+
def initialize_declare(baseclass=Tap::Task, namespace=Object)
|
155
|
+
@desc = nil
|
156
|
+
@baseclass = baseclass
|
157
|
+
@namespace = namespace
|
158
|
+
end
|
159
|
+
|
160
|
+
private
|
161
|
+
|
162
|
+
def parse_prerequisites(const_name)
|
163
|
+
prerequisites = nil
|
164
|
+
|
165
|
+
if const_name.is_a?(Hash)
|
166
|
+
hash = const_name
|
167
|
+
case hash.length
|
168
|
+
when 0
|
169
|
+
const_name = nil
|
170
|
+
when 1
|
171
|
+
const_name = hash.keys[0]
|
172
|
+
prerequisites = hash[const_name]
|
173
|
+
else
|
174
|
+
raise ArgumentError, "multiple task names specified: #{hash.keys.inspect}"
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
if const_name.nil?
|
179
|
+
raise ArgumentError, "no constant name specified"
|
180
|
+
end
|
181
|
+
|
182
|
+
case prerequisites
|
183
|
+
when nil
|
184
|
+
when String
|
185
|
+
prerequisites = Utils.shellsplit(prerequisites)
|
186
|
+
when Array
|
187
|
+
argv = []
|
188
|
+
prerequisites.each do |prereq|
|
189
|
+
argv << '-!'
|
190
|
+
argv << prereq.to_s
|
191
|
+
end
|
192
|
+
prerequisites = argv
|
193
|
+
else
|
194
|
+
prerequisites = ['-!', prerequisites.to_s]
|
195
|
+
end
|
196
|
+
|
197
|
+
[const_name, prerequisites]
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Tap
|
2
|
+
module Declarations
|
3
|
+
class Context
|
4
|
+
include Declarations
|
5
|
+
include Tap::Utils
|
6
|
+
|
7
|
+
attr_reader :app
|
8
|
+
|
9
|
+
def initialize(app, ns=nil)
|
10
|
+
@app = app
|
11
|
+
initialize_declare
|
12
|
+
namespace(ns)
|
13
|
+
end
|
14
|
+
|
15
|
+
# Runs the command with system and raises an error if the command
|
16
|
+
# fails.
|
17
|
+
def sh(*cmd)
|
18
|
+
app.log :sh, cmd.join(' ')
|
19
|
+
system(*cmd) or raise "Command failed with status (#{$?.exitstatus}): [#{cmd.join(' ')}]"
|
20
|
+
end
|
21
|
+
|
22
|
+
def node(num)
|
23
|
+
app.get(num.to_s)
|
24
|
+
end
|
25
|
+
|
26
|
+
def method_missing(sym, *args, &block)
|
27
|
+
app.send(sym, *args, &block)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Tap
|
2
|
+
module Declarations
|
3
|
+
# :startdoc:::-
|
4
|
+
# A special type of Lazydoc::Comment designed to handle the comment syntax
|
5
|
+
# for task declarations.
|
6
|
+
#
|
7
|
+
# Description instances can be assigned a description, or they may parse
|
8
|
+
# one directly from the comment. Comment lines with the constant attribute
|
9
|
+
# '::' will have the value set as desc.
|
10
|
+
# :startdoc:::+
|
11
|
+
class Description < Lazydoc::Comment
|
12
|
+
|
13
|
+
# The description for self.
|
14
|
+
attr_accessor :desc
|
15
|
+
|
16
|
+
# Parses in-comment descriptions from prepended lines, if present.
|
17
|
+
def prepend(line)
|
18
|
+
if line =~ /\s::(?:\s+(.*?)\s*)?$/
|
19
|
+
self.desc = $1.to_s
|
20
|
+
false
|
21
|
+
else
|
22
|
+
super
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Resolves and returns the description.
|
27
|
+
def to_s
|
28
|
+
resolve
|
29
|
+
desc.to_s
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|