tap-server 0.3.0 → 0.4.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.
Files changed (58) hide show
  1. data/History +5 -0
  2. data/cmd/server.rb +36 -8
  3. data/lib/tap/controller/rest_routes.rb +77 -0
  4. data/lib/tap/controller/utils.rb +29 -0
  5. data/lib/tap/controller.rb +244 -145
  6. data/lib/tap/controllers/app.rb +97 -55
  7. data/lib/tap/controllers/data.rb +132 -0
  8. data/lib/tap/controllers/schema.rb +137 -223
  9. data/lib/tap/controllers/server.rb +70 -27
  10. data/lib/tap/server/data.rb +178 -0
  11. data/lib/tap/server/runner.rb +71 -0
  12. data/lib/tap/server/server_error.rb +35 -0
  13. data/lib/tap/server.rb +60 -275
  14. data/lib/tap/tasks/echo.rb +39 -6
  15. data/lib/tap/tasks/render.rb +65 -0
  16. data/public/stylesheets/tap.css +15 -2
  17. data/views/configurable/_config.erb +1 -0
  18. data/views/configurable/_configs.erb +28 -0
  19. data/views/configurable/_flag.erb +2 -0
  20. data/views/configurable/_list_select.erb +8 -0
  21. data/views/configurable/_select.erb +6 -0
  22. data/views/configurable/_switch.erb +2 -0
  23. data/views/layout.erb +1 -1
  24. data/views/object/obj.erb +1 -0
  25. data/views/tap/controllers/app/_action.erb +3 -0
  26. data/views/tap/controllers/app/build.erb +18 -0
  27. data/views/tap/controllers/app/enque.erb +13 -0
  28. data/views/tap/controllers/app/info.erb +20 -7
  29. data/views/tap/controllers/data/_controls.erb +21 -0
  30. data/views/tap/controllers/data/_index_entry.erb +1 -0
  31. data/views/tap/controllers/data/_upload.erb +8 -0
  32. data/views/tap/controllers/data/entry.erb +8 -0
  33. data/views/tap/controllers/data/index.erb +14 -0
  34. data/views/tap/controllers/schema/_build.erb +6 -0
  35. data/views/tap/controllers/schema/_index_entry.erb +6 -0
  36. data/views/tap/controllers/schema/entry.erb +135 -0
  37. data/views/tap/controllers/schema/join.erb +6 -4
  38. data/views/tap/controllers/schema/task.erb +6 -0
  39. data/views/tap/controllers/server/access.erb +4 -0
  40. data/views/tap/controllers/server/admin.erb +21 -0
  41. data/views/tap/controllers/server/help.erb +23 -0
  42. data/views/tap/controllers/server/index.erb +43 -3
  43. data/views/tap/task/input.erb +17 -0
  44. data/views/tap/tasks/load/input.erb +11 -0
  45. metadata +35 -17
  46. data/lib/tap/server_error.rb +0 -34
  47. data/lib/tap/support/persistence.rb +0 -71
  48. data/lib/tap/tasks/server.rb +0 -30
  49. data/views/tap/controllers/app/index.erb +0 -44
  50. data/views/tap/controllers/schema/config/default.erb +0 -4
  51. data/views/tap/controllers/schema/config/flag.erb +0 -3
  52. data/views/tap/controllers/schema/config/switch.erb +0 -6
  53. data/views/tap/controllers/schema/configurations.erb +0 -27
  54. data/views/tap/controllers/schema/node.erb +0 -47
  55. data/views/tap/controllers/schema/preview.erb +0 -3
  56. data/views/tap/controllers/schema/round.erb +0 -30
  57. data/views/tap/controllers/schema/schema.erb +0 -28
  58. data/views/tap/tasks/echo/result.html +0 -1
@@ -1,260 +1,174 @@
1
- require 'tap/controller'
1
+ require 'tap/controllers/data'
2
2
 
3
3
  module Tap
4
4
  module Controllers
