nitro 0.23.0 → 0.24.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (76) hide show
  1. data/CHANGELOG +350 -0
  2. data/INSTALL +2 -2
  3. data/ProjectInfo +61 -0
  4. data/README +5 -4
  5. data/Rakefile +5 -4
  6. data/bin/nitrogen +3 -1
  7. data/doc/AUTHORS +27 -3
  8. data/doc/RELEASES +193 -0
  9. data/doc/lhttpd.txt +4 -0
  10. data/lib/nitro.rb +1 -1
  11. data/lib/nitro/adapter/cgi.rb +6 -321
  12. data/lib/nitro/adapter/fastcgi.rb +2 -14
  13. data/lib/nitro/adapter/scgi.rb +237 -71
  14. data/lib/nitro/adapter/webrick.rb +25 -7
  15. data/lib/nitro/caching.rb +1 -0
  16. data/lib/nitro/cgi.rb +296 -0
  17. data/lib/nitro/{cookie.rb → cgi/cookie.rb} +0 -0
  18. data/lib/nitro/cgi/http.rb +62 -0
  19. data/lib/nitro/{request.rb → cgi/request.rb} +4 -1
  20. data/lib/nitro/{response.rb → cgi/response.rb} +0 -0
  21. data/lib/nitro/cgi/stream.rb +43 -0
  22. data/lib/nitro/cgi/utils.rb +38 -0
  23. data/lib/nitro/compiler.rb +23 -11
  24. data/lib/nitro/compiler/css.rb +8 -0
  25. data/lib/nitro/compiler/morphing.rb +66 -0
  26. data/lib/nitro/context.rb +21 -30
  27. data/lib/nitro/controller.rb +23 -100
  28. data/lib/nitro/dispatcher.rb +18 -8
  29. data/lib/nitro/element.rb +6 -2
  30. data/lib/nitro/flash.rb +2 -2
  31. data/lib/nitro/mixin/buffer.rb +2 -2
  32. data/lib/nitro/mixin/form.rb +204 -93
  33. data/lib/nitro/mixin/javascript.rb +170 -11
  34. data/lib/nitro/mixin/markup.rb +1 -0
  35. data/lib/nitro/mixin/pager.rb +7 -4
  36. data/lib/nitro/mixin/rss.rb +2 -0
  37. data/lib/nitro/mixin/table.rb +23 -6
  38. data/lib/nitro/mixin/xhtml.rb +2 -2
  39. data/lib/nitro/render.rb +19 -5
  40. data/lib/nitro/scaffold.rb +12 -6
  41. data/lib/nitro/server.rb +4 -6
  42. data/lib/nitro/server/runner.rb +2 -2
  43. data/lib/nitro/session.rb +8 -1
  44. data/lib/nitro/session/file.rb +40 -0
  45. data/lib/part/admin.rb +2 -0
  46. data/lib/part/admin/controller.rb +7 -3
  47. data/lib/part/admin/skin.rb +8 -1
  48. data/lib/part/admin/template/index.xhtml +39 -1
  49. data/proto/public/error.xhtml +5 -3
  50. data/proto/public/js/behaviour.js +254 -254
  51. data/proto/public/js/controls.js +427 -165
  52. data/proto/public/js/dragdrop.js +255 -276
  53. data/proto/public/js/effects.js +476 -277
  54. data/proto/public/js/prototype.js +561 -127
  55. data/proto/public/js/scaffold.js +74 -0
  56. data/proto/public/js/scriptaculous.js +44 -0
  57. data/proto/public/js/util.js +548 -0
  58. data/proto/public/scaffold/list.xhtml +4 -1
  59. data/proto/scgi.rb +333 -0
  60. data/script/scgi_ctl +221 -0
  61. data/script/scgi_service +120 -0
  62. data/test/nitro/adapter/raw_post1.bin +0 -0
  63. data/test/nitro/{tc_cookie.rb → cgi/tc_cookie.rb} +1 -1
  64. data/test/nitro/{tc_request.rb → cgi/tc_request.rb} +1 -1
  65. data/test/nitro/mixin/tc_xhtml.rb +1 -1
  66. data/test/nitro/{adapter/tc_cgi.rb → tc_cgi.rb} +12 -12
  67. data/test/nitro/tc_controller.rb +9 -5
  68. metadata +159 -169
  69. data/benchmark/bench.rb +0 -5
  70. data/benchmark/simple-webrick-n-200.txt +0 -44
  71. data/benchmark/static-webrick-n-200.txt +0 -43
  72. data/benchmark/tiny-lhttpd-n-200-c-5.txt +0 -43
  73. data/benchmark/tiny-webrick-n-200-c-5.txt +0 -44
  74. data/benchmark/tiny-webrick-n-200.txt +0 -44
  75. data/benchmark/tiny2-webrick-n-200.txt +0 -44
  76. data/examples/README +0 -7
