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.
Files changed (40) hide show
  1. data/.gemtest +0 -0
  2. data/History.rdoc +4 -0
  3. data/IDEAS.textile +174 -0
  4. data/Manifest.txt +38 -0
  5. data/README.rdoc +66 -0
  6. data/Rakefile +64 -0
  7. data/bin/leash +403 -0
  8. data/data/strelka/apps/strelka-admin +65 -0
  9. data/data/strelka/apps/strelka-setup +26 -0
  10. data/data/strelka/bootstrap-config.rb +34 -0
  11. data/data/strelka/templates/admin/console.tmpl +21 -0
  12. data/data/strelka/templates/layout.tmpl +30 -0
  13. data/lib/strelka/app/defaultrouter.rb +85 -0
  14. data/lib/strelka/app/filters.rb +70 -0
  15. data/lib/strelka/app/parameters.rb +64 -0
  16. data/lib/strelka/app/plugins.rb +205 -0
  17. data/lib/strelka/app/routing.rb +140 -0
  18. data/lib/strelka/app/templating.rb +157 -0
  19. data/lib/strelka/app.rb +175 -0
  20. data/lib/strelka/behavior/plugin.rb +36 -0
  21. data/lib/strelka/constants.rb +53 -0
  22. data/lib/strelka/httprequest.rb +52 -0
  23. data/lib/strelka/logging.rb +241 -0
  24. data/lib/strelka/mixins.rb +143 -0
  25. data/lib/strelka/process.rb +19 -0
  26. data/lib/strelka.rb +40 -0
  27. data/spec/data/layout.tmpl +3 -0
  28. data/spec/data/main.tmpl +1 -0
  29. data/spec/lib/constants.rb +32 -0
  30. data/spec/lib/helpers.rb +134 -0
  31. data/spec/strelka/app/defaultrouter_spec.rb +215 -0
  32. data/spec/strelka/app/parameters_spec.rb +74 -0
  33. data/spec/strelka/app/plugins_spec.rb +167 -0
  34. data/spec/strelka/app/routing_spec.rb +139 -0
  35. data/spec/strelka/app/templating_spec.rb +169 -0
  36. data/spec/strelka/app_spec.rb +160 -0
  37. data/spec/strelka/httprequest_spec.rb +54 -0
  38. data/spec/strelka/logging_spec.rb +72 -0
  39. data/spec/strelka_spec.rb +27 -0
  40. 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