tap-server 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
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