@@ -1,9 +1,12 @@
1
1
  <SystemPage name="List of %list_name%">
2
2
  <h2><a href="#@base/new_%name%">New %name%</a></h2>
3
+ <form action="search">
4
+ Search %list_name%: <input type="text" name="q" />&nbsp;<input type="submit" value="Search" /> (not implemented yet)
5
+ </form>
3
6
  <table>
4
7
  <?r for obj in @%list_name% ?>
5
8
  <tr>
6
- <td width="100%">#{obj.to_s}</td>
9
+ <td width="100%"><a href="#@base/edit_%name%/#{obj.oid}">#{obj.to_s}</a></td>
7
10
  <?r if obj.respond_to?(:update_time) ?>
8
11
  <td nowrap="1">#{obj.update_time.stamp(:db)}</td>
9
12
  <?r end ?>
data/proto/scgi.rb ADDED
@@ -0,0 +1,333 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'stringio'
4
+ require 'yaml'
5
+ require 'digest/sha1'
6
+ require 'logger'
7
+ require 'fileutils'
8
+ require 'socket'
9
+ require 'cgi'
10
+ require 'rubygems'
11
+ require 'cmdparse'
12
+ require 'monitor'
13
+
14
+ def log(msg)
15
+ $stderr.print msg,"\n"
16
+ end
17
+
18
+ def error(msg, exc=nil)
19
+ if exc
20
+ $stderr.print "ERROR: #{msg}: #{exc}\n"
21
+ $stderr.puts exc.backtrace
22
+ else
23
+ $stderr.print "ERROR: #{msg}\n"
24
+ end
25
+ end
26
+
27
+
28
+ # Modifies CGI so that we can use it.
29
+ class SCGIFixed < ::CGI
30
+ public :env_table
31
+
32
+ def initialize(params, data, out, *args)
33
+ @env_table = params
34
+ @args = *args
35
+ @input = StringIO.new(data)
36
+ @out = out
37
+ super(*args)
38
+ end
39
+ def args
40
+ @args
41
+ end
42
+ def env_table
43
+ @env_table
44
+ end
45
+ def stdinput
46
+ @input
47
+ end
48
+ def stdoutput
49
+ @out
50
+ end
51
+ end
52
+
53
+
54
+ class SCGIProcessor < Monitor
55
+
56
+ def initialize(settings)
57
+ @env = settings[:env] || "development"
58
+ @debug = settings[:debug] || false
59
+ @host = settings[:host] || "127.0.0.1"
60
+ @port = settings[:port] || "9999"
61
+ @children = settings[:children] || 1
62
+ @pid_file = settings[:pid_file] || "children.yaml"
63
+ @status_dir = settings[:status_dir] || "/tmp"
64
+ @log_file = settings[:logfile] || "log/scgi.log"
65
+ @maxconns = settings[:maxconns]
66
+ @busy_msg = settings[:busy_msg] || "BUSY"
67
+ @settings = settings
68
+ @started = Time.now
69
+ @conns = 0
70
+ @total_conns = 0
71
+ @errors = 0
72
+
73
+ if @maxconns
74
+ @maxconns = @maxconns.to_i
75
+ else
76
+ @maxconns = 2**30-1
77
+ end
78
+
79
+ if settings[:conns_second]
80
+ @throttle_sleep = 1.0/settings[:conns_second].to_i
81
+ end
82
+
83
+ super()
84
+ end
85
+
86
+ def run
87
+ ENV['NITRO_ENV'] = @env
88
+
89
+ require 'nitro'
90
+ server = TCPServer.new(@host, @port)
91
+
92
+ if @debug
93
+ log("Listening for connections on #@host:#@port")
94
+ listen(server)
95
+ else
96
+ childpids = []
97
+ @children.to_i.times do
98
+ # fork each child listening to the same port. very simple yet effective way to spread the load
99
+ # to multiple processes without using threads and still using high performance libevent
100
+ begin
101
+ pid = fork do
102
+ $stderr = open(@log_file,"w")
103
+ $stderr.sync = false
104
+ listen(server)
105
+ end
106
+ childpids << pid
107
+ Process.detach(pid)
108
+ rescue Object
109
+ error("Could not fork child processes. Your system might not support fork. Use -D instead.", $!)
110
+ end
111
+ end
112
+
113
+ # tell the user what the sha1 is so they can check for modification later
114
+ log("#@pid_file will have SHA1 #{Digest::SHA1.hexdigest(YAML.dump(childpids))}")
115
+ log("Record this somewhere so you know if it was modified later by someone else.")
116
+ # all children forked and the pids are now ready to write to the pid file
117
+ open(@pid_file,"w") { |f| f.write(YAML.dump(childpids)) }
118
+ end
119
+ end
120
+
121
+
122
+ def listen(socket)
123
+ thread = Thread.new do
124
+ while true
125
+ handle_client(socket.accept)
126
+ sleep @throttle_sleep if @throttle_sleep
127
+
128
+ @total_conns += 1
129
+ end
130
+ end
131
+
132
+ begin
133
+ thread.join
134
+ rescue Interrupt
135
+ log("Shutting down from SIGINT.")
136
+ rescue Object
137
+ error("while listening for connections on #@host:#@port", $!)
138
+ end
139
+ end
140
+
141
+
142
+ def handle_client(socket)
143
+ Thread.new do
144
+ begin
145
+ synchronize { @conns += 1}
146
+
147
+ len = ""
148
+ # we only read 10 bytes of the length. any request longer than this is invalid
149
+ while len.length <= 10
150
+ c = socket.read(1)
151
+ if c == ':'
152
+ # found the terminal, len now has a length in it so read the payload
153
+ break
154
+ else
155
+ len << c
156
+ end
157
+ end
158
+
159
+ # we should now either have a payload length to get
160
+ payload = socket.read(len.to_i)
161
+ if (c = socket.read(1)) != ','
162
+ error("Malformed request, does not end with ','")
163
+ else
164
+ read_header(socket, payload, @conns)
165
+ end
166
+ rescue IOError
167
+ error("received IOError #$! when handling client. Your web server doesn't like me.")
168
+ rescue Object
169
+ @errors += 1
170
+ error("after accepting client #@host:#@port -- #{$!.class}", $!)
171
+ ensure
172
+ synchronize { @conns -= 1}
173
+ socket.close if not socket.closed?
174
+ end
175
+ end
176
+
177
+ end
178
+
179
+
180
+ def read_header(socket, payload, conns)
181
+ return if socket.closed?
182
+ request = split_body(payload)
183
+ if request and request["CONTENT_LENGTH"]
184
+ length = request["CONTENT_LENGTH"].to_i
185
+ if length > 0
186
+ body = socket.read(length)
187
+ else
188
+ body = ""
189
+ end
190
+
191
+ if @conns > @maxconns
192
+ socket.write("Content-type: text/plain\r\n\r\n")
193
+ socket.write(@busy_msg)
194
+ else
195
+ process_request(request, body, socket)
196
+ end
197
+ end
198
+ end
199
+
200
+
201
+ def process_request(request, body, socket)
202
+ return if socket.closed?
203
+ cgi = SCGIFixed.new(request, body, socket)
204
+ begin
205
+ synchronize do
206
+ # unfortuneatly, the dependencies.rb file is not thread safe and will throw exceptions
207
+ # claiming that Dispatcher is not defined, or that other classes are missing. We have
208
+ # to sync the dispatch call to get around this.
209
+ Dispatcher.dispatch(cgi, ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS, cgi.stdoutput)
210
+ end
211
+ rescue IOError
212
+ error("received IOError #$! when handling client. Your web server doesn't like me.")
213
+ rescue Object => nitro_error
214
+ error("calling Dispatcher.dispatch", nitro_error)
215
+ end
216
+ end
217
+
218
+
219
+ def split_body(data)
220
+ result = {}
221
+ el = data.split("\0")
222
+ i = 0
223
+ len = el.length
224
+ while i < len
225
+ result[el[i]] = el[i+1]
226
+ i += 1
227
+ end
228
+
229
+ return result
230
+ end
231
+
232
+ def status
233
+ pid = Process.pid
234
+ open("#@status_dir/scgi_nitro_status.#{pid}","w") do |f|
235
+ status = {
236
+ 'time' => Time.now, 'pid' => pid, 'settings' => @settings,
237
+ 'env' => @env, 'status_dir' => @status_dir, 'started' => @started,
238
+ 'max_conns' => @maxconns, 'total_conns' => @total_conns,
239
+ 'conns' => @conns, 'errors' => @errors, 'systimes' => Process.times
240
+ }
241
+ f.write(YAML.dump(status))
242
+ end
243
+ end
244
+ end
245
+
246
+
247
+ def signal_children(pidfile, signal)
248
+ if not File.exists? pidfile
249
+ log("No #{pidfile} as specified. Probably nothing running or wrong path.")
250
+ exit 1
251
+ end
252
+
253
+ childpids = YAML.load_file(pidfile)
254
+ childpids.each do |pid|
255
+ begin
256
+ log("Signaling pid #{pid}")
257
+ Process.kill(signal, pid)
258
+ rescue Object
259
+ log("Couldn't send #{signal} signal to #{pid} pid.")
260
+ end
261
+ end
262
+ end
263
+
264
+
265
+ def make_command(parent, name, desc, options)
266
+ cmd = CmdParse::Command.new(name, false )
267
+ cmd.short_desc = desc
268
+ settings = {}
269
+ cmd.options = CmdParse::OptionParserWrapper.new do |opt|
270
+ options.each do |short, long, info, symbol|
271
+ opt.on(short, long, info) {|val| settings[symbol] = val}
272
+ end
273
+ end
274
+ cmd.set_execution_block do |args|
275
+ yield(settings, args)
276
+ end
277
+ parent.add_command(cmd)
278
+ end
279
+
280
+
281
+ cmd = CmdParse::CommandParser.new( true )
282
+ cmd.program_name = 'scgi'
283
+ cmd.program_version = [0, 2, 1]
284
+ cmd.options = CmdParse::OptionParserWrapper.new do |opt|
285
+ opt.separator "Global options:"
286
+ opt.on("--verbose", "Be verbose when outputting info") {|t| $verbose = true }
287
+ end
288
+
289
+ cmd.add_command( CmdParse::HelpCommand.new )
290
+ cmd.add_command( CmdParse::VersionCommand.new )
291
+
292
+ make_command(cmd, 'start', "Start Nitro Application",
293
+ [['-e','--env STRING','Nitro environment', :env],
294
+ ['-D','--[no-]debug', 'Do not fork children, stay in foreground.', :debug],
295
+ ['-h','--host STRING', 'IP address to bind as server', :host],
296
+ ['-p','--port NUMBER', 'Port to bind to', :port],
297
+ ['-c','--children NUMBER', 'Number of children to start (not win32)', :children],
298
+ ['-f','--pid-file PATH', 'Where to read the list of running children', :pid_file],
299
+ ['-l','--log-file PATH', 'Use a different log from from log/scgi.log', :logfile],
300
+ ['-t','--throttle NUMBER', 'Max conn/second to allow.', :conns_second],
301
+ ['-m','--max-conns NUMBER', 'Max simultaneous connections before the busy message', :maxconns],
302
+ ['-b','--busy-msg', 'Busy message given to clients over the max connections ("busy")', :busy_msg],
303
+ ['-s','--status-dir PATH', 'Where to put the status files', :status_dir]]) do |settings, args|
304
+ scgi = SCGIProcessor.new(settings)
305
+ begin
306
+ trap("HUP") { scgi.status }
307
+ rescue Object
308
+ error("Could not setup a SIGHUP handler. You won't be able to get status.")
309
+ end
310
+
311
+ scgi.run
312
+ end
313
+
314
+
315
+ make_command(cmd, 'status', "Get status from all running children",
316
+ [['-s','--status-dir PATH', 'Where to put the status files', :status_dir],
317
+ ['-f','--pid-file PATH', 'Where to read the list of running children', :pid_file]]) do |settings, args|
318
+ signal_children(settings[:pid_file] || 'children.yaml', "HUP")
319
+ log("Status files for each child should show up in the configured status directory (/tmp by default).")
320
+ end
321
+
322
+ make_command(cmd, 'stop', "Stop all running children",
323
+ [['-s','--sig SIGNAL', 'Where to put the status files', :signal],
324
+ ['-n','--[no-]delete', 'Keep the children.yaml file rather than delete', :nodelete],
325
+ ['-f','--pid-file PATH', 'Where to read the list of running children', :pid_file]]) do |settings, args|
326
+ pid_file = settings[:pid_file] || "children.yaml"
327
+ signal_children(pid_file, settings[:signal] || "INT")
328
+ if not settings[:nodelete] and File.exist?(pid_file)
329
+ File.unlink(pid_file)
330
+ end
331
+ end
332
+
333
+ cmd.parse
data/script/scgi_ctl ADDED
@@ -0,0 +1,221 @@
1
+ #!/usr/bin/env ruby
2
+ require 'drb'
3
+ require 'rubygems'
4
+ require 'cmdparse'
5
+ require 'yaml'
6
+ require 'highline'
7
+
8
+ UI = HighLine.new
9
+ DEFAULT_CONFIG = "conf/scgi.yaml"
10
+
11
+ def safe(error)
12
+ begin
13
+ yield
14
+ rescue Object
15
+ STDERR.puts("ERROR: #{error}: #$!")
16
+ exit 1
17
+ end
18
+ end
19
+
20
+ def load_config(file)
21
+ safe("Could not load config") do
22
+ return YAML.load_file(file)
23
+ end
24
+ end
25
+
26
+ def connect(url)
27
+ conn = nil
28
+ safe("Can't connect to #{url}") do
29
+ conn = DRbObject.new(nil, url)
30
+ end
31
+
32
+ safe("Failed communicating with #{url}") do
33
+ yield conn
34
+ end
35
+ end
36
+
37
+ def deduce_url(settings)
38
+ settings[:control_url] || load_config(settings[:config] || DEFAULT_CONFIG)[:control_url]
39
+ end
40
+
41
+ def defaults(settings)
42
+ defaults = nil
43
+ if settings[:merge]
44
+ UI.say("Merging with previous settings.")
45
+ defaults = load_config(settings[:config] || DEFAULT_CONFIG)
46
+ else
47
+ defaults = {
48
+ :env => "production",
49
+ :host => "127.0.0.1",
50
+ :port => 9999,
51
+ :logfile => "log/scgi.log",
52
+ :config => DEFAULT_CONFIG }
53
+ end
54
+
55
+ settings = defaults.merge(settings)
56
+ # fix up the stuff that's not quite right yet
57
+ settings[:control_url] = "druby://127.0.0.1:#{settings[:port]-1000}"
58
+ settings[:port] = settings[:port].to_i
59
+ settings[:throttle] = settings[:throttle].to_i if settings[:throttle]
60
+ settings[:maxconns] = settings[:maxconns].to_i if settings[:maxconns]
61
+
62
+ return settings
63
+ end
64
+
65
+
66
+ def configure(settings)
67
+ settings = defaults(settings)
68
+
69
+ pass = settings[:password] || UI.ask("What password do you want? ")
70
+ salting = ('a' .. 'z').to_a + ('A' .. 'Z').to_a + ('0' .. '9').to_a
71
+ settings[:password] = pass.crypt(salting[rand(salting.length)] + salting[rand(salting.length)])
72
+
73
+ # great, they are not idiots. Well, they can read --help at least.
74
+ safe("Could not write config") do
75
+ open(settings[:config],"w") {|f| f.write(YAML.dump(settings)) }
76
+ UI.say("Configuration settings written to #{settings[:config]}")
77
+ end
78
+ end
79
+
80
+ def password
81
+ if not $password
82
+ $password = UI.ask("Password: ")
83
+ end
84
+ $password
85
+ end
86
+
87
+ def start(cmd, config)
88
+ fork do
89
+ exec cmd, config
90
+ end
91
+ end
92
+
93
+ def status(url)
94
+ connect(url) do |conn|
95
+ s = conn.status(password)
96
+ times = s[:systimes]
97
+
98
+ puts <<-END
99
+ #{UI.color("Status as of #{s[:time]}:",:green,:bold)}
100
+ PID: #{s[:pid]}\tStarted: #{s[:started]}\tEnvironment: #{s[:env]}
101
+ Connected Requests: #{s[:conns]}
102
+ Conns/Second: #{s[:conns_second] || "Not Set"}
103
+ Max Simultaneous Conns: #{s[:max_conns]}
104
+ Shutdown Started: #{s[:shutdown]}
105
+ Processing Time: #{times.utime} #{times.stime} #{times.cutime} #{times.cstime}
106
+ Current Settings:
107
+ #{s[:settings].to_yaml.gsub(/:([a-z])/, ' \1')}
108
+ END
109
+ end
110
+ end
111
+
112
+ def reconfigure(url)
113
+ connect(url) do |conn|
114
+ conn.reconfigure(password)
115
+ end
116
+ end
117
+
118
+ def stop(url, force)
119
+ connect(url) do |conn|
120
+ conn.shutdown(password, force)
121
+ end
122
+ end
123
+
124
+ def restart(url, force)
125
+ connect(url) do |conn|
126
+ conn.restart(password, force)
127
+ end
128
+ end
129
+
130
+ def monitor(url)
131
+ connect(url) do |conn|
132
+ while true
133
+ sleep 3
134
+ puts "\e[2J"
135
+ status(url)
136
+ end
137
+ end
138
+ end
139
+
140
+
141
+ def make_command(parent, name, desc, options)
142
+ cmd = CmdParse::Command.new(name, false )
143
+ cmd.short_desc = desc
144
+ settings = {}
145
+ cmd.options = CmdParse::OptionParserWrapper.new do |opt|
146
+ options.each do |short, long, info, symbol|
147
+ opt.on(short, long, info) {|val| settings[symbol] = val}
148
+ end
149
+ end
150
+ cmd.set_execution_block do |args|
151
+ yield(settings, args)
152
+ end
153
+ parent.add_command(cmd)
154
+ end
155
+
156
+
157
+ cmd = CmdParse::CommandParser.new( true )
158
+ cmd.program_name = "scgi_nitro"
159
+ cmd.program_version = [0, 4, 0]
160
+ cmd.options = CmdParse::OptionParserWrapper.new do |opt|
161
+ opt.separator "Global options:"
162
+ opt.on("--verbose", "Be verbose when outputting info") {|t| $verbose = true }
163
+ end
164
+
165
+ cmd.add_command( CmdParse::HelpCommand.new )
166
+ cmd.add_command( CmdParse::VersionCommand.new )
167
+
168
+ make_command(cmd, 'config', "Configure the SCGI servers",
169
+ [['-e','--env STRING','Nitro environment', :env],
170
+ ['-h','--host STRING', 'IP address to bind as server', :host],
171
+ ['-p','--port NUMBER', 'Port to bind to (starts at 9999)', :port],
172
+ ['-u','--control-url URL', 'DRuby URL to run control on (same as SCGI -1000)', :control_url],
173
+ ['-l','--log-file PATH', 'Use a different log from from log/scgi.log', :logfile],
174
+ ['-t','--throttle NUMBER', 'Max conn/second to allow.', :conns_second],
175
+ ['-m','--max-conns NUMBER', 'Max simultaneous connections before the busy message', :maxconns],
176
+ ['-P','--moron-mode PASSWORD', 'You are an idiot and you want your password on the command line', :password],
177
+ ['-M','--merge', 'Merge new settings with previous rather than defaults', :merge],
178
+ ['-c','--config PATH', 'Config file to use (#{DEFAULT_CONFIG})', :config]]) do |settings, args|
179
+ configure(settings)
180
+ end
181
+
182
+ make_command(cmd, 'start', "Start the application",
183
+ [['-u','--control-url URL', 'DRuby URL to run control on (same as SCGI -1000)', :control_url],
184
+ ['-c','--config PATH', 'Config file to use (#{DEFAULT_CONFIG})', :config]]) do |settings, args|
185
+ cmd = File.dirname(__FILE__) + "/scgi_service"
186
+ start(cmd, settings[:config] || DEFAULT_CONFIG)
187
+ end
188
+
189
+ make_command(cmd, 'reconfig', "Reconfigure the SCGI servers with a new config",
190
+ [['-u','--control-url URL', 'DRuby URL to run control on (same as SCGI -1000)', :control_url],
191
+ ['-c','--config PATH', 'Config file to use (#{DEFAULT_CONFIG})', :config]]) do |settings, args|
192
+ reconfigure(deduce_url(settings))
193
+ end
194
+
195
+ make_command(cmd, 'status', "Get status",
196
+ [['-u','--control-url URL', 'DRuby URL to run control on (same as SCGI -1000)', :control_url],
197
+ ['-c','--config PATH', 'Config file to use (#{DEFAULT_CONFIG})', :config]]) do |settings, args|
198
+ status(deduce_url(settings))
199
+ end
200
+
201
+ make_command(cmd, 'stop', "Stop the application",
202
+ [['-u','--control-url URL', 'DRuby URL to run control on (same as SCGI -1000)', :control_url],
203
+ ['-c','--config PATH', 'Config file to use (#{DEFAULT_CONFIG})', :config],
204
+ ['-f','--force', 'Forced shutdown rather than graceful (default graceful)', :force]]) do |settings, args|
205
+ stop(deduce_url(settings), settings[:force] || false)
206
+ end
207
+
208
+ make_command(cmd, 'restart', "Restart the application",
209
+ [['-u','--control-url URL', 'DRuby URL to run control on (same as SCGI -1000)', :control_url],
210
+ ['-c','--config PATH', 'Config file to use (#{DEFAULT_CONFIG})', :config],
211
+ ['-f','--force', 'Forced shutdown rather than graceful (default graceful)', :force]]) do |settings, args|
212
+ restart(deduce_url(settings), settings[:force] || false)
213
+ end
214
+
215
+ make_command(cmd, 'monitor', "Monitor the application",
216
+ [['-u','--control-url URL', 'DRuby URL to run control on (same as SCGI -1000)', :control_url],
217
+ ['-c','--config PATH', 'Config file to use (#{DEFAULT_CONFIG})', :config]]) do |settings, args|
218
+ monitor(deduce_url(settings))
219
+ end
220
+
221
+ cmd.parse