strelka 0.0.1pre4
Sign up to get free protection for your applications and to get access to all the features.
- data/.gemtest +0 -0
- data/History.rdoc +4 -0
- data/IDEAS.textile +174 -0
- data/Manifest.txt +38 -0
- data/README.rdoc +66 -0
- data/Rakefile +64 -0
- data/bin/leash +403 -0
- data/data/strelka/apps/strelka-admin +65 -0
- data/data/strelka/apps/strelka-setup +26 -0
- data/data/strelka/bootstrap-config.rb +34 -0
- data/data/strelka/templates/admin/console.tmpl +21 -0
- data/data/strelka/templates/layout.tmpl +30 -0
- data/lib/strelka/app/defaultrouter.rb +85 -0
- data/lib/strelka/app/filters.rb +70 -0
- data/lib/strelka/app/parameters.rb +64 -0
- data/lib/strelka/app/plugins.rb +205 -0
- data/lib/strelka/app/routing.rb +140 -0
- data/lib/strelka/app/templating.rb +157 -0
- data/lib/strelka/app.rb +175 -0
- data/lib/strelka/behavior/plugin.rb +36 -0
- data/lib/strelka/constants.rb +53 -0
- data/lib/strelka/httprequest.rb +52 -0
- data/lib/strelka/logging.rb +241 -0
- data/lib/strelka/mixins.rb +143 -0
- data/lib/strelka/process.rb +19 -0
- data/lib/strelka.rb +40 -0
- data/spec/data/layout.tmpl +3 -0
- data/spec/data/main.tmpl +1 -0
- data/spec/lib/constants.rb +32 -0
- data/spec/lib/helpers.rb +134 -0
- data/spec/strelka/app/defaultrouter_spec.rb +215 -0
- data/spec/strelka/app/parameters_spec.rb +74 -0
- data/spec/strelka/app/plugins_spec.rb +167 -0
- data/spec/strelka/app/routing_spec.rb +139 -0
- data/spec/strelka/app/templating_spec.rb +169 -0
- data/spec/strelka/app_spec.rb +160 -0
- data/spec/strelka/httprequest_spec.rb +54 -0
- data/spec/strelka/logging_spec.rb +72 -0
- data/spec/strelka_spec.rb +27 -0
- metadata +226 -0
data/bin/leash
ADDED
@@ -0,0 +1,403 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'mongrel2'
|
4
|
+
require 'mongrel2/config'
|
5
|
+
require 'strelka'
|
6
|
+
|
7
|
+
require 'pp'
|
8
|
+
require 'fileutils'
|
9
|
+
require 'shellwords'
|
10
|
+
require 'tnetstring'
|
11
|
+
|
12
|
+
require 'trollop'
|
13
|
+
require 'highline'
|
14
|
+
|
15
|
+
# Have to do it this way to avoid the vendored 'sysexits' under OSX.
|
16
|
+
gem 'sysexits'
|
17
|
+
require 'sysexits'
|
18
|
+
|
19
|
+
|
20
|
+
# A tool for setting up and controlling a Mongrel2 cluster.
|
21
|
+
class Strelka::LeashCommand
|
22
|
+
extend ::Sysexits
|
23
|
+
include Sysexits,
|
24
|
+
Strelka::Loggable,
|
25
|
+
Strelka::Constants
|
26
|
+
|
27
|
+
# Make a HighLine color scheme
|
28
|
+
COLOR_SCHEME = HighLine::ColorScheme.new do |scheme|
|
29
|
+
scheme[:header] = [ :bold, :yellow ]
|
30
|
+
scheme[:subheader] = [ :bold, :white ]
|
31
|
+
scheme[:key] = [ :white ]
|
32
|
+
scheme[:value] = [ :bold, :white ]
|
33
|
+
scheme[:error] = [ :red ]
|
34
|
+
scheme[:warning] = [ :yellow ]
|
35
|
+
scheme[:message] = [ :reset ]
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
# Path to the default history file for 'shell' mode
|
40
|
+
HISTORY_FILE = Pathname( "~/.leash.history" )
|
41
|
+
|
42
|
+
# Number of items to store in history by default
|
43
|
+
DEFAULT_HISTORY_SIZE = 100
|
44
|
+
|
45
|
+
# The prompt the 'shell' mode should show
|
46
|
+
PROMPT = 'leash> '
|
47
|
+
|
48
|
+
# The data directory in the project if that exists, otherwise the gem datadir
|
49
|
+
DATADIR = if File.directory?( 'data/strelka' )
|
50
|
+
Pathname( 'data/strelka' )
|
51
|
+
elsif path = Gem.datadir('strelka')
|
52
|
+
Pathname( path )
|
53
|
+
else
|
54
|
+
raise ScriptError, "can't find the data directory!"
|
55
|
+
end
|
56
|
+
|
57
|
+
|
58
|
+
# Class instance variables
|
59
|
+
@command_help = Hash.new {|h,k| h[k] = { :desc => nil, :usage => ''} }
|
60
|
+
@prompt = @option_parser = nil
|
61
|
+
|
62
|
+
|
63
|
+
### Add a help string for the given +command+.
|
64
|
+
def self::help( command, helpstring=nil )
|
65
|
+
if helpstring
|
66
|
+
@command_help[ command.to_sym ][:desc] = helpstring
|
67
|
+
end
|
68
|
+
|
69
|
+
return @command_help[ command.to_sym ][:desc]
|
70
|
+
end
|
71
|
+
|
72
|
+
|
73
|
+
### Add/fetch the +usagestring+ for +command+.
|
74
|
+
def self::usage( command, usagestring=nil )
|
75
|
+
if usagestring
|
76
|
+
prefix = usagestring[ /\A(\s+)/, 1 ]
|
77
|
+
usagestring.gsub!( /^#{prefix}/m, '' ) if prefix
|
78
|
+
|
79
|
+
@command_help[ command.to_sym ][:usage] = usagestring
|
80
|
+
end
|
81
|
+
|
82
|
+
return @command_help[ command.to_sym ][:usage]
|
83
|
+
end
|
84
|
+
|
85
|
+
|
86
|
+
### Return the global Highline prompt object, creating it if necessary.
|
87
|
+
def self::prompt
|
88
|
+
unless @prompt
|
89
|
+
@prompt = HighLine.new
|
90
|
+
@prompt.wrap_at = @prompt.output_cols - 10
|
91
|
+
end
|
92
|
+
|
93
|
+
return @prompt
|
94
|
+
end
|
95
|
+
|
96
|
+
|
97
|
+
### Run the utility with the given +args+.
|
98
|
+
def self::run( args )
|
99
|
+
HighLine.color_scheme = COLOR_SCHEME
|
100
|
+
|
101
|
+
oparser = self.make_option_parser
|
102
|
+
opts = Trollop.with_standard_exception_handling( oparser ) do
|
103
|
+
oparser.parse( args )
|
104
|
+
end
|
105
|
+
|
106
|
+
command = oparser.leftovers.shift
|
107
|
+
self.new( opts ).run( command, *oparser.leftovers )
|
108
|
+
exit :ok
|
109
|
+
|
110
|
+
rescue => err
|
111
|
+
Strelka.logger.fatal "Oops: %s: %s" % [ err.class.name, err.message ]
|
112
|
+
Strelka.logger.debug { ' ' + err.backtrace.join("\n ") }
|
113
|
+
|
114
|
+
exit :software_error
|
115
|
+
end
|
116
|
+
|
117
|
+
|
118
|
+
### Return a String that describes the available commands, e.g., for the 'help'
|
119
|
+
### command.
|
120
|
+
def self::make_command_table
|
121
|
+
commands = self.available_commands
|
122
|
+
|
123
|
+
# Build the command table
|
124
|
+
col1len = commands.map( &:length ).max
|
125
|
+
return commands.collect do |cmd|
|
126
|
+
helptext = self.help( cmd.to_sym ) or next # no help == invisible command
|
127
|
+
"%s %s" % [
|
128
|
+
self.prompt.color(cmd.rjust(col1len), :key),
|
129
|
+
self.prompt.color(helptext, :value)
|
130
|
+
]
|
131
|
+
end.compact
|
132
|
+
end
|
133
|
+
|
134
|
+
|
135
|
+
### Return an Array of the available commands.
|
136
|
+
def self::available_commands
|
137
|
+
return self.public_instance_methods( false ).
|
138
|
+
map( &:to_s ).
|
139
|
+
grep( /_command$/ ).
|
140
|
+
map {|methodname| methodname.sub(/_command$/, '') }.
|
141
|
+
sort
|
142
|
+
end
|
143
|
+
|
144
|
+
|
145
|
+
### Create and configure a command-line option parser for the command.
|
146
|
+
### Returns a Trollop::Parser.
|
147
|
+
def self::make_option_parser
|
148
|
+
unless @option_parser
|
149
|
+
progname = File.basename( $0 )
|
150
|
+
default_configdb = Mongrel2::DEFAULT_CONFIG_URI
|
151
|
+
|
152
|
+
# Make a list of the log level names and the available commands
|
153
|
+
loglevels = Strelka::Logging::LOG_LEVELS.
|
154
|
+
sort_by {|name,lvl| lvl }.
|
155
|
+
collect {|name,lvl| name.to_s }.
|
156
|
+
join( ', ' )
|
157
|
+
command_table = self.make_command_table
|
158
|
+
|
159
|
+
@option_parser = Trollop::Parser.new do
|
160
|
+
banner "Take your Mongrel(2) for a walk:"
|
161
|
+
|
162
|
+
text ''
|
163
|
+
command_table.each {|line| text(line) }
|
164
|
+
text ''
|
165
|
+
|
166
|
+
text 'Global Options'
|
167
|
+
opt :config, "Specify the configfile to use.",
|
168
|
+
:default => DEFAULT_CONFIG_URI
|
169
|
+
text ''
|
170
|
+
|
171
|
+
text 'Other Options:'
|
172
|
+
opt :debug, "Turn debugging on. Also sets the --loglevel to 'debug'."
|
173
|
+
opt :loglevel, "Set the logging level. Must be one of: #{loglevels}",
|
174
|
+
:default => Strelka::Logging::LOG_LEVEL_NAMES[ Strelka.logger.level ]
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
return @option_parser
|
179
|
+
end
|
180
|
+
|
181
|
+
|
182
|
+
#################################################################
|
183
|
+
### I N S T A N C E M E T H O D S
|
184
|
+
#################################################################
|
185
|
+
|
186
|
+
### Create a new instance of the command and set it up with the given
|
187
|
+
### +options+.
|
188
|
+
def initialize( options )
|
189
|
+
Strelka.logger.formatter = Strelka::Logging::ColorFormatter.new( Strelka.logger )
|
190
|
+
@options = options
|
191
|
+
@shellmode = false
|
192
|
+
|
193
|
+
if @options.debug
|
194
|
+
$DEBUG = true
|
195
|
+
$VERBOSE = true
|
196
|
+
Strelka.logger.level = Logger::DEBUG
|
197
|
+
elsif @options.loglevel
|
198
|
+
Strelka.logger.level = Strelka::Logging::LOG_LEVELS[ @options.loglevel ]
|
199
|
+
end
|
200
|
+
|
201
|
+
Mongrel2::Config.configure( :configdb => @options.config )
|
202
|
+
end
|
203
|
+
|
204
|
+
|
205
|
+
######
|
206
|
+
public
|
207
|
+
######
|
208
|
+
|
209
|
+
# The Trollop options hash the command will read its configuration from
|
210
|
+
attr_reader :options
|
211
|
+
|
212
|
+
# True if running in shell mode
|
213
|
+
attr_reader :shellmode
|
214
|
+
|
215
|
+
|
216
|
+
# Delegate the instance #prompt method to the class method instead
|
217
|
+
define_method( :prompt, &self.method(:prompt) )
|
218
|
+
|
219
|
+
|
220
|
+
### Run the command with the specified +command+ and +args+.
|
221
|
+
def run( command, *args )
|
222
|
+
command ||= 'shell'
|
223
|
+
cmd_method = nil
|
224
|
+
|
225
|
+
begin
|
226
|
+
cmd_method = self.method( "#{command}_command" )
|
227
|
+
rescue NoMethodError => err
|
228
|
+
error "No such command"
|
229
|
+
exit :usage
|
230
|
+
end
|
231
|
+
|
232
|
+
cmd_method.call( *args )
|
233
|
+
end
|
234
|
+
|
235
|
+
|
236
|
+
#
|
237
|
+
# Commands
|
238
|
+
#
|
239
|
+
|
240
|
+
### The 'help' command
|
241
|
+
def help_command( *args )
|
242
|
+
|
243
|
+
# Subcommand help
|
244
|
+
if !args.empty?
|
245
|
+
command = args.shift
|
246
|
+
|
247
|
+
if self.class.available_commands.include?( command )
|
248
|
+
header( self.class.help(command) )
|
249
|
+
desc = "\n" + 'Usage: ' + command + ' ' + self.class.usage(command) + "\n"
|
250
|
+
message( desc )
|
251
|
+
else
|
252
|
+
error "No such command %p" % [ command ]
|
253
|
+
end
|
254
|
+
|
255
|
+
# Help by itself show the table of available commands
|
256
|
+
else
|
257
|
+
command_table = self.class.make_command_table
|
258
|
+
header "Available Commands"
|
259
|
+
message( *command_table )
|
260
|
+
end
|
261
|
+
|
262
|
+
end
|
263
|
+
help :help, "Show help for a single COMMAND if given, or list available commands if not"
|
264
|
+
usage :help, "[COMMAND]"
|
265
|
+
|
266
|
+
|
267
|
+
### The 'setup' command
|
268
|
+
def setup_command( *args )
|
269
|
+
port = args.shift || DEFAULT_ADMIN_PORT
|
270
|
+
self.setup_bootstrap_config( port )
|
271
|
+
m2pid = self.start_admin_mongrel2
|
272
|
+
apppid = self.start_admin_app
|
273
|
+
|
274
|
+
header "Okay, point a browser at http://localhost:#{port}/"
|
275
|
+
|
276
|
+
rescue
|
277
|
+
message "Killing admin mongrel2..."
|
278
|
+
Process.kill( :TERM, m2pid ) if m2pid
|
279
|
+
|
280
|
+
message "Killing admin app..."
|
281
|
+
Process.kill( apppid, :TERM ) if apppid
|
282
|
+
|
283
|
+
ensure
|
284
|
+
results = Process.waitall
|
285
|
+
message "Done: %p" % [ results ]
|
286
|
+
end
|
287
|
+
help :setup, "Set up a new Strelka environment and start the admin server."
|
288
|
+
usage :setup, "[PORT]"
|
289
|
+
|
290
|
+
|
291
|
+
### Install a bootstrap config database for the admin server, which will run
|
292
|
+
### on localhost at the specified +port+.
|
293
|
+
def setup_bootstrap_config( port )
|
294
|
+
configfile = DATADIR + 'bootstrap-config.rb'
|
295
|
+
runspace = Module.new do
|
296
|
+
extend Mongrel2::Config::DSL, FileUtils::Verbose
|
297
|
+
end
|
298
|
+
|
299
|
+
header "Loading config from #{configfile}"
|
300
|
+
source = configfile.read
|
301
|
+
Mongrel2::Config.init_database!
|
302
|
+
runspace.module_eval( source, configfile.to_s, 0 )
|
303
|
+
end
|
304
|
+
|
305
|
+
|
306
|
+
### Start the Mongrel2 instance that serves as the front end of the admin server.
|
307
|
+
def start_admin_mongrel2
|
308
|
+
server = Mongrel2::Config.servers.find {|s| s.name == 'adminserver' } or
|
309
|
+
raise ScriptError, "Ack! No server named 'adminserver' in #{Mongrel2::Config.pathname}"
|
310
|
+
|
311
|
+
message "Starting mongrel2..."
|
312
|
+
cmd = [ 'mongrel2', 'mongrel2 (Strelka Admin)' ]
|
313
|
+
args = [ Mongrel2::Config.pathname.to_s, server.uuid ]
|
314
|
+
|
315
|
+
return spawn( cmd, *args )
|
316
|
+
end
|
317
|
+
|
318
|
+
|
319
|
+
### Start the admin app.
|
320
|
+
def start_admin_app
|
321
|
+
message "Starting up control app..."
|
322
|
+
|
323
|
+
pid = fork do
|
324
|
+
appsdir = DATADIR + 'apps'
|
325
|
+
templatedir = DATADIR + 'templates'
|
326
|
+
strelka_admin = appsdir + 'strelka-admin'
|
327
|
+
|
328
|
+
Kernel.load( strelka_admin )
|
329
|
+
|
330
|
+
Inversion::Template.configure( :template_paths => [templatedir] )
|
331
|
+
Strelka::AdminConsole.run( 'admin-console' )
|
332
|
+
|
333
|
+
exit!
|
334
|
+
end
|
335
|
+
|
336
|
+
return pid
|
337
|
+
end
|
338
|
+
|
339
|
+
|
340
|
+
### The 'version' command
|
341
|
+
def version_command( *args )
|
342
|
+
message( "<%= color 'Version:', :header %> " + Mongrel2.version_string(true) )
|
343
|
+
end
|
344
|
+
help :version, "Prints the Ruby-Mongrel2 version."
|
345
|
+
|
346
|
+
|
347
|
+
#
|
348
|
+
# Utility methods
|
349
|
+
#
|
350
|
+
|
351
|
+
### Output normal output
|
352
|
+
def message( *parts )
|
353
|
+
self.prompt.say( parts.map(&:to_s).join($/) )
|
354
|
+
end
|
355
|
+
|
356
|
+
|
357
|
+
### Output the given +text+ highlighted as a header.
|
358
|
+
def header( text )
|
359
|
+
message( self.prompt.color(text, :header) )
|
360
|
+
end
|
361
|
+
|
362
|
+
|
363
|
+
### Output the given +text+ highlighted as an error.
|
364
|
+
def error( text )
|
365
|
+
message( self.prompt.color(text, :error) )
|
366
|
+
end
|
367
|
+
|
368
|
+
|
369
|
+
### Read command line history from HISTORY_FILE
|
370
|
+
def read_history
|
371
|
+
histfile = HISTORY_FILE.expand_path
|
372
|
+
|
373
|
+
if histfile.exist?
|
374
|
+
lines = histfile.readlines.collect {|line| line.chomp }
|
375
|
+
self.log.debug "Read %d saved history commands from %s." % [ lines.length, histfile ]
|
376
|
+
Readline::HISTORY.push( *lines )
|
377
|
+
else
|
378
|
+
self.log.debug "History file '%s' was empty or non-existant." % [ histfile ]
|
379
|
+
end
|
380
|
+
end
|
381
|
+
|
382
|
+
|
383
|
+
### Save command line history to HISTORY_FILE
|
384
|
+
def save_history
|
385
|
+
histfile = HISTORY_FILE.expand_path
|
386
|
+
|
387
|
+
lines = Readline::HISTORY.to_a.reverse.uniq.reverse
|
388
|
+
lines = lines[ -DEFAULT_HISTORY_SIZE, DEFAULT_HISTORY_SIZE ] if
|
389
|
+
lines.length > DEFAULT_HISTORY_SIZE
|
390
|
+
|
391
|
+
self.log.debug "Saving %d history lines to %s." % [ lines.length, histfile ]
|
392
|
+
|
393
|
+
histfile.open( File::WRONLY|File::CREAT|File::TRUNC ) do |ofh|
|
394
|
+
ofh.puts( *lines )
|
395
|
+
end
|
396
|
+
end
|
397
|
+
|
398
|
+
|
399
|
+
end # class Strelka::LeashCommand
|
400
|
+
|
401
|
+
|
402
|
+
Strelka::LeashCommand.run( ARGV.dup )
|
403
|
+
|
@@ -0,0 +1,65 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'pathname'
|
4
|
+
require 'inversion'
|
5
|
+
require 'strelka'
|
6
|
+
|
7
|
+
# The Strelka admin web console.
|
8
|
+
class Strelka::AdminConsole < Strelka::App
|
9
|
+
plugins :templating, :routing
|
10
|
+
|
11
|
+
default_content_type 'text/html'
|
12
|
+
|
13
|
+
layout 'layout.tmpl'
|
14
|
+
templates \
|
15
|
+
:console => 'admin/console.tmpl'
|
16
|
+
|
17
|
+
|
18
|
+
### Initialize some application data.
|
19
|
+
def initialize( * )
|
20
|
+
super
|
21
|
+
|
22
|
+
Mongrel2.logger = Strelka.logger
|
23
|
+
Inversion.logger = Strelka.logger
|
24
|
+
Configurability.logger = Strelka.logger
|
25
|
+
|
26
|
+
@control = Mongrel2::Control.new
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
# GET / -- console view
|
31
|
+
get do |req|
|
32
|
+
tmpl = self.template( :console )
|
33
|
+
tmpl.control = @control
|
34
|
+
tmpl.servers = Mongrel2::Config.servers
|
35
|
+
|
36
|
+
tmpl.strelka_version = Strelka.version_string( true )
|
37
|
+
tmpl.mongrel2_version = Mongrel2.version_string( true )
|
38
|
+
tmpl.inversion_version = Inversion.version_string( true )
|
39
|
+
|
40
|
+
return tmpl
|
41
|
+
end
|
42
|
+
|
43
|
+
end # class Strelka::AdminConsole
|
44
|
+
|
45
|
+
if __FILE__ == $0
|
46
|
+
datadir = Pathname( __FILE__ ).dirname.parent
|
47
|
+
templatedir = datadir + 'templates'
|
48
|
+
|
49
|
+
# Log to the screen if STDERR is opened to one
|
50
|
+
if $stderr.tty?
|
51
|
+
Strelka.logger = Logger.new( $stderr )
|
52
|
+
else
|
53
|
+
Strelka.logger = Logger.new( 'admin-console.log' )
|
54
|
+
end
|
55
|
+
|
56
|
+
Strelka.logger.level = $VERBOSE ? Logger::DEBUG : Logger::INFO
|
57
|
+
|
58
|
+
Inversion::Template.configure( :template_paths => [templatedir] )
|
59
|
+
Mongrel2::Config.configure( :configdb => Strelka::Constants::DEFAULT_CONFIG_URI )
|
60
|
+
|
61
|
+
Strelka::AdminConsole.run( 'admin-console' )
|
62
|
+
end
|
63
|
+
|
64
|
+
|
65
|
+
|
@@ -0,0 +1,26 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'inversion'
|
4
|
+
require 'strelka'
|
5
|
+
|
6
|
+
# The Strelka setup web application.
|
7
|
+
class Strelka::SetupProcess < Strelka::Process
|
8
|
+
|
9
|
+
layout 'setup/layout.tmpl'
|
10
|
+
|
11
|
+
views :step1 => 'setup/step1',
|
12
|
+
:step2 => 'setup/step2'
|
13
|
+
|
14
|
+
|
15
|
+
### Progress through the setup process.
|
16
|
+
def main( req )
|
17
|
+
params = show( :step1 )
|
18
|
+
params = show( :step2, params )
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
end # class Strelka::AdminConsole
|
23
|
+
|
24
|
+
|
25
|
+
Strelka::SetupProcess.run( 'strelka-setup' )
|
26
|
+
|
@@ -0,0 +1,34 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'mongrel2/config'
|
4
|
+
include Mongrel2::Config::DSL
|
5
|
+
|
6
|
+
require 'strelka/constants'
|
7
|
+
include Strelka::Constants
|
8
|
+
|
9
|
+
# This is the config that's loaded by 'leash setup' to get the admin server
|
10
|
+
# up and running.
|
11
|
+
|
12
|
+
server 'admin' do
|
13
|
+
name 'adminserver'
|
14
|
+
port DEFAULT_ADMIN_PORT
|
15
|
+
access_log '/logs/admin-access.log'
|
16
|
+
error_log '/logs/admin-error.log'
|
17
|
+
pid_file '/run/admin.pid'
|
18
|
+
|
19
|
+
default_host 'localhost'
|
20
|
+
|
21
|
+
host 'localhost' do
|
22
|
+
route '/', handler( 'tcp://127.0.0.1:19999', 'admin-console' )
|
23
|
+
|
24
|
+
route '/css', directory( 'data/strelka/static/css/', 'base.css', 'text/css' )
|
25
|
+
route '/images', directory( 'data/strelka/static/images/' )
|
26
|
+
route '/fonts', directory( 'data/strelka/static/fonts/' )
|
27
|
+
route '/js', directory( 'data/strelka/static/js/', 'index.js', 'text/javascript' )
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
setting "control_port", 'ipc://run/admin-control'
|
32
|
+
|
33
|
+
mkdir_p 'logs'
|
34
|
+
mkdir_p 'run'
|
@@ -0,0 +1,21 @@
|
|
1
|
+
<?publish title ?>Admin Console<?end publish ?>
|
2
|
+
|
3
|
+
<p>The admin console. Is you is, or is you isn't?</p>
|
4
|
+
|
5
|
+
<ul>
|
6
|
+
<li>Strelka: <?attr strelka_version ?></li>
|
7
|
+
<li>Mongrel2: <?attr mongrel2_version ?></li>
|
8
|
+
<li>Inversion: <?attr inversion_version ?></li>
|
9
|
+
</ul>
|
10
|
+
|
11
|
+
|
12
|
+
<section id="mongrel2-status">
|
13
|
+
<header>
|
14
|
+
<h1>Mongrel Status</h1>
|
15
|
+
</header>
|
16
|
+
|
17
|
+
<table>
|
18
|
+
|
19
|
+
</table>
|
20
|
+
</section>
|
21
|
+
|
@@ -0,0 +1,30 @@
|
|
1
|
+
<!doctype html>
|
2
|
+
<head>
|
3
|
+
<meta charset="utf-8">
|
4
|
+
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
5
|
+
|
6
|
+
<title>Strelka: <?subscribe title ?></title>
|
7
|
+
<meta name="author" content="Michael Granger">
|
8
|
+
|
9
|
+
<meta name="viewport" content="width=device-width,initial-scale=1">
|
10
|
+
|
11
|
+
<link rel="stylesheet" href="/css/master.css" type="text/css" media="screen"
|
12
|
+
title="no title" charset="utf-8" />
|
13
|
+
</head>
|
14
|
+
|
15
|
+
<body>
|
16
|
+
|
17
|
+
<header>
|
18
|
+
<h1><?subscribe title ?></h1>
|
19
|
+
</header>
|
20
|
+
|
21
|
+
<section id="dump">
|
22
|
+
<?attr body ?>
|
23
|
+
</section>
|
24
|
+
|
25
|
+
<footer>
|
26
|
+
<p><tt>$Id$</tt></p>
|
27
|
+
</footer>
|
28
|
+
|
29
|
+
</body>
|
30
|
+
</html>
|
@@ -0,0 +1,85 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'strelka' unless defined?( Strelka )
|
4
|
+
require 'strelka/app' unless defined?( Strelka::App )
|
5
|
+
|
6
|
+
# Simple (dumb?) request router for Strelka::App-based applications.
|
7
|
+
class Strelka::App::DefaultRouter
|
8
|
+
include Strelka::Loggable
|
9
|
+
|
10
|
+
### Create a new router that will route requests according to the specified
|
11
|
+
### +routes+. Each route is a tuple of the form:
|
12
|
+
###
|
13
|
+
### [
|
14
|
+
### <http_verb>, # The HTTP verb as a Symbol (e.g., :GET, :POST, etc.)
|
15
|
+
### <path_array>, # An Array of the parts of the path, as Strings and Regexps.
|
16
|
+
### <action>, # A #to_proc-able object to invoke when the route is matched
|
17
|
+
### <options_hash>, # The hash of route config options
|
18
|
+
### ]
|
19
|
+
def initialize( routes=[] )
|
20
|
+
@routes = Hash.new {|routes, verb| routes[verb] = {} }
|
21
|
+
routes.each do |tuple|
|
22
|
+
self.log.debug " adding route: %p" % [ tuple ]
|
23
|
+
self.add_route( *tuple )
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
######
|
29
|
+
public
|
30
|
+
######
|
31
|
+
|
32
|
+
# A Hash, keyed by Regexps, that contains the routing logic
|
33
|
+
attr_reader :routes
|
34
|
+
|
35
|
+
|
36
|
+
### Add a route for the specified +verb+, +path+, and +options+ that will return
|
37
|
+
### +action+ when a request matches them.
|
38
|
+
def add_route( verb, path, action, options={} )
|
39
|
+
re = Regexp.compile( path.join('/') )
|
40
|
+
|
41
|
+
# Make the Hash for the specified HTTP verb if it hasn't been
|
42
|
+
self.routes[ verb ][ re ] = { :options => options, :action => action }
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
### Determine the most-specific route for the specified +request+ and return
|
47
|
+
### the #to_proc-able object that handles it.
|
48
|
+
def route_request( request )
|
49
|
+
verb = request.verb
|
50
|
+
path = request.app_path || ''
|
51
|
+
route = nil
|
52
|
+
|
53
|
+
# Strip the leading '/'
|
54
|
+
path.slice!( 0, 1 ) if path.start_with?( '/' )
|
55
|
+
|
56
|
+
verbroutes = @routes[ verb ] or return nil
|
57
|
+
longestmatch = verbroutes.keys.inject( nil ) do |longestmatch, pattern|
|
58
|
+
self.log.debug "Matching pattern %p; longest match so far: %p" %
|
59
|
+
[ pattern, longestmatch ]
|
60
|
+
|
61
|
+
# If the pattern doesn't match, keep the longest match and move on to the next
|
62
|
+
match = pattern.match( path ) or next longestmatch
|
63
|
+
|
64
|
+
# If there was no previous match, or this match was longer, keep it
|
65
|
+
self.log.debug " matched: %p (size = %d)" % [ match[0], match[0].length ]
|
66
|
+
next match if longestmatch.nil? || match[0].length > longestmatch[0].length
|
67
|
+
|
68
|
+
# Otherwise just keep the previous match
|
69
|
+
self.log.debug " kept longer match %p (size = %d)" %
|
70
|
+
[ longestmatch[0], longestmatch[0].length ]
|
71
|
+
longestmatch
|
72
|
+
end
|
73
|
+
|
74
|
+
# If there wasn't a match, abort
|
75
|
+
return nil unless longestmatch
|
76
|
+
|
77
|
+
# The best route is the one with the key of the regexp of the
|
78
|
+
# longest match
|
79
|
+
route = verbroutes[ longestmatch.regexp ]
|
80
|
+
|
81
|
+
# Bind the method to the app and
|
82
|
+
return route[:action]
|
83
|
+
end
|
84
|
+
|
85
|
+
end # class Strelka::App::DefaultRouter
|