strelka 0.0.1pre4
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/.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
|