5
-
6
- # ::controller
7
- class Schema < Tap::Controller
8
- set :default_layout, 'layout.erb'
9
-
10
- # Initializes a new schema and redirects to display.
11
- def index
12
- id = initialize_schema
13
- redirect("/schema/display/#{id}")
14
- end
15
-
16
- # Loads the schema indicated by id and renders 'schema.erb' with the default
17
- # layout.
18
- def display(id)
19
- schema = load_schema(id)
20
- render 'schema.erb', :locals => {
21
- :id => id,
22
- :schema => schema
23
- }, :layout => true
24
- end
25
-
26
- # Updates the specified schema with the request parameters. Update forwards
27
- # the request to the action ('add' or 'remove') specified in the action
28
- # parameter.
29
- def update(id)
30
- case request['action']
31
- when 'add' then add(id)
32
- when 'remove' then remove(id)
33
- when 'echo' then echo
34
- else raise Tap::ServerError, "unknown action: #{request['action']}"
35
- end
36
- end
37
-
38
- # Adds nodes or joins to the schema. Parameters:
39
- #
40
- # nodes[]:: An array of nodes to add to the schema. Each entry is split using
41
- # Shellwords to yield an argv; the argv initializes the node. The
42
- # index of each new node is added to targets[].
43
- # sources[]:: An array of source node indicies used to create a join.
44
- # targets[]:: An array of target node indicies used to create a join (note
45
- # the indicies of new nodes are added to targets).
46
- #
47
- # Add creates and pushes new nodes onto schema as specified in nodes, then
48
- # creates joins between the sources and targets. The join class is inferred
49
- # by Utils.infer_join; if no join can be inferred the join class is
50
- # effectively nil, and consistent with that, the node output for sources
51
- # and the node input for targets is set to nil.
52
- #
53
- # === Notes
5
+ class Schema < Data
6
+
7
+ # Adds tasks or joins to the schema. Parameters:
54
8
  #
55
- # The nomenclature for source and target is relative to the join, and may
56
- # seem backwards for the node (ex: 'sources[]=0&targets[]=1' makes a join
57
- # like '0:1')
9
+ # tasks[]:: An array of tasks to add to the schema.
10
+ # inputs[]:: An array of task indicies used as inputs to a join.
11
+ # outputs[]:: An array of task indicies used as outputs for a join.
58
12
  #
59
13
  def add(id)
60
- unless request.post?
61
- raise Tap::ServerError, "add must be performed with post"
14
+ if id == "new"
15
+ id = data.next_id(type).to_s
62
16
  end
63
-
64
- round = (request['round'] || 0).to_i
65
- outputs = (request['outputs[]'] || []).collect {|index| index.to_i }
66
- inputs = (request['inputs[]'] || []).collect {|index| index.to_i }
67
- nodes = request['nodes[]'] || []
68
-
69
- load_schema(id) do |schema|
70
- nodes.each do |arg|
71
- next unless arg && !arg.empty?
72
-
73
- outputs << schema.nodes.length
74
- schema.nodes << Tap::Support::Node.new(Shellwords.shellwords(arg), round)
75
- end
76
-
77
- if inputs.empty? || outputs.empty?
78
- inputs.each {|index| schema[index].output = nil }
79
- outputs.each {|index| schema[index].input = round }
80
- else
81
17
 
82
- # temporary
83
- if inputs.length > 1 && outputs.length > 1
84
- raise "multi-way join specified"
85
- end
18
+ tasks = request['tasks'] || []
19
+ inputs = request['inputs'] || []
20
+ outputs = request['outputs'] || []
21
+ queue = request['queue'] || []
86
22
 
87
- schema.set(Tap::Support::Join, inputs, outputs)
23
+ update_schema(id) do |schema|
24
+ current = schema.tasks
25
+ tasks.each do |task|
26
+ key = task
27
+ while current.has_key?(key)
28
+ i ||= 0
29
+ key = "#{task}_#{i}"
30
+ i += 1
31
+ end
32
+
33
+ current[key] = {'id' => task}
34
+ end
35
+
36
+ if !inputs.empty? && !outputs.empty?
37
+ schema.joins << [inputs, outputs]
38
+ end
39
+
40
+ queue.each do |task|
41
+ schema.queue << [task]
88
42
  end
89
43
  end
90
-
91
- redirect("/schema/display/#{id}")
44
+
45
+ redirect uri(id)
92
46
  end
