tap-server 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/History CHANGED
@@ -1,3 +1,7 @@
1
+ == 0.2.0 / 2009-03-07
2
+
3
+ * Reworked server routing to route by ::controller
4
+
1
5
  == 0.1.1 / 2009-02-23
2
6
 
3
7
  Update executable to use Tap-0.12.2.
data/README CHANGED
@@ -45,5 +45,5 @@ Tap requires an updated version of RubyGems[http://docs.rubygems.org/]
45
45
  Copyright (c) 2009, Regents of the University of Colorado.
46
46
  Developer:: {Simon Chiang}[http://bahuvrihi.wordpress.com], {Biomolecular Structure Program}[http://biomol.uchsc.edu/], {Hansen Lab}[http://hsc-proteomics.uchsc.edu/hansenlab/]
47
47
  Support:: CU Denver School of Medicine Deans Academic Enrichment Fund
48
- Licence:: {MIT-Style}[link:files/MIT-LICENSE.html]
48
+ License:: {MIT-Style}[link:files/MIT-LICENSE.html]
49
49
 
@@ -0,0 +1,98 @@
1
+ require 'tap/controller'
2
+ require 'rack/mime'
3
+ require 'time'
4
+
5
+ module Tap
6
+ module Controllers
7
+
8
+ # ::controller
9
+ class App < Tap::Controller
10
+ set :default_layout, 'layout.erb'
11
+
12
+ def call(env)
13
+ # serve public files before actions
14
+ server = env['tap.server'] ||= Tap::Server.new
15
+
16
+ if path = server.public_path(env['PATH_INFO'])
17
+ content = File.read(path)
18
+ headers = {
19
+ "Last-Modified" => [File.mtime(path).httpdate],
20
+ "Content-Type" => [Rack::Mime.mime_type(File.extname(path), 'text/plain')],
21
+ "Content-Length" => [content.size.to_s]
22
+ }
23
+
24
+ [200, headers, [content]]
25
+ else
26
+ super
27
+ end
28
+ end
29
+
30
+ def index
31
+ env_names = {}
32
+ server.env.minimap.each do |name, environment|
33
+ env_names[environment] = name
34
+ end
35
+
36
+ render('index.erb', :locals => {:env => server.env, :env_names => env_names}, :layout => true)
37
+ end
38
+
39
+ def info
40
+ if request.post?
41
+ app.info
42
+ else
43
+ render('info.erb', :locals => {:update => true, :content => app.info}, :layout => true)
44
+ end
45
+ end
46
+
47
+ #--
48
+ # Currently tail is hard-coded to tail the server log only.
49
+ def tail(id=nil)
50
+ begin
51
+ path = root.subpath(:log, 'server.log')
52
+ raise unless File.exists?(path)
53
+ rescue
54
+ raise Tap::ServerError.new("invalid path", 404)
55
+ end
56
+
57
+ pos = request['pos'].to_i
58
+ if pos > File.size(path)
59
+ raise Tap::ServerError.new("tail position out of range (try update)", 500)
60
+ end
61
+
62
+ content = File.open(path) do |file|
63
+ file.pos = pos
64
+ file.read
65
+ end
66
+
67
+ if request.post?
68
+ content
69
+ else
70
+ render('tail.erb', :locals => {
71
+ :id => id,
72
+ :path => File.basename(path),
73
+ :update => true,
74
+ :content => content
75
+ }, :layout => true)
76
+ end
77
+ end
78
+
79
+ def run
80
+ Thread.new { app.run }
81
+ redirect("/app/tail")
82
+ end
83
+
84
+ def stop
85
+ app.stop
86
+ redirect("/app/info")
87
+ end
88
+
89
+ def terminate
90
+ app.terminate
91
+ redirect("/app/info")
92
+ end
93
+
94
+ def help(key=nil)
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,261 @@
1
+ require 'tap/controller'
2
+
3
+ module Tap
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
54
+ #
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')
58
+ #
59
+ def add(id)
60
+ unless request.post?
61
+ raise Tap::ServerError, "add must be performed with post"
62
+ 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
+
82
+ # temporary
83
+ if inputs.length > 1 && outputs.length > 1
84
+ raise "multi-way join specified"
85
+ end
86
+
87
+ schema.set(Tap::Support::Join, inputs, outputs)
88
+ end
89
+ end
90
+
91
+ redirect("/schema/display/#{id}")
92
+ 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
105
+ #
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'.
109
+ #
110
+ def remove(id)
111
+ unless request.post?
112
+ raise Tap::ServerError, "remove must be performed with post"
113
+ 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
146
+ end
147
+ end
148
+
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
162
+ end
163
+
164
+ def save(id)
165
+ unless request.post?
166
+ raise Tap::ServerError, "submit must be performed with post"
167
+ 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"
181
+ 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
195
+ end
196
+
197
+ Thread.new { app.run }
198
+ redirect("/app/tail")
199
+ end
200
+
201
+ 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)
208
+ 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
220
+ 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
232
+ else
233
+ schema
234
+ end
235
+ 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
240
+ end
241
+ end
242
+
243
+ def instantiate(*argv)
244
+ key = argv.shift
245
+ tasc = server.env.tasks.search(key).constantize
246
+ tasc.parse(argv)
247
+ end
248
+
249
+ # helper to echo requests back... good for debugging
250
+ def echo # :nodoc:
251
+ "<pre>#{request.params.to_yaml}</pre>"
252
+ 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
258
+ end
259
+ end
260
+ end
261
+ end
data/lib/tap/server.rb CHANGED
@@ -6,16 +6,14 @@ require 'tap/server_error'
6
6
 
7
7
  module Tap
8
8
  Env.manifest(:controllers) do |env|
9
- entries = env.root.glob(:controllers, "*_controller.rb").collect do |path|
10
- const_name = File.basename(path).chomp('.rb').camelize
11
- Support::Constant.new(const_name, path)
12
- end
13
-
14
- Support::Manifest.intern(entries) do |manifest, const|
15
- const.basename.chomp('_controller')
9
+ controllers = Support::ConstantManifest.new('controller')
10
+ env.load_paths.each do |path_root|
11
+ controllers.register(path_root, '**/*.rb')
16
12
  end
13
+ controllers
17
14
  end
18
15
 
16
+ # :::-
19
17
  # Server is a Rack application that dispatches calls to other Rack apps, most
20
18
  # commonly a Tap::Controller.
21
19
  #
@@ -37,24 +35,25 @@ module Tap
37
35
  # req.get('/sample/path/to/resource').body # => "Sample got /sample : /path/to/resource"
38
36
  #
39
37
  # Server automatically maps unknown keys to a controller by searching
40
- # env.controllers. As a result '/example' maps to the ExampleController
41
- # defined in 'controllers/example_controller.rb'.
38
+ # env.controllers. As a result '/example' maps to the Example controller
39
+ # defined in 'lib/example.rb'.
42
40
  #
43
- # # [controllers/example_controller.rb] => %q{
44
- # # class ExampleController
41
+ # # [lib/example.rb] => %q{
42
+ # # ::controller
43
+ # # class Example
45
44
  # # def self.call(env)
46
- # # [200, {}, ["ExampleController got #{env['SCRIPT_NAME']} : #{env['PATH_INFO']}"]]
45
+ # # [200, {}, ["Example got #{env['SCRIPT_NAME']} : #{env['PATH_INFO']}"]]
47
46
  # # end
48
47
  # # end
49
48
  # # }
50
49
  #
51
- # req.get('/example/path/to/resource').body # => "ExampleController got /example : /path/to/resource"
50
+ # req.get('/example/path/to/resource').body # => "Example got /example : /path/to/resource"
52
51
  #
53
52
  # If desired, controllers can be set with aliases to map a path key to a
54
53
  # lookup key.
55
54
  #
56
55
  # server.controllers['sample'] = 'example'
57
- # req.get('/sample/path/to/resource').body # => "ExampleController got /sample : /path/to/resource"
56
+ # req.get('/sample/path/to/resource').body # => "Example got /sample : /path/to/resource"
58
57
  #
59
58
  # If no controller can be found, the request is routed using the
60
59
  # default_controller_key and the request is NOT adjusted.
@@ -66,6 +65,7 @@ module Tap
66
65
  #
67
66
  # req.get('/unknown/path/to/resource').body # => "App got : /unknown/path/to/resource"
68
67
  #
68
+ # :::+
69
69
  class Server
70
70
  include Rack::Utils
71
71
  include Configurable
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tap-server
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Simon Chiang
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-02-23 00:00:00 -07:00
12
+ date: 2009-03-07 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -20,7 +20,7 @@ dependencies:
20
20
  requirements:
21
21
  - - ">="
22
22
  - !ruby/object:Gem::Version
23
- version: 0.12.2
23
+ version: 0.12.3
24
24
  version:
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: rack
@@ -44,9 +44,9 @@ extra_rdoc_files:
44
44
  - History
45
45
  files:
46
46
  - cmd/server.rb
47
- - controllers/app_controller.rb
48
- - controllers/schema_controller.rb
49
47
  - lib/tap/controller.rb
48
+ - lib/tap/controllers/app.rb
49
+ - lib/tap/controllers/schema.rb
50
50
  - lib/tap/server.rb
51
51
  - lib/tap/server_error.rb
52
52
  - lib/tap/tasks/echo.rb
@@ -57,19 +57,19 @@ files:
57
57
  - tap.yml
58
58
  - views/404.erb
59
59
  - views/500.erb
60
- - views/app_controller/index.erb
61
- - views/app_controller/info.erb
62
- - views/app_controller/tail.erb
63
60
  - views/layout.erb
64
- - views/schema_controller/config/default.erb
65
- - views/schema_controller/config/flag.erb
66
- - views/schema_controller/config/switch.erb
67
- - views/schema_controller/configurations.erb
68
- - views/schema_controller/join.erb
69
- - views/schema_controller/node.erb
70
- - views/schema_controller/preview.erb
71
- - views/schema_controller/round.erb
72
- - views/schema_controller/schema.erb
61
+ - views/tap/controllers/app/index.erb
62
+ - views/tap/controllers/app/info.erb
63
+ - views/tap/controllers/app/tail.erb
64
+ - views/tap/controllers/schema/config/default.erb
65
+ - views/tap/controllers/schema/config/flag.erb
66
+ - views/tap/controllers/schema/config/switch.erb
67
+ - views/tap/controllers/schema/configurations.erb
68
+ - views/tap/controllers/schema/join.erb
69
+ - views/tap/controllers/schema/node.erb
70
+ - views/tap/controllers/schema/preview.erb
71
+ - views/tap/controllers/schema/round.erb
72
+ - views/tap/controllers/schema/schema.erb
73
73
  - views/tap/tasks/echo/result.html
74
74
  - README
75
75
  - MIT-LICENSE
@@ -1,92 +0,0 @@
1
- require 'tap/controller'
2
- require 'rack/mime'
3
- require 'time'
4
-
5
- class AppController < Tap::Controller
6
- set :default_layout, 'layout.erb'
7
-
8
- def call(env)
9
- # serve public files before actions
10
- server = env['tap.server'] ||= Tap::Server.new
11
-
12
- if path = server.public_path(env['PATH_INFO'])
13
- content = File.read(path)
14
- headers = {
15
- "Last-Modified" => [File.mtime(path).httpdate],
16
- "Content-Type" => [Rack::Mime.mime_type(File.extname(path), 'text/plain')],
17
- "Content-Length" => [content.size.to_s]
18
- }
19
-
20
- [200, headers, [content]]
21
- else
22
- super
23
- end
24
- end
25
-
26
- def index
27
- env_names = {}
28
- server.env.minimap.each do |name, environment|
29
- env_names[environment] = name
30
- end
31
-
32
- render('index.erb', :locals => {:env => server.env, :env_names => env_names}, :layout => true)
33
- end
34
-
35
- def info
36
- if request.post?
37
- app.info
38
- else
39
- render('info.erb', :locals => {:update => true, :content => app.info}, :layout => true)
40
- end
41
- end
42
-
43
- #--
44
- # Currently tail is hard-coded to tail the server log only.
45
- def tail(id=nil)
46
- begin
47
- path = root.subpath(:log, 'server.log')
48
- raise unless File.exists?(path)
49
- rescue
50
- raise Tap::ServerError.new("invalid path", 404)
51
- end
52
-
53
- pos = request['pos'].to_i
54
- if pos > File.size(path)
55
- raise Tap::ServerError.new("tail position out of range (try update)", 500)
56
- end
57
-
58
- content = File.open(path) do |file|
59
- file.pos = pos
60
- file.read
61
- end
62
-
63
- if request.post?
64
- content
65
- else
66
- render('tail.erb', :locals => {
67
- :id => id,
68
- :path => File.basename(path),
69
- :update => true,
70
- :content => content
71
- }, :layout => true)
72
- end
73
- end
74
-
75
- def run
76
- Thread.new { app.run }
77
- redirect("/app/tail")
78
- end
79
-
80
- def stop
81
- app.stop
82
- redirect("/app/info")
83
- end
84
-
85
- def terminate
86
- app.terminate
87
- redirect("/app/info")
88
- end
89
-
90
- def help(key=nil)
91
- end
92
- end
@@ -1,255 +0,0 @@
1
- require 'tap/controller'
2
-
3
- class SchemaController < Tap::Controller
4
- set :default_layout, 'layout.erb'
5
-
6
- # Initializes a new schema and redirects to display.
7
- def index
8
- id = initialize_schema
9
- redirect("/schema/display/#{id}")
10
- end
11
-
12
- # Loads the schema indicated by id and renders 'schema.erb' with the default
13
- # layout.
14
- def display(id)
15
- schema = load_schema(id)
16
- render 'schema.erb', :locals => {
17
- :id => id,
18
- :schema => schema
19
- }, :layout => true
20
- end
21
-
22
- # Updates the specified schema with the request parameters. Update forwards
23
- # the request to the action ('add' or 'remove') specified in the action
24
- # parameter.
25
- def update(id)
26
- case request['action']
27
- when 'add' then add(id)
28
- when 'remove' then remove(id)
29
- when 'echo' then echo
30
- else raise Tap::ServerError, "unknown action: #{request['action']}"
31
- end
32
- end
33
-
34
- # Adds nodes or joins to the schema. Parameters:
35
- #
36
- # nodes[]:: An array of nodes to add to the schema. Each entry is split using
37
- # Shellwords to yield an argv; the argv initializes the node. The
38
- # index of each new node is added to targets[].
39
- # sources[]:: An array of source node indicies used to create a join.
40
- # targets[]:: An array of target node indicies used to create a join (note
41
- # the indicies of new nodes are added to targets).
42
- #
43
- # Add creates and pushes new nodes onto schema as specified in nodes, then
44
- # creates joins between the sources and targets. The join class is inferred
45
- # by Utils.infer_join; if no join can be inferred the join class is
46
- # effectively nil, and consistent with that, the node output for sources
47
- # and the node input for targets is set to nil.
48
- #
49
- # === Notes
50
- #
51
- # The nomenclature for source and target is relative to the join, and may
52
- # seem backwards for the node (ex: 'sources[]=0&targets[]=1' makes a join
53
- # like '0:1')
54
- #
55
- def add(id)
56
- unless request.post?
57
- raise Tap::ServerError, "add must be performed with post"
58
- end
59
-
60
- round = (request['round'] || 0).to_i
61
- outputs = (request['outputs[]'] || []).collect {|index| index.to_i }
62
- inputs = (request['inputs[]'] || []).collect {|index| index.to_i }
63
- nodes = request['nodes[]'] || []
64
-
65
- load_schema(id) do |schema|
66
- nodes.each do |arg|
67
- next unless arg && !arg.empty?
68
-
69
- outputs << schema.nodes.length
70
- schema.nodes << Tap::Support::Node.new(Shellwords.shellwords(arg), round)
71
- end
72
-
73
- if inputs.empty? || outputs.empty?
74
- inputs.each {|index| schema[index].output = nil }
75
- outputs.each {|index| schema[index].input = round }
76
- else
77
-
78
- # temporary
79
- if inputs.length > 1 && outputs.length > 1
80
- raise "multi-way join specified"
81
- end
82
-
83
- schema.set(Tap::Support::Join, inputs, outputs)
84
- end
85
- end
86
-
87
- redirect("/schema/display/#{id}")
88
- end
89
-
90
- # Removes nodes or joins from the schema. Parameters:
91
- #
92
- # sources[]:: An array of source node indicies to remove.
93
- # targets[]:: An array of target node indicies to remove.
94
- #
95
- # Normally remove sets the node.output for each source to nil and the
96
- # node.input for each target to nil. However, if a node is indicated in
97
- # both sources and targets AND it has no join input/output, then it will
98
- # be removed.
99
- #
100
- # === Notes
101
- #
102
- # The nomenclature for source and target is relative to the join, and may
103
- # seem backwards for the node (ex: for the sequence '0:1:2', 'targets[]=1'
104
- # breaks the join '0:1' while 'sources[]=1' breaks the join '1:2'.
105
- #
106
- def remove(id)
107
- unless request.post?
108
- raise Tap::ServerError, "remove must be performed with post"
109
- end
110
-
111
- round = (request['round'] || 0).to_i
112
- outputs = (request['outputs[]'] || []).collect {|index| index.to_i }
113
- inputs = (request['inputs[]'] || []).collect {|index| index.to_i }
114
-
115
- load_schema(id) do |schema|
116
- # Remove joins. Removed indicies are popped to ensure
117
- # that if a join was removed the node will not be.
118
- outputs.delete_if do |index|
119
- next unless node = schema.nodes[index]
120
- if node.input_join
121
- node.input = round
122
- true
123
- else
124
- false
125
- end
126
- end
127
-
128
- inputs.delete_if do |index|
129
- next unless node = schema.nodes[index]
130
- if node.output_join
131
- node.output = nil
132
- true
133
- else
134
- false
135
- end
136
- end
137
-
138
- # Remove nodes. Setting a node to nil causes it's removal during
139
- # compact; orphaned joins are removed during compact as well.
140
- (inputs & outputs).each do |index|
141
- schema.nodes[index] = nil
142
- end
143
- end
144
-
145
- redirect("/schema/display/#{id}")
146
- end
147
-
148
- def submit(id)
149
- case request['action']
150
- when 'save' then save(id)
151
- when 'preview' then preview(id)
152
- when 'echo' then echo
153
- when 'run'
154
- dump_schema(id, schema)
155
- run(id)
156
- else raise Tap::ServerError, "unknown action: #{request['action']}"
157
- end
158
- end
159
-
160
- def save(id)
161
- unless request.post?
162
- raise Tap::ServerError, "submit must be performed with post"
163
- end
164
-
165
- dump_schema(id, schema)
166
- redirect("/schema/display/#{id}")
167
- end
168
-
169
- def preview(id)
170
- response.headers['Content-Type'] = 'text/plain'
171
- render('preview.erb', :locals => {:id => id, :schema => schema})
172
- end
173
-
174
- def run(id)
175
- unless request.post?
176
- raise Tap::ServerError, "run must be performed with post"
177
- end
178
-
179
- # it would be nice to someday put all this on a separate thread...
180
- schema = load_schema(id)
181
- tasks = server.env.tasks
182
- schema.build(app) do |(key, *args)|
183
- if const = tasks.search(key)
184
- const.constantize.parse(args, app) do |help|
185
- raise "help not implemented"
186
- #redirect("/app/help/#{key}")
187
- end
188
- else
189
- raise ArgumentError, "unknown task: #{key}"
190
- end
191
- end
192
-
193
- Thread.new { app.run }
194
- redirect("/app/tail")
195
- end
196
-
197
- protected
198
-
199
- # Parses a Tap::Support::Schema from the request.
200
- def schema
201
- argv = request['argv[]'] || []
202
- argv.delete_if {|arg| arg.empty? }
203
- Tap::Support::Schema.parse(argv)
204
- end
205
-
206
- def initialize_schema
207
- current = root.glob(:schema, "*").collect {|path| File.basename(path).chomp(".yml") }
208
-
209
- id = random_key(current.length)
210
- while current.include?(id)
211
- id = random_key(current.length)
212
- end
213
-
214
- dump_schema(id, schema)
215
- id
216
- end
217
-
218
- def load_schema(id)
219
- unless path = root.filepath(:schema, "#{id}.yml")
220
- raise ServerError, "no schema for id: #{id}"
221
- end
222
- schema = Tap::Support::Schema.load_file(path)
223
-
224
- if block_given?
225
- result = yield(schema)
226
- dump_schema(id, schema)
227
- result
228
- else
229
- schema
230
- end
231
- end
232
-
233
- def dump_schema(id, schema=nil)
234
- root.prepare(:schema, "#{id}.yml") do |file|
235
- file << YAML.dump(schema.dump) if schema
236
- end
237
- end
238
-
239
- def instantiate(*argv)
240
- key = argv.shift
241
- tasc = server.env.tasks.search(key).constantize
242
- tasc.parse(argv)
243
- end
244
-
245
- # helper to echo requests back... good for debugging
246
- def echo # :nodoc:
247
- "<pre>#{request.params.to_yaml}</pre>"
248
- end
249
-
250
- # Generates a random integer key.
251
- def random_key(length) # :nodoc:
252
- length = 1 if length < 1
253
- rand(length * 10000).to_s
254
- end
255
- end