maveric 0.3.1 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/maveric.rb +311 -451
- data/lib/maveric/extensions.rb +20 -17
- data/lib/maveric/fastcgi.rb +14 -10
- data/lib/maveric/mongrel.rb +10 -30
- data/lib/maveric/sessions.rb +19 -16
- data/lib/maveric/webrick.rb +0 -3
- metadata +7 -15
data/lib/maveric.rb
CHANGED
@@ -3,34 +3,33 @@
|
|
3
3
|
#
|
4
4
|
# == Resources
|
5
5
|
#
|
6
|
-
# Maveric can normally be found on
|
7
|
-
#
|
6
|
+
# Maveric releases can normally be found on
|
7
|
+
# {rubyforge}[http://rubyforge.org/projects/maveric/].
|
8
|
+
# Documentation sits at {rubyforge}[http://maveric.rubyforge.org/] as well.
|
9
|
+
# Maveric's code base resides at rubyforge as well, but will eventually be
|
10
|
+
# located at {devjavu}[http://maveric.devjavu.com/].
|
8
11
|
#
|
9
12
|
# Maveric is also listed at the RAA as
|
10
13
|
# {maveric}[http://raa.ruby-lang.org/project/maveric/]. This page lags a bit
|
11
|
-
# behind rubyforge.
|
12
|
-
#
|
13
|
-
# Maveric's home away from home is http://code.stadik.net which contains
|
14
|
-
# many other things.
|
14
|
+
# behind rubyforge in terms of updates.
|
15
15
|
#
|
16
16
|
# == Version
|
17
17
|
#
|
18
|
-
#
|
19
|
-
#
|
20
|
-
#
|
18
|
+
# We are at version 0.4.0 and approaching a solid, stable, and full release.
|
19
|
+
# It's not often I complete a project to a public release, often relegating the
|
20
|
+
# the half completed code to my code vault and it never seeing the light of day
|
21
|
+
# again. So this is definately a yey!
|
22
|
+
#
|
23
|
+
# Version 0.4.0 refines the Route class and interactions by utilizing a Struct.
|
24
|
+
# Maveric no longer depends on any lib outside of core or stdlibs allowing you
|
25
|
+
# to only install Maveric for functionality. We are now using -w and -d switches
|
26
|
+
# during development and striving for concise *and* correct code.
|
21
27
|
#
|
22
|
-
#
|
23
|
-
#
|
24
|
-
#
|
25
|
-
# subclass of Exception can be raised. One of the larger differences is greater
|
26
|
-
# customization of Maveric and Controller instances through extending methods.
|
27
|
-
# These are touched upon in their respective docs. Have fun!
|
28
|
+
# Maveric addons have been revised, giving the current functionality of Maveric
|
29
|
+
# to FastCGI and Mongrel. A plugin to WEBrick has been provided, but is untested
|
30
|
+
# and not supported.
|
28
31
|
#
|
29
|
-
#
|
30
|
-
# better execution path. Instead I got all of that as well as altering basic
|
31
|
-
# implementation details and adding pre and post processors for Controller and
|
32
|
-
# refinment of Maveric's. With the degree of changes we get 0.3.0! Nothing
|
33
|
-
# but good stuff yo.
|
32
|
+
# Check other documentation for a full list of changes.
|
34
33
|
#
|
35
34
|
# == Authors
|
36
35
|
#
|
@@ -84,26 +83,17 @@
|
|
84
83
|
# dependancy on Mongrel sooner than later. Maveric should now be very maveric.
|
85
84
|
#
|
86
85
|
# Because Maveric 0.1.0 worked, but still didn't do things the way I wanted
|
87
|
-
# I revised and refactored codes and algorithms into
|
88
|
-
# be all that I want. Everything beyond 1.0.0 will be improvements and
|
86
|
+
# I revised and refactored codes and algorithms into future versions which will
|
87
|
+
# hopefully be all that I want. Everything beyond 1.0.0 will be improvements and
|
89
88
|
# extensions.
|
90
89
|
#
|
91
|
-
# = Features
|
92
|
-
# * Optional sessions
|
93
|
-
# * Flexible and strong route setup
|
94
|
-
# * Sharing of a Controller between Maveric instances is facilitated
|
95
|
-
# * Inheritance is highly respected
|
96
|
-
#
|
97
90
|
# == Not so Maveric
|
98
91
|
#
|
99
|
-
# Maveric has additional
|
92
|
+
# Maveric has additional libs to allow you to run Maveric behind different
|
100
93
|
# http server implementations. CGI, FastCGI, and Mongrel are supported. If
|
101
94
|
# another service exists that you'd like to have supported please submit a
|
102
95
|
# patch of a good reason why.
|
103
96
|
#
|
104
|
-
# NOTE: Beyond Maveric 0.1.0 extensions have not been touched, future releases
|
105
|
-
# should include updates. AFAIK they should work.
|
106
|
-
#
|
107
97
|
# For these examples we will utilize the simplest functional Maveric possible
|
108
98
|
# require 'maveric'
|
109
99
|
# class Maveric::Index < Maveric::Controller
|
@@ -112,34 +102,29 @@
|
|
112
102
|
# end
|
113
103
|
# end
|
114
104
|
#
|
115
|
-
# To use CGI:
|
116
|
-
# Maveric.new.dispatch.to_s
|
105
|
+
# To use CGI: (not cgi.rb)
|
106
|
+
# puts Maveric.new.dispatch.to_s
|
117
107
|
#
|
118
108
|
# To use FastCGI:
|
119
109
|
# require 'maveric/fastcgi'
|
120
|
-
# ::Maveric::FCGI.new
|
110
|
+
# ::Maveric::FCGI(MyMaveric.new)
|
121
111
|
#
|
122
112
|
# To use Mongrel:
|
123
113
|
# require 'maveric/mongrel'
|
124
114
|
# h = ::Mongrel::HttpHandler ip, port
|
125
|
-
# h.register mountpoint,
|
115
|
+
# h.register mountpoint, MyMaveric.new
|
126
116
|
# h.join.run
|
127
117
|
#
|
128
118
|
# However, if your script is mounted at a point other than /, use the
|
129
119
|
# :path_prefix option to adjust your routes. This affects all routes generated
|
130
120
|
# from MyMaveric.
|
131
|
-
#
|
132
|
-
#
|
133
|
-
#
|
121
|
+
# mav = MyMaveric.new :path_prefix => mountpoint
|
122
|
+
# ::Maveric::FCGI(mav)
|
123
|
+
# h.register mountpoint, mav
|
134
124
|
#
|
135
125
|
# --------------------------------
|
136
|
-
|
137
|
-
|
138
|
-
# pointless time checks in place. Anything greater than the WARN level on the
|
139
|
-
# logging output tends to output a good deal of information. DEBUG is not for
|
140
|
-
# the meek.
|
141
|
-
require 'log4r'
|
142
|
-
require 'stringio'
|
126
|
+
require 'uri'
|
127
|
+
require 'set'
|
143
128
|
require 'maveric/extensions'
|
144
129
|
|
145
130
|
##
|
@@ -150,45 +135,17 @@ require 'maveric/extensions'
|
|
150
135
|
# trickery.
|
151
136
|
class Maveric
|
152
137
|
## Implementation details.
|
153
|
-
VERSION='0.
|
154
|
-
|
138
|
+
VERSION='0.4.0'
|
155
139
|
## Standard end of line for HTTP
|
156
140
|
EOL="\r\n"
|
157
141
|
## Group 1 wil contain a boundary for multipart/form-data bodies.
|
158
142
|
MP_BOUND_REGEX = /\Amultipart\/form-data.*boundary=\"?([^\";, ]+)\"?/n
|
159
143
|
## Contains a list of environment preprocessors.
|
160
|
-
@
|
144
|
+
@prepare_env = []
|
161
145
|
|
162
146
|
# I hate putting utility methods here, rather than in some module, but
|
163
147
|
# I don't see how to do it without getting messy.
|
164
148
|
class << self
|
165
|
-
|
166
|
-
## Builds a logging object if there's not one yet, then returns it.
|
167
|
-
def log
|
168
|
-
unless defined? @@maveric_logger
|
169
|
-
@@maveric_logger = Log4r::Logger.new 'mvc'
|
170
|
-
@@maveric_logger.outputters = Log4r::Outputter['stderr']
|
171
|
-
@@maveric_logger.level = Log4r::INFO
|
172
|
-
@@maveric_logger.info "#{self} #{::Maveric::VERSION}"+
|
173
|
-
" integrated at #{Time.now}"
|
174
|
-
end
|
175
|
-
@@maveric_logger
|
176
|
-
end
|
177
|
-
|
178
|
-
## Performs URI escaping.
|
179
|
-
def escape(s)
|
180
|
-
s.to_s.gsub(/([^ a-zA-Z0-9_.-]+)/n) {
|
181
|
-
'%'+$1.unpack('H2'*$1.size).join('%').upcase
|
182
|
-
}.tr(' ', '+')
|
183
|
-
end
|
184
|
-
|
185
|
-
## Unescapes a URI escaped string.
|
186
|
-
def unescape(s)
|
187
|
-
s.tr('+', ' ').gsub(/((?:%[0-9a-fA-F]{2})+)/n){
|
188
|
-
[$1.delete('%')].pack('H*')
|
189
|
-
}
|
190
|
-
end
|
191
|
-
|
192
149
|
##
|
193
150
|
# Parses a query string by breaking it up around the
|
194
151
|
# delimiting characters. You can also use this to parse
|
@@ -198,8 +155,8 @@ class Maveric
|
|
198
155
|
# This will return a hash of parameters, values being contained in an
|
199
156
|
# array. As a warning, the default value is an empty array, not nil.
|
200
157
|
def query_parse(qs, delim = '&;')
|
201
|
-
(qs||'').split(/[#{delim}] */n).inject(
|
202
|
-
k, v =
|
158
|
+
(qs||'').split(/[#{delim}] */n).inject({}) { |h,p|
|
159
|
+
k, v = URI.decode(p).split('=',2)
|
203
160
|
(h[k]||=[]) << v
|
204
161
|
h
|
205
162
|
}
|
@@ -211,12 +168,11 @@ class Maveric
|
|
211
168
|
#
|
212
169
|
# Might need to be rehauled to match query_parse's behaviour.
|
213
170
|
def parse_multipart boundary, body
|
214
|
-
::Maveric.type_check :body, body, IO, StringIO
|
215
171
|
values = {}
|
216
172
|
bound = /(?:\r?\n|\A)#{Regexp::quote('--'+boundary)}(?:--)?\r$/
|
217
173
|
until body.eof?
|
218
174
|
fv = {}
|
219
|
-
until body.eof? or /^#{EOL}
|
175
|
+
until body.eof? or /^#{EOL}$/ =~ l
|
220
176
|
case l = body.readline
|
221
177
|
when /^Content-Type: (.+?)(\r$|\Z)/m
|
222
178
|
fv[:type] = $1
|
@@ -225,59 +181,42 @@ class Maveric
|
|
225
181
|
end
|
226
182
|
end
|
227
183
|
|
228
|
-
o =
|
229
|
-
|
230
|
-
body.inject do |buf,line|
|
184
|
+
o = fv[:filename] ? '' : fv[:tempfile] = Tempfile.new('MVC').binmode
|
185
|
+
body.inject do |buf, line|
|
231
186
|
o << buf.chomp and break if bound =~ line
|
232
187
|
o << buf
|
233
188
|
line
|
234
|
-
end
|
189
|
+
end # Merb and Camping do it faster.
|
235
190
|
|
236
191
|
fv[:tempfile].rewind if fv.key? :tempfile
|
237
192
|
values[fv[:name]] = fv.key?(:filename) ? fv : o
|
238
193
|
end
|
239
|
-
body.rewind
|
194
|
+
body.rewind rescue nil # FCGI::Stream fun.
|
240
195
|
values
|
241
196
|
end
|
242
197
|
|
243
|
-
##
|
244
|
-
# I have a penchant for static typing, so as a general assertion
|
245
|
-
# and insurance that the correct types of data are being passed I created
|
246
|
-
# this little checking method. It will test value to be an instance of
|
247
|
-
# any of the trailing class, or if the block provided evalutates as true.
|
248
|
-
def type_check name, value, *klasses, &test
|
249
|
-
return unless $DEBUG
|
250
|
-
# order of speed: #include?, #index, #any?
|
251
|
-
if i = klasses.any?{|k|value.is_a? k} then return i
|
252
|
-
elsif test and r = test[value] then return r
|
253
|
-
else
|
254
|
-
raise TypeError, "Expected #{klasses*' or '} for #{name},"+
|
255
|
-
" got #{value.class}:#{value.inspect}."
|
256
|
-
end
|
257
|
-
end
|
258
|
-
|
259
198
|
##################### Non-utility methods
|
260
199
|
|
261
200
|
##
|
262
201
|
# A recursive method to hunt out subclasses of Controller nested
|
263
202
|
# within a Maveric subclass. Used in the setting up of autoroutes.
|
203
|
+
# Disregards everything within a nested Maveric subclass.
|
264
204
|
def nested_controllers realm=self, stk=[]
|
265
205
|
stk << realm #We don't need to visit the same thing twice.
|
266
206
|
realm.constants.map do |c|
|
267
207
|
next if stk.include?(c = realm.const_get(c)) or not c.is_a? Module
|
268
208
|
a = []
|
269
209
|
a << c if c < ::Maveric::Controller
|
270
|
-
a += nested_controllers c, stk
|
210
|
+
a += nested_controllers c, stk unless c < ::Maveric
|
211
|
+
a
|
271
212
|
end.compact.flatten
|
272
213
|
end
|
273
214
|
|
274
215
|
##
|
275
216
|
# Sets up a new Maveric with everything it needs for normal
|
276
217
|
# operation. Many things are either copied or included from it's
|
277
|
-
# parent
|
278
|
-
# their respective modules.
|
218
|
+
# parent. Models and Views are included by their respective modules.
|
279
219
|
def inherited klass
|
280
|
-
::Maveric.log.info "#{klass} inherits from #{self}."
|
281
220
|
super
|
282
221
|
parent = self
|
283
222
|
klass.class_eval do
|
@@ -285,117 +224,134 @@ class Maveric
|
|
285
224
|
module_eval { include parent::Models }
|
286
225
|
const_set(:Views, Module.new).
|
287
226
|
module_eval { include parent::Views }
|
288
|
-
@
|
227
|
+
@prepare_env = parent.prepare_env.dup
|
289
228
|
end
|
290
229
|
end
|
291
230
|
|
292
231
|
##
|
293
|
-
#
|
294
|
-
#
|
295
|
-
#
|
296
|
-
#
|
297
|
-
# If a block is given then, in prepare_env, that block will be called
|
298
|
-
# with the environment hash as the single argument.
|
232
|
+
# Actions are kept in an array to maintain running order.
|
233
|
+
#
|
234
|
+
# This is where you'd add environmental preprocessors to a Maveric. Please
|
235
|
+
# note that actions are copied to subclasses at definition time.
|
299
236
|
#
|
300
|
-
# If
|
301
|
-
#
|
302
|
-
#
|
237
|
+
# If +action+ is a Symbol and no block is provided, in #prepare_environment,
|
238
|
+
# the corresponding method is called with the environment hash as an
|
239
|
+
# argument.
|
240
|
+
# If a block is given then that block will be called with the environment
|
241
|
+
# hash as the single argument.
|
242
|
+
#
|
243
|
+
# Additional arguments honored include :test, which should be a Proc that
|
303
244
|
# accepts a single argument. The argument will be the environment
|
304
245
|
# hash and the boolean result of the block will determine if the
|
305
|
-
#
|
306
|
-
def
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
act[:do] = block
|
311
|
-
@adj_env << act
|
246
|
+
# action will be run upon the environment.
|
247
|
+
def prepare_env action=nil, opts={}, &block
|
248
|
+
if action.is_a? Symbol
|
249
|
+
@prepare_env << opts.update(:name => action, :run => block)
|
250
|
+
else @prepare_env end
|
312
251
|
end
|
313
252
|
end
|
314
253
|
|
254
|
+
## Alias to Maveric.prepare_env
|
255
|
+
def prepare_env(*a, &b)
|
256
|
+
self.class.prepare_env(*a, &b)
|
257
|
+
end
|
258
|
+
|
315
259
|
##
|
316
|
-
#
|
317
|
-
#
|
260
|
+
# Sets @options from +opts+ and initializes @routes.
|
261
|
+
# Adds routes from nested Controllers.
|
318
262
|
def initialize opts={}
|
319
|
-
|
320
|
-
|
321
|
-
@
|
322
|
-
|
323
|
-
|
263
|
+
@options = {:path_prefix => self.class.to_path(true)}
|
264
|
+
@options.update opts
|
265
|
+
@routes = Set.new
|
266
|
+
self.class.nested_controllers.each do |cont|
|
267
|
+
routes = cont.routes
|
268
|
+
routes ||= [[cont.to_path(0).sub(/^#{self.class.to_path(0)}\/?/,'/'),{}]]
|
269
|
+
routes.each {|route,ropts| add_route cont, route, ropts }
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
## Passes arguments to ::Maveric::Route.new and adds the result to @routes.
|
274
|
+
def add_route controller, path, opts={}
|
275
|
+
@routes << ::Maveric::Route.new(controller, path, opts)
|
276
|
+
end
|
277
|
+
|
278
|
+
##
|
279
|
+
# Returns the first non nil result of Route#match across @routes. Will remove
|
280
|
+
# the option setting of :path_prefix form the beginning of the path.
|
281
|
+
def match path
|
282
|
+
path = path.sub(/^#{@options[:path_prefix]}\/?/, '/') if \
|
283
|
+
@options.key? :path_prefix
|
284
|
+
@routes.eject {|route| route.match path }
|
324
285
|
end
|
325
286
|
|
326
|
-
|
287
|
+
##
|
288
|
+
# Returns the first non nil result of Route#path_to across the subset of
|
289
|
+
# @routes that aim at +controller+.
|
290
|
+
def path_to controller, args={}
|
291
|
+
@routes.eject {|route| route.controller == controller and route.path_to args }
|
292
|
+
end
|
293
|
+
|
294
|
+
attr_reader :options, :routes
|
327
295
|
|
328
296
|
##
|
329
|
-
# Maveric's cue to start doing the heavy lifting.
|
297
|
+
# Maveric's cue to start doing the heavy lifting. +env+ should be
|
330
298
|
# a hash with the typical assignments from an HTTP environment or crying
|
331
|
-
# and whining will ensue.
|
332
|
-
def
|
333
|
-
::Maveric.log.info "#{self.class}#process\n "+
|
334
|
-
"[#{Time.now}] #{env['REMOTE_ADDR']} => #{env['REQUEST_URI']}"
|
335
|
-
::Maveric.type_check :req_body, req_body, StringIO
|
336
|
-
::Maveric.type_check :env, env, Hash
|
299
|
+
# and whining will ensue. +req_body+ should be an IO compatible instance.
|
300
|
+
def dispatch req_body=$stdin, env=ENV, opts={}
|
337
301
|
begin
|
338
302
|
prepare_environment env unless env.key? :maveric
|
339
303
|
raise RuntimeError, [404, "Page not found."] unless env[:route]
|
340
304
|
|
341
305
|
# If this is a POST request we need to load data from the body
|
342
306
|
if env['REQUEST_METHOD'] =~ /^post$/i
|
343
|
-
|
307
|
+
params = env.key?(:multipart_boundary) ?
|
344
308
|
parse_multipart(env[:multipart_boundary], req_body) :
|
345
309
|
::Maveric.query_parse(req_body.read)
|
310
|
+
env[:params].update params
|
346
311
|
end
|
347
312
|
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
end
|
352
|
-
|
353
|
-
env[:route][:controller].new req_body, env, opts
|
354
|
-
rescue # we catch exception.is_a? StandardError
|
355
|
-
::Maveric.log.error "#{Time.now}:\n#{$!.inspect}\n#{$!.backtrace*"\n"}"
|
356
|
-
begin
|
357
|
-
raise $! # this makes sense in a certain context...
|
358
|
-
# Here is where we should have transformational stuffs for accessorizing
|
359
|
-
# or customizing error pages. As long as someone doesn't get stupid
|
360
|
-
# about it.
|
361
|
-
rescue
|
362
|
-
return $!
|
363
|
-
end
|
313
|
+
env[:route].controller.new(req_body, env, opts)
|
314
|
+
rescue StandardError => e
|
315
|
+
error(e)
|
364
316
|
end
|
365
317
|
end
|
366
318
|
|
319
|
+
## Override for custom error handling and/or styling.
|
320
|
+
def error err=$!
|
321
|
+
err
|
322
|
+
end
|
323
|
+
|
367
324
|
##
|
368
|
-
#
|
325
|
+
# +env+ should be a normal environment hash derived from HTTP.
|
326
|
+
# Run through actions specified by Maveric.prepare_env in the order stated,
|
327
|
+
# testing env against act[:test] if present to determine if it will be used,
|
328
|
+
# and runs the action on env.
|
369
329
|
def prepare_environment env
|
370
|
-
|
330
|
+
# DEV-NOTE: This method is seperated from prepare_env, in contrast to
|
331
|
+
# #before and #after in Controller, due to the fact one Maveric instance
|
332
|
+
# exists for each service, rather than for each occurance of env per
|
333
|
+
# request.
|
371
334
|
env.update :maveric => self
|
372
|
-
|
373
|
-
act[:test].nil? or act[:test][env]
|
374
|
-
|
375
|
-
::Maveric.log.debug "#{self.class} prep_env #{act[:name]}"
|
376
|
-
if act[:do]
|
377
|
-
act[:do].call env
|
378
|
-
else
|
379
|
-
__send__ act[:name], env
|
380
|
-
end
|
335
|
+
prepare_env.each do |act|
|
336
|
+
next unless act[:test].nil? or act[:test][env]
|
337
|
+
(act[:run] || method(act[:name]))[env]
|
381
338
|
end
|
382
339
|
end
|
383
340
|
|
384
341
|
### Does this count as magic? No, just defaults.
|
385
|
-
|
386
|
-
|
387
|
-
env[:route] = env[:maveric].router[url]
|
342
|
+
prepare_env :route do |env|
|
343
|
+
env[:route] = env[:maveric].match env['REQUEST_URI']
|
388
344
|
end
|
389
345
|
|
390
|
-
|
391
|
-
env[:cookies] = ::Maveric.query_parse env['HTTP_COOKIE'], ';,'
|
346
|
+
prepare_env :cookies do |env|
|
347
|
+
env[:cookies] = ::Maveric.query_parse env['HTTP_COOKIE'], ';,'
|
392
348
|
end
|
393
349
|
|
394
|
-
|
350
|
+
prepare_env :params do |env|
|
395
351
|
env[:params] = ::Maveric.query_parse env['QUERY_STRING']
|
396
352
|
end
|
397
353
|
|
398
|
-
|
354
|
+
prepare_env :multipart_detect do |env|
|
399
355
|
if not env.key? :multipart_boundary \
|
400
356
|
and env['REQUEST_METHOD'] =~ /^post$/i \
|
401
357
|
and env['CONTENT_TYPE'] =~ MP_BOUND_REGEX
|
@@ -403,17 +359,29 @@ class Maveric
|
|
403
359
|
end
|
404
360
|
end
|
405
361
|
|
406
|
-
##
|
407
|
-
# Holds views related methods and helpers
|
362
|
+
## Holds views related methods and helpers.
|
408
363
|
module Views
|
409
|
-
def
|
410
|
-
|
411
|
-
|
364
|
+
def _ content; content; end
|
365
|
+
|
366
|
+
def raw_file filename
|
367
|
+
File.read(filename)
|
368
|
+
end
|
369
|
+
|
370
|
+
def sprintf_file filename, *params
|
371
|
+
raw_file(filename) % [*params]
|
372
|
+
end
|
373
|
+
|
374
|
+
def erubis_file filename, binding
|
375
|
+
require 'erubis'
|
376
|
+
::Erubis::Eruby.new(raw_file(filename)).result(binding)
|
377
|
+
end
|
378
|
+
|
379
|
+
def path_to controller, args={}
|
380
|
+
@env[:maveric].path_to controller, args
|
412
381
|
end
|
413
382
|
end
|
414
383
|
|
415
|
-
##
|
416
|
-
# Repository for models. Still not really utilized.
|
384
|
+
## Repository for models. Still not really utilized.
|
417
385
|
module Models
|
418
386
|
end
|
419
387
|
end
|
@@ -442,13 +410,20 @@ end
|
|
442
410
|
# a String.
|
443
411
|
#
|
444
412
|
# The instance variable @action contains a Symbol corresponding to the
|
445
|
-
# Controller method
|
413
|
+
# Controller method being called. The result of this call is assigned to
|
446
414
|
# @body.
|
447
415
|
#
|
448
416
|
# Those are the only instance variables you should be warned against playing
|
449
417
|
# with frivously. All instance variables are initially assigned before the
|
450
418
|
# before actions have been run, with the exception of @body which is set
|
451
419
|
# after @action is called.
|
420
|
+
#
|
421
|
+
# It's recommended all work be done within methods of Controller, rather than
|
422
|
+
# overriding the inherited methods of Controller, but really it's up to you.
|
423
|
+
#
|
424
|
+
# NOTE: Due to the extension of the Controller instance by the View and Models
|
425
|
+
# modules of the operating Maveric, one must take care not to overlap method
|
426
|
+
# names. I hope to remove this 'feature' in a future version.
|
452
427
|
class Maveric::Controller
|
453
428
|
REQUEST_METHODS = [:post, :get, :put, :delete, :head] # CRUDY
|
454
429
|
@before = []
|
@@ -459,380 +434,265 @@ class Maveric::Controller
|
|
459
434
|
def inherited klass
|
460
435
|
parent = self
|
461
436
|
klass.class_eval do
|
462
|
-
@routes = []
|
463
437
|
@before = parent.before.dup
|
464
438
|
@after = parent.after.dup
|
439
|
+
@routes = []
|
465
440
|
end
|
466
441
|
end
|
467
|
-
|
442
|
+
|
468
443
|
## Removes currently set routes and adds the stated ones.
|
469
444
|
def set_routes r, o={}
|
470
|
-
::Maveric.type_check :r, r, String, Array
|
471
445
|
clear_routes
|
472
|
-
|
473
|
-
r.flatten.map{|e| add_route e }
|
446
|
+
[r].flatten.map{|e| add_route e, o }
|
474
447
|
end
|
475
|
-
|
448
|
+
|
476
449
|
## Add a route to the Controller.
|
477
450
|
def add_route r, o={}
|
478
|
-
|
479
|
-
|
480
|
-
@routes << r
|
451
|
+
# As each route may have its own specific options, we need to hold them
|
452
|
+
# seperately so we don't clobber.
|
453
|
+
@routes << [r, o]
|
481
454
|
end
|
482
455
|
|
483
456
|
## Removes all currently set routes.
|
484
457
|
def clear_routes
|
485
458
|
@routes.clear
|
486
459
|
end
|
487
|
-
|
488
|
-
##
|
460
|
+
|
461
|
+
## Unless routes have been set for this Controller, #routes returns nil.
|
489
462
|
def routes
|
490
|
-
@routes.empty?
|
491
|
-
|
492
|
-
@routes
|
463
|
+
return nil if @routes.empty?
|
464
|
+
@routes
|
493
465
|
end
|
494
466
|
|
495
467
|
##
|
496
|
-
# If no
|
497
|
-
#
|
498
|
-
# If a Symbol or String is given with no block, during Controller
|
499
|
-
# initialization before the action is called, the corresponding
|
500
|
-
# method is called with the Controller instance as an argument.
|
501
|
-
# If a block is given, the block is called with the controller
|
502
|
-
# instance as an argument instead.
|
468
|
+
# If no arguments are given, the array of actions is returned. The first
|
469
|
+
# argument should be a Symbol and should be the name of the action.
|
503
470
|
#
|
504
|
-
# If
|
505
|
-
#
|
506
|
-
#
|
507
|
-
#
|
508
|
-
#
|
471
|
+
# If the second argument is omitted or a collection of hashed options,
|
472
|
+
# then an action is added to the array of actions with +action+ as its
|
473
|
+
# name. If a block is provided it will be run, else the controller instance
|
474
|
+
# method of the same name of the action will be run.
|
475
|
+
# For conditional execution of the action you may provide a list of what
|
476
|
+
# actions it should exclusively run on or not run on, via :only and
|
477
|
+
# :exclude respectively.
|
478
|
+
#
|
479
|
+
# If the second argument is an instance of Controller then the actions are
|
480
|
+
# conditionally, on the basis of :only and :exclude lists, run on the
|
481
|
+
# controller.
|
509
482
|
#
|
510
483
|
# NOTE: If you are referencing instance variables within the action,
|
511
|
-
# it is recommended that you create a method rather than a block.
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
484
|
+
# it is recommended that you create a method rather than a block. Unless
|
485
|
+
# you want to do instance_eval fun, but anyways.
|
486
|
+
def before action=nil, opts={}, &block
|
487
|
+
if action.is_a? Symbol
|
488
|
+
if opts.is_a? ::Maveric::Controller
|
489
|
+
@before.each do |act|
|
490
|
+
next unless (act[:only].nil? or act[:only].include? action) \
|
491
|
+
and (act[:exclude].nil? or not act[:exclude].include? action)
|
492
|
+
(act[:run] || controller.method(act[:name]))[opts]
|
493
|
+
end
|
494
|
+
else # opts should be a Hash
|
495
|
+
opts[:only] = [*opts[:only]] if opts.key? :only
|
496
|
+
opts[:exclude] = [*opts[:exclude]] if opts.key? :exclude
|
497
|
+
@before << opts.update(:name => action, :run => block)
|
498
|
+
end
|
499
|
+
else @before end
|
520
500
|
end
|
521
501
|
|
522
502
|
##
|
523
503
|
# If no argument is given, the array of actions is returned.
|
524
|
-
#
|
525
|
-
# If a Symbol or String is given with no block, during Controller
|
526
|
-
# initialization after the action is called, the corresponding
|
504
|
+
#
|
505
|
+
# If a Symbol or String is given with no block, during Controller
|
506
|
+
# initialization after the action is called, the corresponding
|
527
507
|
# method is called with the Controller instance as an argument.
|
528
508
|
# If a block is given, the block is called with the controller
|
529
509
|
# instance as an argument instead.
|
530
510
|
#
|
531
|
-
# If you are specifying other options, you must explicitly state
|
532
|
-
# :name => <chosen label> as an argument.
|
533
511
|
# Additional arguments include :only and :exclude, whose values should
|
534
512
|
# be a Symbol or an Array of such that correspond with actions that
|
535
513
|
# they should only run on or not run on.
|
536
514
|
#
|
537
515
|
# NOTE: If you are referencing instance variables within the action,
|
538
516
|
# it is recommended that you create a method rather than a block.
|
539
|
-
def after
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
517
|
+
def after action=nil, opts={}, &block
|
518
|
+
if action.is_a? Symbol
|
519
|
+
if opts.is_a? ::Maveric::Controller
|
520
|
+
@after.each do |act|
|
521
|
+
next unless (act[:only].nil? or act[:only].include? action) \
|
522
|
+
and (act[:exclude].nil? or not act[:exclude].include? action)
|
523
|
+
(act[:run] || controller.method(act[:name]))[opts]
|
524
|
+
end
|
525
|
+
else # opts should be a Hash
|
526
|
+
opts[:only] = [*opts[:only]] if opts.key? :only
|
527
|
+
opts[:exclude] = [*opts[:exclude]] if opts.key? :exclude
|
528
|
+
@after << opts.update(:name => action, :run => block)
|
529
|
+
end
|
530
|
+
else @after end
|
547
531
|
end
|
548
532
|
end
|
549
533
|
|
534
|
+
## Alias to Controller.before
|
535
|
+
def before(*a, &b)
|
536
|
+
self.class.before(*a, &b)
|
537
|
+
end
|
538
|
+
|
539
|
+
## Alias to Controller.after
|
540
|
+
def after(*a, &b)
|
541
|
+
self.class.before(*a, &b)
|
542
|
+
end
|
543
|
+
|
550
544
|
##
|
551
|
-
#
|
545
|
+
# Within the initialization of controller, resulting in the object returned
|
546
|
+
# by the call to Maveric#dispatch, you are the busy bee. This hive has several
|
547
|
+
# instance variables which are set down for you to derive all the information
|
548
|
+
# you might need.
|
552
549
|
#
|
553
|
-
#
|
554
|
-
#
|
555
|
-
#
|
556
|
-
#
|
550
|
+
# * @env contains the HTTP environment hash with the special keys of:
|
551
|
+
# * :maveric, who's value is the Maveric instance the request has called upon
|
552
|
+
# * :route, which contains a struct instance which was returned by
|
553
|
+
# Route#match that helped us get here.
|
554
|
+
# * :params, containing a hash of QUERY_STRING data. If REQUEST_METHOD
|
555
|
+
# is 'post' then parameter data from the request bod is overlayed.
|
556
|
+
# * :cookies, which contains hashed data parsed from HTTP_COOKIE.
|
557
|
+
# * plugins or extensions to Maveric may add other special hash pairs
|
558
|
+
# to @env, such as :session, by 'maveric/sessions'.
|
559
|
+
# * @cookies contains a hash
|
560
|
+
# header. If you plan on adding or altering cookie data, do it here.
|
561
|
+
# * @in contains a reference to the input stream of the http request. Note that
|
562
|
+
# if REQUEST_METHOD is 'post' then it has probably already been read.
|
563
|
+
# * @action is the controller instance method to be called. The returned
|
564
|
+
# value of the call should be a string, which will be assigned to @body.
|
565
|
+
# * @status, by default is 200. And @headers, which by default only sets
|
566
|
+
# Content-Type to 'text/html'.
|
557
567
|
#
|
558
|
-
# By the time the Controller.new is done running, it
|
568
|
+
# By the time the Controller.new is done running, it will have @status,
|
559
569
|
# @headers, and @body set. @status should be an Integer matching the
|
560
570
|
# http status code, @headers a string keyed hash of http headers, and @body
|
561
571
|
# to a string of the http response body.
|
562
572
|
def initialize req_body=$stdin, env=ENV, opts={}
|
563
|
-
::Maveric.log.warn "Provided env has not been properly processed, results "+
|
564
|
-
"may vary!" unless ([:maveric, :route, :params, :cookies]-env.keys).empty?
|
565
|
-
::Maveric.type_check :req_body, req_body, StringIO, IO
|
566
|
-
::Maveric.type_check :env, env, Hash
|
567
|
-
::Maveric.type_check :opts, opts, Hash
|
568
|
-
|
569
573
|
@status, @headers = 200, {'Content-Type'=>'text/html'}
|
570
574
|
@env, @in = env, req_body
|
571
575
|
@cookies = @env[:cookies].dup
|
572
576
|
|
573
|
-
|
577
|
+
if @env[:maveric]
|
578
|
+
extend @env[:maveric].class::Models
|
579
|
+
extend @env[:maveric].class::Views
|
580
|
+
end
|
581
|
+
|
582
|
+
action ||= @env[:route].action rescue nil
|
583
|
+
action ||= @env[:route].route.opts[:default_action] rescue nil
|
574
584
|
action ||= @env['REQUEST_METHOD'].downcase rescue nil
|
575
585
|
action ||= 'get'
|
576
586
|
@action = action.to_sym
|
577
587
|
|
578
|
-
|
579
|
-
|
580
|
-
self.class.before.select do |act|
|
581
|
-
(act[:only].nil? or act[:only].include? @action) and \
|
582
|
-
(act[:exclude].nil? or not act[:exclude].include? @action)
|
583
|
-
end.each do |act|
|
584
|
-
::Maveric.log.debug "#{self.class} before #{act[:name]}"
|
585
|
-
if act[:do]
|
586
|
-
act[:do].call self
|
587
|
-
else
|
588
|
-
__send__ act[:name], self
|
589
|
-
end
|
590
|
-
end
|
591
|
-
|
592
|
-
raise NoMethodError, [503, "#{@action} not implemented.", nil, @env] if \
|
593
|
-
REQUEST_METHODS.include? @action and not respond_to? @action
|
588
|
+
before @action, self
|
594
589
|
@body = __send__ @action
|
595
|
-
|
596
|
-
self.class.after.select do |act|
|
597
|
-
(act[:only].nil? or act[:only].include? @action) and \
|
598
|
-
(act[:exclude].nil? or not act[:exclude].include? @action)
|
599
|
-
end.each do |act|
|
600
|
-
::Maveric.log.debug "#{self.class} after #{act[:name]}"
|
601
|
-
if act[:do]
|
602
|
-
act[:do].call self
|
603
|
-
else
|
604
|
-
__send__ act[:name], self
|
605
|
-
end
|
606
|
-
end
|
607
|
-
|
608
|
-
::Maveric.log.debug self # omg, masochistic.
|
590
|
+
after @action, self
|
609
591
|
end
|
610
592
|
|
611
593
|
attr_reader :status, :headers, :body
|
612
594
|
|
595
|
+
## A simple way to retrive pertinant data
|
596
|
+
def to_a # to_splat in 1.9
|
597
|
+
[@status, @headers, @body]
|
598
|
+
end
|
599
|
+
|
613
600
|
## For quick and raw response outputting!
|
614
|
-
def to_http
|
615
|
-
response =
|
616
|
-
|
617
|
-
|
601
|
+
def to_http raw=true
|
602
|
+
response = if not raw then 'Status:'
|
603
|
+
elsif @env and @env.key? 'HTTP_VERSION' then @env['HTTP_VERSION']
|
604
|
+
else 'HTTP/1.1' end
|
605
|
+
response += " #{self.status}" + ::Maveric::EOL # Status message? :/
|
606
|
+
response << self.headers.map{|k,v| "#{k}: #{v}" }*::Maveric::EOL
|
607
|
+
response << ::Maveric::EOL*2 + self.body
|
618
608
|
end
|
619
609
|
|
620
610
|
private
|
621
611
|
|
622
612
|
##
|
623
|
-
# TODO: Doc me. ehird is confused
|
624
|
-
#
|
625
613
|
# This is a simple method, really. The only confusing part is the expressions
|
626
614
|
# just above the return. Controller#render calls itself. That is all.
|
627
615
|
def render view, s=''
|
628
|
-
::Maveric.log.debug "#{self.class}#render"+
|
629
|
-
" #{view.inspect}, #{s[0..100].inspect}"
|
630
|
-
::Maveric.type_check :view, view, Symbol, String
|
631
|
-
::Maveric.type_check :s, s, String
|
632
616
|
s = yield s if block_given?
|
633
|
-
|
617
|
+
raise ArgumentError, "The View #{view.inspect} not defined." unless \
|
618
|
+
respond_to? view
|
619
|
+
s = __send__ view, s
|
634
620
|
s = render :layout, s unless view.to_s[/^(_|layout$)/] \
|
635
|
-
or caller.any?{|l|l=~/`render'/} # i don't like this but it works
|
621
|
+
or caller.any?{|l| l=~/`render'/ } # i don't like this but it works
|
636
622
|
return s
|
637
623
|
end
|
638
624
|
|
639
|
-
##
|
640
|
-
|
641
|
-
|
642
|
-
|
643
|
-
|
644
|
-
def setup controller
|
645
|
-
controller.extend @env[:maveric].class::Models
|
646
|
-
controller.extend @env[:maveric].class::Views
|
625
|
+
## Provides a better error report if the method is a standard http method.
|
626
|
+
def method_missing m, *a, &b
|
627
|
+
raise NoMethodError, [503, "#{m} not implemented.", nil, @env] if \
|
628
|
+
REQUEST_METHODS.include? m
|
629
|
+
super
|
647
630
|
end
|
648
631
|
|
649
632
|
##
|
650
|
-
# Standard Controller cleanup.
|
651
|
-
#
|
652
633
|
# Folds the datasets of @cookie to @headers if they have been altered or
|
653
634
|
# are new.
|
654
|
-
def
|
635
|
+
def cookies_fold controller
|
655
636
|
@cookies.each do |k,v|
|
656
637
|
next unless v != @env[:cookies][k]
|
657
|
-
@headers['Set-Cookie'] << "#{k}=#{
|
638
|
+
@headers['Set-Cookie'] << "#{k}=#{URI.decode(v)}"
|
658
639
|
end
|
659
640
|
end
|
660
641
|
|
661
|
-
|
662
|
-
after :cleanup
|
642
|
+
after :cookies_fold
|
663
643
|
end
|
664
644
|
|
665
645
|
##
|
666
|
-
#
|
667
|
-
#
|
668
|
-
#
|
669
|
-
#
|
670
|
-
#
|
671
|
-
#
|
672
|
-
#
|
673
|
-
#
|
674
|
-
|
675
|
-
# trailing ':' is useful if the parameter occurs within a string of similar
|
676
|
-
# characters.
|
677
|
-
#
|
678
|
-
# Parameters are replaced by default with the regexp of /(#{URI_CHAR}+)/ in
|
679
|
-
# the final Route. If a parameter symbol matches a key in the opts hash the
|
680
|
-
# associated value is placed into the Route instead.
|
681
|
-
#
|
682
|
-
class Maveric::Route < Regexp
|
646
|
+
# Encapsulating route functionality into a single class.
|
647
|
+
#
|
648
|
+
# Route instances are typically stored in a Maveric instance and used
|
649
|
+
# for matching incoming paths to controllers. After a #match is made
|
650
|
+
# the resulting Struct instance has members consisting of the parameters
|
651
|
+
# of the seed path, as well as struct[:controller] pointing to the
|
652
|
+
# destination Controller and struct[:route] pointing to the Route
|
653
|
+
# instance itself.
|
654
|
+
class Maveric::Route
|
683
655
|
## A negative class of URI delimiting and reserved characters.
|
684
656
|
URI_CHAR = '[^/?:,&#]'
|
685
657
|
## Standard pattern for finding parameters in provided paths.
|
686
658
|
PARAM_MATCH= %r~:(#{URI_CHAR}+):?~
|
687
659
|
|
688
|
-
# NOTE: The following examples shows a return value of the equivalent regexp
|
689
|
-
# and not the result of Route#inspect.
|
690
|
-
#
|
691
|
-
# Route.new '/'
|
692
|
-
# => /^\/$/ # with no groups or derived values
|
693
|
-
# Route.new '/:value'
|
694
|
-
# => /^\/([^/?:,&#]+)$/ # with group 1 assigned to :value
|
695
|
-
# Route.new '/:val:ue'
|
696
|
-
# => /^\/([^/?:,&#]+)ue$/ # with group 1 assigned to :val
|
697
|
-
# Route.new '/:value', :value => 'print|find'
|
698
|
-
# => /^\/(print|find)$/
|
699
|
-
# Route.new '/:whtevr', :whtevr => '.*'
|
700
|
-
# => /^\/(.*)$/ # if you want a real catch-all
|
701
|
-
def initialize path, opts={}
|
702
|
-
::Maveric.type_check :path, path, String
|
703
|
-
::Maveric.type_check :opts, opts, Hash
|
704
|
-
|
705
|
-
@path = path.dup.freeze
|
706
|
-
@options = opts.dup.freeze
|
707
|
-
|
708
|
-
regex, @params = __compile path, opts
|
709
|
-
@params.freeze
|
710
|
-
super %r~^#{regex}$~iu
|
711
|
-
freeze
|
712
|
-
|
713
|
-
::Maveric.log.debug self.inspect
|
714
|
-
end
|
715
|
-
|
716
|
-
attr_reader :path, :params, :options
|
717
|
-
|
718
|
-
##
|
719
|
-
# If given a hash that contains all of and only contains keys matching its
|
720
|
-
# parameters then a path will be built by interpolating the hash values for
|
721
|
-
# their corresponding parameters.
|
722
|
-
#
|
723
|
-
# NOTE: Should I or should I not pass the result to #route to ensure
|
724
|
-
# the generation of a compatible path?
|
725
|
-
def build arg
|
726
|
-
::Maveric.log.debug "#{self.class}#build #{arg.inspect} : #{@params}"
|
727
|
-
::Maveric.type_check :arg, arg, Hash
|
728
|
-
if @params.sort == arg.keys.sort
|
729
|
-
@options.
|
730
|
-
merge(arg).
|
731
|
-
inject(@path){|r,(k,v)| r.sub /:#{k}:?/, v }
|
732
|
-
end
|
733
|
-
end
|
734
|
-
|
735
|
-
##
|
736
|
-
# When given a path that matches the Route itself, a hash is returned with
|
737
|
-
# keys being parameters and values being the associated value gathered from
|
738
|
-
# the path.
|
739
|
-
def route path
|
740
|
-
::Maveric.log.debug "#{self.class}#route #{path.inspect} : #{self}"
|
741
|
-
::Maveric.type_check :path, path, String
|
742
|
-
if self =~ path
|
743
|
-
@options.
|
744
|
-
merge( Hash[ *@params.zip($~.captures).flatten ] ).
|
745
|
-
update(nil => self) # should be safe enough.
|
746
|
-
end
|
747
|
-
end
|
748
|
-
|
749
660
|
##
|
750
|
-
#
|
751
|
-
#
|
752
|
-
#
|
661
|
+
# Builds a regex and respective param list from path by parsing out
|
662
|
+
# fragments matching PARAM_MATCH and replacing them with either a
|
663
|
+
# corresponding symbol from the opts hash or the default regex.
|
753
664
|
#
|
754
|
-
#
|
755
|
-
#
|
756
|
-
|
757
|
-
|
758
|
-
|
759
|
-
self.class.new @path.sub(/^#{root}\/?/,'/'), @options.merge(opts)
|
760
|
-
end
|
761
|
-
|
762
|
-
##
|
763
|
-
# As Route is a subclass of a core lib class, the inspect isn't as
|
764
|
-
# informative as we'd like. So we override it.
|
765
|
-
def inspect
|
766
|
-
"#<%s:0x%x %s %s %s>" % [
|
767
|
-
self.class,
|
768
|
-
[object_id<<1].pack('i').unpack('I')[0],
|
769
|
-
super,
|
770
|
-
@params.inspect,
|
771
|
-
@path.inspect
|
772
|
-
] # we don't include @options, I know.
|
773
|
-
end
|
774
|
-
|
775
|
-
private
|
776
|
-
|
777
|
-
##
|
778
|
-
# Builds a regex from path by parsing out fragments matching PARAM_MATCH
|
779
|
-
# and replacing them with either a corresponding symbol from the opts
|
780
|
-
# hash or the default regex.
|
781
|
-
def __compile path, opts
|
782
|
-
::Maveric.type_check :path, path, String
|
783
|
-
::Maveric.type_check :opts, opts, Hash
|
665
|
+
# The final Route instance is utilized to build paths and match
|
666
|
+
# absolute paths for routing.
|
667
|
+
def initialize controller, path, opts={}
|
668
|
+
@controller = controller
|
669
|
+
@path = path.dup.freeze
|
784
670
|
params = []
|
785
|
-
regex = path.gsub PARAM_MATCH do
|
786
|
-
param = $1.to_sym
|
787
|
-
raise ArgumentError, "Duplicated parameter in path."+
|
788
|
-
" <#{param.inspect}>" if params.include? param
|
789
|
-
params << param
|
671
|
+
regex = @path.gsub PARAM_MATCH do
|
672
|
+
params << param = $1.to_sym
|
790
673
|
"(#{opts[param] || URI_CHAR+'+'})"
|
791
674
|
end
|
792
|
-
|
675
|
+
raise ArgumentError, "Duplicated parameters in path."+
|
676
|
+
" <#{params.inspect}>" if params.size != params.uniq.size
|
677
|
+
@struct = Struct.new(:route, :controller, *params)
|
678
|
+
@regex = /\A#{regex}\z/.freeze
|
679
|
+
@opts = opts.reject{|k,v| !params.include? k }.freeze
|
793
680
|
end
|
794
|
-
end
|
795
681
|
|
796
|
-
|
797
|
-
class Maveric::Router
|
798
|
-
def initialize opts={}
|
799
|
-
@routings = Hash.new
|
800
|
-
end
|
801
|
-
|
802
|
-
## Returns a list of the controllers with listed routes.
|
803
|
-
def controllers
|
804
|
-
@routings.values.uniq
|
805
|
-
end
|
682
|
+
attr_reader :path, :regex, :opts, :struct, :controller
|
806
683
|
|
807
684
|
##
|
808
|
-
#
|
809
|
-
#
|
810
|
-
def
|
811
|
-
|
812
|
-
" #{routes.inspect} #{controller.inspect}"
|
813
|
-
::Maveric.type_check :controller, controller, Class
|
814
|
-
::Maveric.type_check :routes, routes, Array, String, ::Maveric::Route
|
815
|
-
routes = [routes] unless routes.is_a? Array
|
816
|
-
routes.flatten.map do |route|
|
817
|
-
::Maveric.type_check :route, route, String, ::Maveric::Route
|
818
|
-
route = ::Maveric::Route.new route, opts if route.instance_of? String
|
819
|
-
@routings[route] = controller
|
820
|
-
end
|
821
|
-
end
|
822
|
-
|
823
|
-
## Return an array of routes mapped to a controller.
|
824
|
-
def to controller
|
825
|
-
@routings.map{|(r,d)| r if d == controller }.compact
|
685
|
+
# Matches the Route's regex against path, returning a Struct instance
|
686
|
+
# of the resulting data, or nil.
|
687
|
+
def match path
|
688
|
+
@regex =~ path and @struct.new(self, @controller, *$~.captures)
|
826
689
|
end
|
827
690
|
|
828
691
|
##
|
829
|
-
#
|
830
|
-
#
|
831
|
-
|
832
|
-
|
833
|
-
|
834
|
-
@routings.eject do |(r,c)|
|
835
|
-
b=r.route(path) and {:controller=>c}.update b
|
836
|
-
end
|
692
|
+
# Returns nil unless all parameters are included in args, preventing the
|
693
|
+
# creation of incomplete paths, otherwise returns a path string.
|
694
|
+
def path_to args
|
695
|
+
return nil unless @opts.keys.sort == args.keys.sort
|
696
|
+
args.inject(@path){|u,(k,v)| u.sub(/#{k.inspect}:?/, v.to_s) }
|
837
697
|
end
|
838
698
|
end
|