93
-
94
- # Removes nodes or joins from the schema. Parameters:
95
- #
96
- # sources[]:: An array of source node indicies to remove.
97
- # targets[]:: An array of target node indicies to remove.
98
- #
99
- # Normally remove sets the node.output for each source to nil and the
100
- # node.input for each target to nil. However, if a node is indicated in
101
- # both sources and targets AND it has no join input/output, then it will
102
- # be removed.
103
- #
104
- # === Notes
47
+
48
+ # Removes tasks or joins from a schema. Parameters:
105
49
  #
106
- # The nomenclature for source and target is relative to the join, and may
107
- # seem backwards for the node (ex: for the sequence '0:1:2', 'targets[]=1'
108
- # breaks the join '0:1' while 'sources[]=1' breaks the join '1:2'.
50
+ # tasks[]:: An array of task keys to remove.
51
+ # joins[]:: An array of join indicies to remove.
109
52
  #
110
53
  def remove(id)
111
- unless request.post?
112
- raise Tap::ServerError, "remove must be performed with post"
54
+ if id == "new"
55
+ id = data.next_id(type).to_s
113
56
  end
114
-
115
- round = (request['round'] || 0).to_i
116
- outputs = (request['outputs[]'] || []).collect {|index| index.to_i }
117
- inputs = (request['inputs[]'] || []).collect {|index| index.to_i }
118
-
119
- load_schema(id) do |schema|
120
- # Remove joins. Removed indicies are popped to ensure
121
- # that if a join was removed the node will not be.
122
- outputs.delete_if do |index|
123
- next unless node = schema.nodes[index]
124
- if node.input_join
125
- node.input = round
126
- true
127
- else
128
- false
129
- end
130
- end
131
-
132
- inputs.delete_if do |index|
133
- next unless node = schema.nodes[index]
134
- if node.output_join
135
- node.output = nil
136
- true
137
- else
138
- false
139
- end
140
- end
141
-
142
- # Remove nodes. Setting a node to nil causes it's removal during
143
- # compact; orphaned joins are removed during compact as well.
144
- (inputs & outputs).each do |index|
145
- schema.nodes[index] = nil
57
+
58
+ tasks = request['tasks'] || []
59
+ joins = request['joins'] || []
60
+ queue = request['queue'] || []
61
+
62
+ update_schema(id) do |schema|
63
+ tasks.each do |key|
64
+ schema.tasks.delete(key)
146
65
  end
66
+
67
+ joins.each {|index| schema.joins[index.to_i] = nil }
68
+ schema.joins.compact!
69
+
70
+ queue.each {|index| schema.queue[index.to_i] = nil }
71
+ schema.queue.compact!
72
+
73
+ schema.cleanup!
147
74
  end
148
75
 
149
- redirect("/schema/display/#{id}")
150
- end
151
-
152
- def submit(id)
153
- case request['action']
154
- when 'save' then save(id)
155
- when 'preview' then preview(id)
156
- when 'echo' then echo
157
- when 'run'
158
- dump_schema(id, schema)
159
- run(id)
160
- else raise Tap::ServerError, "unknown action: #{request['action']}"
161
- end
76
+ redirect uri(id)
162
77
  end
163
-
164
- def save(id)
165
- unless request.post?
166
- raise Tap::ServerError, "submit must be performed with post"
78
+
79
+ def configure(id)
80
+ if id == "new"
81
+ id = data.next_id(type).to_s
167
82
  end
168
-
169
- dump_schema(id, schema)
170
- redirect("/schema/display/#{id}")
171
- end
172
-
173
- def preview(id)
174
- response.headers['Content-Type'] = 'text/plain'
175
- render('preview.erb', :locals => {:id => id, :schema => schema})
176
- end
177
-
178
- def run(id)
179
- unless request.post?
180
- raise Tap::ServerError, "run must be performed with post"
83
+
84
+ schema = Tap::Schema.new(request['schema'] || {})
85
+ schema.scrub! do |obj|
86
+ scrub(obj['config'])
181
87
  end
182
-
183
- # it would be nice to someday put all this on a separate thread...
184
- schema = load_schema(id)
185
- tasks = server.env.tasks
186
- schema.build(app) do |(key, *args)|
187
- if const = tasks.search(key)
188
- const.constantize.parse(args, app) do |help|
189
- raise "help not implemented"
190
- #redirect("/app/help/#{key}")
191
- end
192
- else
193
- raise ArgumentError, "unknown task: #{key}"
194
- end
88
+
89
+ data.create_or_update(type, id) do |io|
90
+ io << schema.dump
195
91
  end
