tap-server 0.1.1 → 0.2.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.
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