maveric 0.3.1 → 0.4.0
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/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
|