196
-
197
- Thread.new { app.run }
198
- redirect("/app/tail")
92
+
93
+ redirect uri(id)
199
94
  end
200
-
95
+
96
+ # Helper methods
201
97
  protected
202
-
203
- # Parses a Tap::Support::Schema from the request.
204
- def schema
205
- argv = request['argv[]'] || []
206
- argv.delete_if {|arg| arg.empty? }
207
- Tap::Support::Schema.parse(argv)
98
+
99
+ def env
100
+ server.env
208
101
  end
209
-
210
- def initialize_schema
211
- current = root.glob(:schema, "*").collect {|path| File.basename(path).chomp(".yml") }
212
-
213
- id = random_key(current.length)
214
- while current.include?(id)
215
- id = random_key(current.length)
216
- end
217
-
218
- dump_schema(id, schema)
219
- id
102
+
103
+ def type
104
+ :schema
220
105
  end
221
-
222
- def load_schema(id)
223
- unless path = root.filepath(:schema, "#{id}.yml")
224
- raise ServerError, "no schema for id: #{id}"
225
- end
226
- schema = Tap::Support::Schema.load_file(path)
227
-
228
- if block_given?
229
- result = yield(schema)
230
- dump_schema(id, schema)
231
- result
106
+
107
+ def display(id)
108
+ schema = if path = data.find(type, id)
109
+ Tap::Schema.load_file(path)
232
110
  else
233
- schema
111
+ Tap::Schema.new
234
112
  end
113
+
114
+ schema.resolve! do |type, key, data|
115
+ env.manifest(type)[key]
116
+ end
117
+
118
+ render "entry.erb", :locals => {
119
+ :id => id,
120
+ :schema => schema
121
+ }, :layout => true
235
122
  end
236
-
237
- def dump_schema(id, schema=nil)
238
- root.prepare(:schema, "#{id}.yml") do |file|
239
- file << YAML.dump(schema.dump) if schema
123
+
124
+ def stringify(obj)
125
+ case obj
126
+ when String, Numeric, true, false
127
+ obj.to_s
128
+ when Symbol, Regexp
129
+ obj.inspect
130
+ when nil
131
+ '~'
132
+ when $stdout
133
+ 'data/results.txt'
134
+ else
135
+ obj
240
136
  end
241
137
  end
242
-
243
- def instantiate(*argv)
244
- key = argv.shift
245
- tasc = server.env.tasks.search(key).constantize
246
- tasc.parse(argv)
138
+
139
+ def default_config(configurable)
140
+ configs = configurable.configurations
141
+ Configurable::DelegateHash.new(configs).to_hash do |hash, key, value|
142
+ hash[key.to_s] = stringify(value)
143
+ end
247
144
  end
248
-
249
- # helper to echo requests back... good for debugging
250
- def echo # :nodoc:
251
- "<pre>#{request.params.to_yaml}</pre>"
145
+
146
+ def scrub(obj)
147
+ case obj
148
+ when Hash
149
+ obj.delete_if do |key, value|
150
+ value ? scrub(value) : true
151
+ end
152
+ when Array
153
+ obj.delete_if do |value|
154
+ value ? scrub(value) : true
155
+ end
156
+ end
157
+
158
+ false
252
159
  end
253
-
254
- # Generates a random integer key.
255
- def random_key(length) # :nodoc:
256
- length = 1 if length < 1
257
- rand(length * 10000).to_s
160
+
161
+ def update_schema(id)
162
+ path = data.find(type, id) || data.create(type, id)
163
+ schema = Tap::Schema.load_file(path)
164
+
165
+ yield(schema)
166
+
167
+ data.update(type, id) do |io|
168
+ io << schema.dump
169
+ end
170
+
171
+ id
258
172
  end
259
173
  end
260
174
  end
@@ -1,9 +1,11 @@
1
1
  require 'tap/controller'
2
+ require 'rack/mime'
3
+ require 'time'
2
4
 
3
5
  module Tap
4
6
  module Controllers
