bahuvrihi-tap 0.10.2 → 0.10.3
Sign up to get free protection for your applications and to get access to all the features.
- data/cgi/run.rb +36 -0
- data/cmd/run.rb +4 -3
- data/cmd/server.rb +36 -0
- data/lib/tap/app.rb +1 -1
- data/lib/tap/constants.rb +1 -1
- data/lib/tap/env.rb +8 -2
- data/lib/tap/exe.rb +1 -57
- data/lib/tap/support/declarations.rb +73 -51
- data/lib/tap/support/executable.rb +81 -2
- data/lib/tap/support/gems/rack.rb +151 -0
- data/lib/tap/support/parsers/base.rb +81 -0
- data/lib/tap/support/parsers/command_line.rb +90 -0
- data/lib/tap/support/parsers/server.rb +83 -0
- data/lib/tap/task.rb +327 -7
- data/lib/tap/workflow.rb +9 -48
- data/template/404.erb +12 -0
- data/template/index.erb +41 -0
- data/vendor/url_encoded_pair_parser.rb +93 -0
- metadata +10 -4
- data/lib/tap/support/command_line/parser.rb +0 -121
- data/lib/tap/support/framework.rb +0 -83
- data/lib/tap/support/framework_class.rb +0 -182
data/lib/tap/workflow.rb
CHANGED
@@ -76,43 +76,17 @@ module Tap
|
|
76
76
|
# app.run
|
77
77
|
# app.results(w1.exit_points, w2.exit_points)) # => [8, -8]
|
78
78
|
#
|
79
|
-
class Workflow
|
80
|
-
include Support::Framework
|
81
|
-
|
82
|
-
class << self
|
83
|
-
protected
|
84
|
-
|
85
|
-
def define(name, klass=Tap::Task, &block)
|
86
|
-
instance_var = "@#{name}".to_sym
|
87
|
-
|
88
|
-
define_method(name) do |*args|
|
89
|
-
raise ArgumentError, "wrong number of arguments (#{args.length} for 1)" if args.length > 1
|
90
|
-
|
91
|
-
instance_name = args[0] || name
|
92
|
-
instance_variable_set(instance_var, {}) unless instance_variable_defined?(instance_var)
|
93
|
-
instance_variable_get(instance_var)[instance_name] ||= task(instance_name, klass, &block)
|
94
|
-
end
|
95
|
-
|
96
|
-
define_method("#{name}=") do |input|
|
97
|
-
input = {name => input} unless input.kind_of?(Hash)
|
98
|
-
instance_variable_set(instance_var, input)
|
99
|
-
end
|
100
|
-
end
|
101
|
-
end
|
79
|
+
class Workflow < Task
|
102
80
|
|
103
81
|
# The entry point for self.
|
104
82
|
attr_accessor :entry_point
|
105
83
|
|
106
84
|
# The exit point for self.
|
107
85
|
attr_accessor :exit_point
|
108
|
-
|
109
|
-
# The task block provided during initialization.
|
110
|
-
attr_reader :task_block
|
111
|
-
|
86
|
+
|
112
87
|
# Creates a new Task with the specified attributes.
|
113
88
|
def initialize(config={}, name=nil, app=App.instance, &task_block)
|
114
|
-
super
|
115
|
-
@task_block = (task_block == nil ? default_task_block : task_block)
|
89
|
+
super
|
116
90
|
initialize_workflow
|
117
91
|
end
|
118
92
|
|
@@ -157,32 +131,22 @@ module Tap
|
|
157
131
|
# the number of inputs required by all the entry points;
|
158
132
|
# if the entry points have different input requirements, they
|
159
133
|
# have to be enqued separately.
|
160
|
-
def
|
134
|
+
def unbatched_enq(*inputs)
|
161
135
|
entry_points.each do |task|
|
162
136
|
app.enq(task, *inputs)
|
163
137
|
end
|
164
138
|
end
|
165
|
-
|
166
|
-
batch_function :enq
|
167
|
-
|
139
|
+
|
168
140
|
# Sets the on_complete_block for all exit points for self and
|
169
141
|
# self.batch. Use unbatched_on_complete to set the on_complete_block
|
170
142
|
# for just self.exit_points.
|
171
|
-
def
|
143
|
+
def unbatched_on_complete(override=false, &block)
|
172
144
|
exit_points.each do |task|
|
173
145
|
task.on_complete(override, &block)
|
174
146
|
end
|
175
147
|
self
|
176
148
|
end
|
177
|
-
|
178
|
-
batch_function(:on_complete) {}
|
179
|
-
|
180
|
-
def task(name, klass=Tap::Task, &block)
|
181
|
-
configs = config[name] || {}
|
182
|
-
raise ArgumentError, "config '#{name}' is not a hash" unless configs.kind_of?(Hash)
|
183
|
-
klass.new(configs, name, &block)
|
184
|
-
end
|
185
|
-
|
149
|
+
|
186
150
|
# The workflow definition method. By default workflow
|
187
151
|
# simply calls the task_block. In subclasses, workflow
|
188
152
|
# should be overridden to provide the workflow definition.
|
@@ -190,11 +154,8 @@ module Tap
|
|
190
154
|
task_block.call(self) if task_block
|
191
155
|
end
|
192
156
|
|
193
|
-
|
194
|
-
|
195
|
-
# Hook to set a default task block. By default, nil.
|
196
|
-
def default_task_block
|
197
|
-
nil
|
157
|
+
def process(*inputs)
|
158
|
+
enq(*inputs)
|
198
159
|
end
|
199
160
|
end
|
200
161
|
end
|
data/template/404.erb
ADDED
data/template/index.erb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
<html>
|
2
|
+
<head>
|
3
|
+
<title>Tap::Manifest</title>
|
4
|
+
</head>
|
5
|
+
<body>
|
6
|
+
<h1><a href="<%= Tap::WEBSITE %>">Tap (<ins>T</ins>ask <ins>Ap</ins>plication) <%= Tap::VERSION %> </a></h1>
|
7
|
+
|
8
|
+
<% server.manifest(:envs, true).minimize.each do |(env_key, env)|
|
9
|
+
cgi_manifest = env.manifest(:cgis, true).minimize
|
10
|
+
task_manifest = env.manifest(:tasks, true).minimize
|
11
|
+
next if cgi_manifest.empty? && task_manifest.empty? %>
|
12
|
+
<h2><%= env_key %></h2>
|
13
|
+
|
14
|
+
<% unless cgi_manifest.empty? %>
|
15
|
+
<ul>
|
16
|
+
<% cgi_manifest.each do |(cgi_key, path)|
|
17
|
+
lazydoc = Tap::Support::Lazydoc[path]
|
18
|
+
lazydoc.resolve
|
19
|
+
summary = lazydoc.default_attributes['summary']
|
20
|
+
%>
|
21
|
+
<li><a href="<%= cgi_key %>"><%= cgi_key %></a>: <%= summary %></li>
|
22
|
+
<% end %>
|
23
|
+
</ul>
|
24
|
+
<% end %>
|
25
|
+
|
26
|
+
<% unless task_manifest.empty? %>
|
27
|
+
<ul>
|
28
|
+
<% task_manifest.each do |(task_key, const)|
|
29
|
+
lazydoc = Tap::Support::Lazydoc[const.require_path]
|
30
|
+
lazydoc.resolve
|
31
|
+
summary = lazydoc[const.name]['manifest'].subject
|
32
|
+
%>
|
33
|
+
<li><a href="run?task=<%= task_key %>"><%= const.name %></a> <%= summary %></li>
|
34
|
+
<% end %>
|
35
|
+
</ul>
|
36
|
+
<% end %>
|
37
|
+
<% end %>
|
38
|
+
|
39
|
+
<a href="index?refresh=true">Refresh</a>
|
40
|
+
</body>
|
41
|
+
</html>
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require 'strscan'
|
2
|
+
|
3
|
+
class UrlEncodedPairParser < StringScanner #:nodoc:
|
4
|
+
attr_reader :top, :parent, :result
|
5
|
+
|
6
|
+
def initialize(pairs = [])
|
7
|
+
super('')
|
8
|
+
@result = {}
|
9
|
+
pairs.each { |key, value| parse(key, value) }
|
10
|
+
end
|
11
|
+
|
12
|
+
KEY_REGEXP = %r{([^\[\]=&]+)}
|
13
|
+
BRACKETED_KEY_REGEXP = %r{\[([^\[\]=&]+)\]}
|
14
|
+
|
15
|
+
# Parse the query string
|
16
|
+
def parse(key, value)
|
17
|
+
self.string = key
|
18
|
+
@top, @parent = result, nil
|
19
|
+
|
20
|
+
# First scan the bare key
|
21
|
+
key = scan(KEY_REGEXP) or return
|
22
|
+
key = post_key_check(key)
|
23
|
+
|
24
|
+
# Then scan as many nestings as present
|
25
|
+
until eos?
|
26
|
+
r = scan(BRACKETED_KEY_REGEXP) or return
|
27
|
+
key = self[1]
|
28
|
+
key = post_key_check(key)
|
29
|
+
end
|
30
|
+
|
31
|
+
bind(key, value)
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
# After we see a key, we must look ahead to determine our next action. Cases:
|
36
|
+
#
|
37
|
+
# [] follows the key. Then the value must be an array.
|
38
|
+
# = follows the key. (A value comes next)
|
39
|
+
# & or the end of string follows the key. Then the key is a flag.
|
40
|
+
# otherwise, a hash follows the key.
|
41
|
+
def post_key_check(key)
|
42
|
+
if scan(/\[\]/) # a[b][] indicates that b is an array
|
43
|
+
container(key, Array)
|
44
|
+
nil
|
45
|
+
elsif check(/\[[^\]]/) # a[b] indicates that a is a hash
|
46
|
+
container(key, Hash)
|
47
|
+
nil
|
48
|
+
else # End of key? We do nothing.
|
49
|
+
key
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# Add a container to the stack.
|
54
|
+
def container(key, klass)
|
55
|
+
type_conflict! klass, top[key] if top.is_a?(Hash) && top.key?(key) && ! top[key].is_a?(klass)
|
56
|
+
value = bind(key, klass.new)
|
57
|
+
type_conflict! klass, value unless value.is_a?(klass)
|
58
|
+
push(value)
|
59
|
+
end
|
60
|
+
|
61
|
+
# Push a value onto the 'stack', which is actually only the top 2 items.
|
62
|
+
def push(value)
|
63
|
+
@parent, @top = @top, value
|
64
|
+
end
|
65
|
+
|
66
|
+
# Bind a key (which may be nil for items in an array) to the provided value.
|
67
|
+
def bind(key, value)
|
68
|
+
if top.is_a? Array
|
69
|
+
if key
|
70
|
+
if top[-1].is_a?(Hash) && ! top[-1].key?(key)
|
71
|
+
top[-1][key] = value
|
72
|
+
else
|
73
|
+
top << {key => value}#.with_indifferent_access
|
74
|
+
push top.last
|
75
|
+
end
|
76
|
+
else
|
77
|
+
top << value
|
78
|
+
end
|
79
|
+
elsif top.is_a? Hash
|
80
|
+
key = CGI.unescape(key)
|
81
|
+
parent << (@top = {}) if top.key?(key) && parent.is_a?(Array)
|
82
|
+
return top[key] ||= value
|
83
|
+
else
|
84
|
+
raise ArgumentError, "Don't know what to do: top is #{top.inspect}"
|
85
|
+
end
|
86
|
+
|
87
|
+
return value
|
88
|
+
end
|
89
|
+
|
90
|
+
def type_conflict!(klass, value)
|
91
|
+
raise TypeError, "Conflicting types for parameter containers. Expected an instance of #{klass} but found an instance of #{value.class}. This can be caused by colliding Array and Hash parameters like qs[]=value&qs[key]=value."
|
92
|
+
end
|
93
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bahuvrihi-tap
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.10.
|
4
|
+
version: 0.10.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Simon Chiang
|
@@ -30,11 +30,13 @@ files:
|
|
30
30
|
- README
|
31
31
|
- MIT-LICENSE
|
32
32
|
- History
|
33
|
+
- cgi/run.rb
|
33
34
|
- cmd/console.rb
|
34
35
|
- cmd/destroy.rb
|
35
36
|
- cmd/generate.rb
|
36
37
|
- cmd/manifest.rb
|
37
38
|
- cmd/run.rb
|
39
|
+
- cmd/server.rb
|
38
40
|
- doc/Tutorial
|
39
41
|
- doc/Class Reference
|
40
42
|
- doc/Command Reference
|
@@ -85,7 +87,6 @@ files:
|
|
85
87
|
- lib/tap/support/batchable_class.rb
|
86
88
|
- lib/tap/support/class_configuration.rb
|
87
89
|
- lib/tap/support/command_line.rb
|
88
|
-
- lib/tap/support/command_line/parser.rb
|
89
90
|
- lib/tap/support/comment.rb
|
90
91
|
- lib/tap/support/configurable.rb
|
91
92
|
- lib/tap/support/configurable_class.rb
|
@@ -95,14 +96,16 @@ files:
|
|
95
96
|
- lib/tap/support/declarations.rb
|
96
97
|
- lib/tap/support/executable.rb
|
97
98
|
- lib/tap/support/executable_queue.rb
|
98
|
-
- lib/tap/support/framework.rb
|
99
|
-
- lib/tap/support/framework_class.rb
|
100
99
|
- lib/tap/support/gems/rake.rb
|
100
|
+
- lib/tap/support/gems/rack.rb
|
101
101
|
- lib/tap/support/gems.rb
|
102
102
|
- lib/tap/support/instance_configuration.rb
|
103
103
|
- lib/tap/support/lazy_attributes.rb
|
104
104
|
- lib/tap/support/lazydoc.rb
|
105
105
|
- lib/tap/support/manifest.rb
|
106
|
+
- lib/tap/support/parsers/base.rb
|
107
|
+
- lib/tap/support/parsers/command_line.rb
|
108
|
+
- lib/tap/support/parsers/server.rb
|
106
109
|
- lib/tap/support/run_error.rb
|
107
110
|
- lib/tap/support/shell_utils.rb
|
108
111
|
- lib/tap/support/tdoc.rb
|
@@ -127,6 +130,9 @@ files:
|
|
127
130
|
- lib/tap/test.rb
|
128
131
|
- lib/tap/workflow.rb
|
129
132
|
- lib/tap.rb
|
133
|
+
- template/404.erb
|
134
|
+
- template/index.erb
|
135
|
+
- vendor/url_encoded_pair_parser.rb
|
130
136
|
has_rdoc: true
|
131
137
|
homepage: http://tap.rubyforge.org
|
132
138
|
post_install_message:
|
@@ -1,121 +0,0 @@
|
|
1
|
-
module Tap
|
2
|
-
module Support
|
3
|
-
module CommandLine
|
4
|
-
class Parser
|
5
|
-
class << self
|
6
|
-
def parse_sequence(str, count=0)
|
7
|
-
seq = []
|
8
|
-
seq << count if str[0] == ?:
|
9
|
-
str.split(/:+/).each do |n|
|
10
|
-
seq << n.to_i unless n.empty?
|
11
|
-
end
|
12
|
-
seq << count + 1 if str[-1] == ?:
|
13
|
-
seq
|
14
|
-
end
|
15
|
-
|
16
|
-
def bracket_regexp(l, r)
|
17
|
-
/\A--(\d*)#{Regexp.escape(l)}([\d,]*)#{Regexp.escape(r)}\z/
|
18
|
-
end
|
19
|
-
|
20
|
-
def parse_bracket(lead, str, count=0)
|
21
|
-
bracket = []
|
22
|
-
str.split(/,+/).each do |n|
|
23
|
-
bracket << n.to_i unless n.empty?
|
24
|
-
end
|
25
|
-
|
26
|
-
[lead.empty? ? count : lead.to_i, bracket]
|
27
|
-
end
|
28
|
-
|
29
|
-
# Parses the input string as YAML, if the string matches the YAML document
|
30
|
-
# specifier (ie it begins with "---\s*\n"). Otherwise returns the string.
|
31
|
-
#
|
32
|
-
# str = {'key' => 'value'}.to_yaml # => "--- \nkey: value\n"
|
33
|
-
# Tap::Script.parse_yaml(str) # => {'key' => 'value'}
|
34
|
-
# Tap::Script.parse_yaml("str") # => "str"
|
35
|
-
def parse_yaml(str)
|
36
|
-
str =~ /\A---\s*\n/ ? YAML.load(str) : str
|
37
|
-
end
|
38
|
-
|
39
|
-
def shift_arg(argv)
|
40
|
-
index = nil
|
41
|
-
argv.each_with_index do |arg, i|
|
42
|
-
if arg !~ /\A-/
|
43
|
-
index = i
|
44
|
-
break
|
45
|
-
end
|
46
|
-
end
|
47
|
-
index == nil ? nil : argv.delete_at(index)
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
ROUND = /\A--(\+(\d+)|\+*)\z/
|
52
|
-
SEQUENCE = /\A--(\d*(:\d*)+)\z/
|
53
|
-
FORK = bracket_regexp("[", "]")
|
54
|
-
MERGE = bracket_regexp("{", "}")
|
55
|
-
SYNC_MERGE = bracket_regexp("(", ")")
|
56
|
-
INVALID = /\A--(\z|[^A-Za-z])/
|
57
|
-
|
58
|
-
attr_reader :rounds
|
59
|
-
attr_reader :sequences
|
60
|
-
attr_reader :forks
|
61
|
-
attr_reader :merges
|
62
|
-
attr_reader :sync_merges
|
63
|
-
|
64
|
-
def initialize(argv)
|
65
|
-
@sequences = []
|
66
|
-
@forks = []
|
67
|
-
@merges = []
|
68
|
-
@sync_merges = []
|
69
|
-
|
70
|
-
current = []
|
71
|
-
current_round = []
|
72
|
-
count = 0
|
73
|
-
@rounds = [current_round]
|
74
|
-
|
75
|
-
argv.each do |arg|
|
76
|
-
unless arg =~ INVALID
|
77
|
-
current << arg
|
78
|
-
next
|
79
|
-
end
|
80
|
-
|
81
|
-
# for peformance split to match
|
82
|
-
# most arguments just once.
|
83
|
-
current_round << current unless current.empty?
|
84
|
-
current = []
|
85
|
-
|
86
|
-
case arg
|
87
|
-
when ROUND
|
88
|
-
current_round = (@rounds[$2 ? $2.to_i : $1.length] ||= [])
|
89
|
-
when SEQUENCE
|
90
|
-
@sequences << Parser.parse_sequence($1, count)
|
91
|
-
when FORK
|
92
|
-
@forks << Parser.parse_bracket($1, $2, count)
|
93
|
-
when MERGE
|
94
|
-
@merges << Parser.parse_bracket($1, $2, count)
|
95
|
-
when SYNC_MERGE
|
96
|
-
@sync_merges << Parser.parse_bracket($1, $2, count)
|
97
|
-
else
|
98
|
-
raise ArgumentError, "invalid argument: #{arg}"
|
99
|
-
end
|
100
|
-
|
101
|
-
count += 1
|
102
|
-
end
|
103
|
-
|
104
|
-
current_round << current unless current.empty?
|
105
|
-
@rounds.delete_if {|round| round.nil? || round.empty? }
|
106
|
-
end
|
107
|
-
|
108
|
-
def targets
|
109
|
-
targets = []
|
110
|
-
sequences.each {|sequence| targets.concat(sequence[1..-1]) }
|
111
|
-
forks.each {|fork| targets.concat(fork[1]) }
|
112
|
-
targets.concat merges.collect {|target, sources| target }
|
113
|
-
targets.concat sync_merges.collect {|target, sources| target }
|
114
|
-
|
115
|
-
targets.uniq.sort
|
116
|
-
end
|
117
|
-
|
118
|
-
end
|
119
|
-
end
|
120
|
-
end
|
121
|
-
end
|
@@ -1,83 +0,0 @@
|
|
1
|
-
require 'tap/support/batchable'
|
2
|
-
require 'tap/support/executable'
|
3
|
-
require 'tap/support/framework_class'
|
4
|
-
|
5
|
-
module Tap
|
6
|
-
module Support
|
7
|
-
|
8
|
-
# Framework encapsulates the basic framework functionality (batching,
|
9
|
-
# configuration, documentation, etc) used by Task and Workflow. Note
|
10
|
-
# that Framework does NOT encapsulate the functionality needed to
|
11
|
-
# make a class useful in workflows, such as enq and on_complete.
|
12
|
-
module Framework
|
13
|
-
include Batchable
|
14
|
-
include Configurable
|
15
|
-
|
16
|
-
def self.included(mod)
|
17
|
-
mod.extend Support::BatchableClass
|
18
|
-
mod.extend Support::ConfigurableClass
|
19
|
-
mod.extend Support::FrameworkClass
|
20
|
-
mod.lazy_attr :manifest
|
21
|
-
mod.lazy_attr :args
|
22
|
-
end
|
23
|
-
|
24
|
-
# The application used to load config_file templates
|
25
|
-
# (and hence, to initialize batched objects).
|
26
|
-
attr_reader :app
|
27
|
-
|
28
|
-
# The name of self.
|
29
|
-
#--
|
30
|
-
# Currently names may be any object. Audit makes use of name
|
31
|
-
# via to_s, as does app when figuring configuration filepaths.
|
32
|
-
attr_accessor :name
|
33
|
-
|
34
|
-
# Initializes a new instance and associated batch objects. Batch
|
35
|
-
# objects will be initialized for each configuration template
|
36
|
-
# specified by app.each_config_template(config_file) where
|
37
|
-
# config_file = app.config_filepath(name).
|
38
|
-
def initialize(config={}, name=nil, app=App.instance)
|
39
|
-
super()
|
40
|
-
@app = app
|
41
|
-
@name = name || self.class.default_name
|
42
|
-
|
43
|
-
case config
|
44
|
-
when InstanceConfiguration
|
45
|
-
@config = config
|
46
|
-
config.bind(self)
|
47
|
-
else
|
48
|
-
initialize_config(config)
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
# Creates a new batched object and adds the object to batch. The batched object
|
53
|
-
# will be a duplicate of the current object but with a new name and/or
|
54
|
-
# configurations.
|
55
|
-
def initialize_batch_obj(overrides={}, name=nil)
|
56
|
-
obj = super().reconfigure(overrides)
|
57
|
-
obj.name = name if name
|
58
|
-
obj
|
59
|
-
end
|
60
|
-
|
61
|
-
# Logs the inputs to the application logger (via app.log)
|
62
|
-
def log(action, msg="", level=Logger::INFO)
|
63
|
-
# TODO - add a task identifier?
|
64
|
-
app.log(action, msg, level)
|
65
|
-
end
|
66
|
-
|
67
|
-
# Raises a TerminateError if app.state == State::TERMINATE.
|
68
|
-
# check_terminate may be called at any time to provide a
|
69
|
-
# breakpoint in long-running processes.
|
70
|
-
def check_terminate
|
71
|
-
if app.state == App::State::TERMINATE
|
72
|
-
raise App::TerminateError.new
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
# Returns self.name
|
77
|
-
def to_s
|
78
|
-
name.to_s
|
79
|
-
end
|
80
|
-
|
81
|
-
end
|
82
|
-
end
|
83
|
-
end
|