5
7
 
6
- # :startdoc::controller remotely controls and monitors server
8
+ # :startdoc::controller remotely controls and monitor a server
7
9
  #
8
10
  # Server provides several uris to control and monitor the server behavior.
9
11
  # Importantly, server allows the remote shutdown of a Tap::Server if a
@@ -11,48 +13,89 @@ module Tap
11
13
  # background and still have a shutdown handle on them.
12
14
  #
13
15
  class Server < Tap::Controller
14
- set :default_layout, 'layout.erb'
15
-
16
+ include Utils
17
+
16
18
  def index
17
- render 'index.erb'
19
+ render('index.erb', :layout => true)
18
20
  end
19
21
 
20
- # Returns 'pong'.
22
+ # Returns pong
21
23
  def ping
22
- response['Content-Type'] = 'text/plain'
24
+ response['Content-Type'] = "text/plain"
23
25
  "pong"
24
26
  end
25
27
 
26
- # Returns the public server configurations as xml.
27
- def config
28
- response['Content-Type'] = 'text/xml'
29
- %Q{<?xml version="1.0"?>
30
- <server>
31
- <uri>#{uri}</uri>
32
- <shutdown_key>#{shutdown_key}</shutdown_key>
33
- </server>}
28
+ # Essentially a login for server administration
29
+ def access
30
+ if request.get?
31
+ render 'access.erb', :locals => {
32
+ :secret => request['secret']
33
+ }, :layout => true
34
+ else
35
+ redirect uri("admin", :secret => request['secret'])
36
+ end
37
+ end
38
+
39
+ # Administrate this server
40
+ def admin(secret=nil)
41
+ template = server.admin?(secret) ? 'admin.erb' : 'access.erb'
42
+ render template, :locals => {:secret => secret}, :layout => true
34
43
  end
35
44
 
36
- # Shuts down the server. Shutdown requires a shutdown key which
37
- # is setup when the server is launched. If no shutdown key was
38
- # setup, shutdown does nothing and responds accordingly.
39
- def shutdown
40
- if shutdown_key && request['shutdown_key'].to_i == shutdown_key
41
- # wait a second to shutdown, so the response is sent out.
42
- Thread.new {sleep 1; server.stop! }
45
+ # Terminates app and stops self (on post).
46
+ def shutdown(secret=nil)
47
+ response['Content-Type'] = "text/plain"
48
+
49
+ if server.admin?(secret) && request.post?
50
+ # wait a bit to shutdown, so the response is sent out.
51
+ Thread.new { sleep(0.1); server.stop! }
43
52
  "shutdown"
44
53
  else
45
- "you do not have permission to shutdown this server"
54
+ ""
46
55
  end
47
56
  end
48
57
 
49
- protected
58
+ def help(type=nil, *key)
59
+ if constant = server.env[type][key.join('/')]
60
+ module_render 'help.erb', constant
61
+ else
62
+ "unknown #{type}: #{key.join('/')}"
63
+ end
64
+ end
50
65
 
51
- # returns the server shutdown key. the shutdown key is required
52
- # for shutdown to function, a nil shutdown key disables shutdown.
53
- def shutdown_key # :nodoc:
54
- server.shutdown_key
66
+ # ensure server methods are not added as actions
67
+ set :define_action, false
68
+
69
+ def call(rack_env)
70
+ path = rack_env['tap.server'].env.path(:public, rack_env['PATH_INFO']) {|file| File.file?(file) }
71
+ if path
72
+ static_file(path)
73
+ else
74
+ super
75
+ end
55
76
  end
77
+
78
+ # Returns a controller uri, attaching the secret to the action, if specified.
79
+ def uri(action=nil, params={})
80
+ secret = params.delete(:secret)
81
+
82
+ if secret
83
+ action = action ? "#{action}/#{secret}" : secret
84
+ end
85
+
86
+ super(action, params)
87
+ end
88
+
89
+ # Returns a help uri for the specified resource, currently only sketched out.
90
+ def help_uri(type, key)
91
+ uri("help/#{type}/#{key}")
92
+ end
93
+
94
+ # Stops the server.
95
+ def stop!
96
+ server.stop!
97
+ end
98
+
56
99
  end
57
100
  end
58
101
  end