roda-cj 0.9.2 → 0.9.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2f2740123aa0c6fafe4c1665f9c83e408cef9105
4
- data.tar.gz: 74ec8c03a23405c2ac18eb23ceea5ccb3f4813c1
3
+ metadata.gz: 5360a34504fec1081e28143f1f2ce25c69747061
4
+ data.tar.gz: a593de99e0868ce9478922e9fc1a775fd28afaf7
5
5
  SHA512:
6
- metadata.gz: 876514335f729d2beeea7b218bc14d7cac1772f5b568a4a985457b01321c16ef33116bb3dd605e218d3512185d34dffaca426b9e803d0e860c56619eb2838359
7
- data.tar.gz: d046733fe6737ccabc612523c7812b1be778cea3675223a1a5bc3c202a91fad2da26e2fac842789542368fcb95e0b8d201cff9235de3410c3e1e5e414314f1f0
6
+ metadata.gz: 7bf78f056f2b0d1e034e7fab2be1a9dfaad92261c72e692da8c9bcdae895fefa10c348cb8a950445c769b7f7e944e110481e8d066d7a517043a175e647e952be
7
+ data.tar.gz: d963a3e7c9d02a9077d7f77678d7292b427b8d55ee21b614b7ebd1a401dccd83e9e4d037dd58d319b0490748cf3c33935206437e54ea908365ed8058d6892081
data/CHANGELOG CHANGED
@@ -1,5 +1,31 @@
1
1
  = HEAD
2
2
 
3
+ * Add backtracking_array plugin, allowing array matchers to backtrack if later matchers do not match (jeremyevans)
4
+
5
+ * Add :all hash matcher, allowing array matchers to include conditions where you want to match multiple conditions (jeremyevans)
6
+
7
+ * Add json plugin, allowing match blocks to return arrays/hashes, returning JSON (jeremyevans)
8
+
9
+ * Add view_subdirs plugin, for setting a subdirectory for views on a per-request basis (jeremyevans)
10
+
11
+ * Allow default halt method to take no arguments, and use the current response (jeremyevans)
12
+
13
+ * Add symbol_views plugin, allowing match blocks to return a template name symbol (jeremyevans)
14
+
15
+ * Add per_thread_caching plugin, for using separate caches per thread instead of shared thread-safe caches (jeremyevans)
16
+
17
+ * Add hash_matcher class method, for easily creating hash match methods (jeremyevans)
18
+
19
+ * Add symbol_matchers plugin, for using symbol-specific matching regexps (jeremyevans)
20
+
21
+ * Add csrf plugin for csrf protection using rack_csrf (jeremyevans)
22
+
23
+ * Optimize r.is, r.get, r.post and similar methods by reducing the number of Array objects created (jeremyevans)
24
+
25
+ * Support RequestClassMethods and ResponseClassMethods in plugins (jeremyevans)
26
+
27
+ * Add Roda::RodaCache for a thread safe cache, currently used for match patterns, templates, and plugins (jeremyevans)
28
+
3
29
  * Optimize matching by caching consume regexp for strings, regexp, symbol, and :extension matchers (jeremyevans)
4
30
 
5
31
  * Add r.root for matching root (path "/"), for easier to read version of r.is "" (jeremyevans)
data/README.rdoc CHANGED
@@ -268,20 +268,17 @@ This makes it easy to handle multiple strings without a Regexp:
268
268
 
269
269
  === Hash
270
270
 
271
- Hashes call a <tt>match_*</tt> method with the given key using the hash value,
272
- and match if that matcher returns true.
273
-
271
+ Hashes allow easily calling specialized match methods on the request.
274
272
  The default registered matchers included with Roda are documented below.
275
- You can add your own hash matchers by adding the appropriate <tt>match_*</tt>
276
- method to the request class using the +request_module+ method:
273
+ You can add your own hash matchers using the +hash_matcher+ class method,
274
+ which creates an appropriate request match method. The +hash_matcher+
275
+ block will be called with the value of the hash.
277
276
 
278
277
  class App < Roda
279
- request_module do
280
- def match_foo(v)
281
- ...
282
- end
278
+ hash_matcher(:foo) do |v|
279
+ ...
283
280
  end
284
-
281
+
285
282
  route do |r|
286
283
  r.on :foo=>'bar' do
287
284
  ...
@@ -289,6 +286,27 @@ method to the request class using the +request_module+ method:
289
286
  end
290
287
  end
291
288
 
289
+ ==== :all
290
+
291
+ The :all matcher matches if all of the entries in the given array matches. So
292
+
293
+ r.on :all=>[:a, :b] do
294
+ ...
295
+ end
296
+
297
+ is the same as:
298
+
299
+ r.on :a, :b do
300
+ ...
301
+ end
302
+
303
+ The reason it also exists as a separate hash matcher is so you can use it inside
304
+ an array matcher. so:
305
+
306
+ r.on ['foo', {:all=>['foos', :id]}] do
307
+ end
308
+
309
+ Would match +/foo+ and +/foos/10+, but not +/foos+.
292
310
 
293
311
  ==== :extension
294
312
 
@@ -605,6 +623,7 @@ or modifying the +render_opts+ hash after loading the plugin:
605
623
  render_opts[:layout] = "admin_layout" # Default layout template
606
624
  render_opts[:layout_opts] = {:engine=>'haml'} # Default layout template options
607
625
  render_opts[:opts] = {:default_encoding=>'UTF-8'} # Default template options
626
+ render_opts[:cache] = false # Disable template caching
608
627
  end
609
628
 
610
629
  == Plugins
@@ -617,6 +636,10 @@ override any Roda method and call +super+ to get the default behavior.
617
636
  These plugins ship with roda:
618
637
 
619
638
  all_verbs :: Adds routing methods to the request for all http verbs.
639
+ backtracking_array :: Allows array matchers to backtrack if later matchers
640
+ do not match.
641
+ csrf :: Adds CSRF protection and helper methods using
642
+ {rack_csrf}[https://github.com/baldowl/rack_csrf].
620
643
  default_headers :: Override the default response headers used.
621
644
  error_handler :: Adds a +error+ block that is called for all responses that
622
645
  raise exceptions.
@@ -628,6 +651,8 @@ header_matchers :: Adds host, header, and accept hash matchers.
628
651
  hooks :: Adds before and after methods to run code before and after requests.
629
652
  indifferent_params :: Adds params method with indifferent access to params,
630
653
  allowing use of symbol keys for accessing params.
654
+ json :: Allows match blocks to return arrays and hashes, using a json
655
+ representation as the response body.
631
656
  middleware :: Allows the Roda app to be used as a rack middleware, calling the
632
657
  next middleware if no route matches.
633
658
  multi_route :: Adds the ability for multiple named route blocks, with the
@@ -636,8 +661,15 @@ not_found :: Adds a +not_found+ block that is called for all 404 responses
636
661
  without bodies.
637
662
  pass :: Adds a pass method allowing you to skip the current +r.on+ block as if
638
663
  it did not match.
664
+ per_thread_caching :: Switches the thread-safe cache from a shared cache to a
665
+ per-thread cache.
639
666
  render :: Adds support for rendering templates via tilt, as described above.
640
667
  streaming :: Adds support for streaming responses.
668
+ symbol_matchers :: Adds support for symbol-specific matching regexps.
669
+ symbol_views :: Allows match blocks to return template name symbols, uses the
670
+ template view as the response body.
671
+ view_subdirs :: Allows for setting a view subdirectory to use on a per-request
672
+ basis.
641
673
 
642
674
  === External Plugins
643
675
 
@@ -649,13 +681,15 @@ autoforme :: Adds support for easily creating a simple administrative front
649
681
 
650
682
  === How to create plugins
651
683
 
652
- Authoring your own plugins is pretty straightforward. Plugins are just modules
653
- that contain one of the following modules:
684
+ Authoring your own plugins is pretty straightforward. Plugins are just modules,
685
+ which may contain any of the following modules:
654
686
 
655
687
  InstanceMethods :: module included in the Roda class
656
688
  ClassMethods :: module that extends the Roda class
657
689
  RequestMethods :: module included in the class of the request
690
+ RequestClassMethods :: module extending the class of the request
658
691
  ResponseMethods :: module included in the class of the response
692
+ ResponseClassMethods :: module extending the class of the response
659
693
 
660
694
  If the plugin responds to +load_dependencies+, it will be called first, and should
661
695
  be used if the plugin depends on another plugin.
data/lib/roda.rb CHANGED
@@ -9,88 +9,46 @@ class Roda
9
9
  # Error class raised by Roda
10
10
  class RodaError < StandardError; end
11
11
 
12
- # Base class used for Roda requests. The instance methods for this
13
- # class are added by Roda::RodaPlugins::Base::RequestMethods, so this
14
- # only contains the class methods.
15
- class RodaRequest < ::Rack::Request;
16
- @roda_class = ::Roda
17
- @match_pattern_cache = {}
18
-
19
- if defined?(RUBY_ENGINE) && RUBY_ENGINE != 'ruby'
20
- # :nocov:
21
- @match_pattern_mutex = Mutex.new
22
-
23
- def self.cached_matcher(obj)
24
- unless pattern = @match_pattern_mutex.synchronize{@match_pattern_cache[obj]}
25
- pattern = consume_pattern(yield)
26
- @match_pattern_mutex.synchronize{@match_pattern_cache[obj] = pattern}
27
- end
28
- pattern
12
+ if defined?(RUBY_ENGINE) && RUBY_ENGINE != 'ruby'
13
+ # A thread safe cache class, offering only #[] and #[]= methods,
14
+ # each protected by a mutex. Used on non-MRI where Hash is not
15
+ # thread safe.
16
+ class RodaCache
17
+ # Create a new thread safe cache.
18
+ def initialize
19
+ @mutex = Mutex.new
20
+ @hash = {}
29
21
  end
30
22
 
31
- def self.inherited(subclass)
32
- super
33
- subclass.instance_variable_set(:@match_pattern_cache, {})
34
- subclass.instance_variable_set(:@match_pattern_mutex, Mutex.new)
35
- end
36
- # :nocov:
37
- else
38
- # Return the cached pattern for the given object. If the object is
39
- # not already cached, yield to get the basic pattern, and convert the
40
- # basic pattern to a pattern that does not partial segments.
41
- def self.cached_matcher(obj)
42
- unless pattern = @match_pattern_cache[obj]
43
- pattern = @match_pattern_cache[obj] = consume_pattern(yield)
44
- end
45
- pattern
23
+ # Make getting value from underlying hash thread safe.
24
+ def [](key)
25
+ @mutex.synchronize{@hash[key]}
46
26
  end
47
27
 
48
- # Initialize the match_pattern cache in the subclass.
49
- def self.inherited(subclass)
50
- super
51
- subclass.instance_variable_set(:@match_pattern_cache, {})
28
+ # Make setting value in underlying hash thread safe.
29
+ def []=(key, value)
30
+ @mutex.synchronize{@hash[key] = value}
52
31
  end
53
32
  end
33
+ else
34
+ # Hashes are already thread-safe in MRI, due to the GVL, so they
35
+ # can safely be used as a cache.
36
+ RodaCache = Hash
37
+ end
54
38
 
55
- class << self
56
- # Reference to the Roda class related to this request class.
57
- attr_accessor :roda_class
58
-
59
- # Since RodaRequest is anonymously subclassed when Roda is subclassed,
60
- # and then assigned to a constant of the Roda subclass, make inspect
61
- # reflect the likely name for the class.
62
- def inspect
63
- "#{roda_class.inspect}::RodaRequest"
64
- end
65
-
66
- private
67
-
68
- # The pattern to use for consuming, based on the given argument. The returned
69
- # pattern requires the path starts with a string and does not match partial
70
- # segments.
71
- def consume_pattern(pattern)
72
- /\A(\/(?:#{pattern}))(\/|\z)/
73
- end
74
- end
39
+ # Base class used for Roda requests. The instance methods for this
40
+ # class are added by Roda::RodaPlugins::Base::RequestMethods, the
41
+ # class methods are added by Roda::RodaPlugins::Base::RequestClassMethods.
42
+ class RodaRequest < ::Rack::Request;
43
+ @roda_class = ::Roda
44
+ @match_pattern_cache = ::Roda::RodaCache.new
75
45
  end
76
46
 
77
47
  # Base class used for Roda responses. The instance methods for this
78
- # class are added by Roda::RodaPlugins::Base::ResponseMethods, so this
79
- # only contains the class methods.
48
+ # class are added by Roda::RodaPlugins::Base::ResponseMethods, the class
49
+ # methods are added by Roda::RodaPlugins::Base::ResponseClassMethods.
80
50
  class RodaResponse < ::Rack::Response;
81
51
  @roda_class = ::Roda
82
-
83
- class << self
84
- # Reference to the Roda class related to this response class.
85
- attr_accessor :roda_class
86
-
87
- # Since RodaResponse is anonymously subclassed when Roda is subclassed,
88
- # and then assigned to a constant of the Roda subclass, make inspect
89
- # reflect the likely name for the class.
90
- def inspect
91
- "#{roda_class.inspect}::RodaResponse"
92
- end
93
- end
94
52
  end
95
53
 
96
54
  @builder = ::Rack::Builder.new
@@ -100,11 +58,8 @@ class Roda
100
58
  # Module in which all Roda plugins should be stored. Also contains logic for
101
59
  # registering and loading plugins.
102
60
  module RodaPlugins
103
- # Mutex protecting the plugins hash
104
- @mutex = ::Mutex.new
105
-
106
61
  # Stores registered plugins
107
- @plugins = {}
62
+ @plugins = RodaCache.new
108
63
 
109
64
  # If the registered plugin already exists, use it. Otherwise,
110
65
  # require it and return it. This raises a LoadError if such a
@@ -112,9 +67,9 @@ class Roda
112
67
  # not register itself correctly.
113
68
  def self.load_plugin(name)
114
69
  h = @plugins
115
- unless plugin = @mutex.synchronize{h[name]}
70
+ unless plugin = h[name]
116
71
  require "roda/plugins/#{name}"
117
- raise RodaError, "Plugin #{name} did not register itself correctly in Roda::RodaPlugins" unless plugin = @mutex.synchronize{h[name]}
72
+ raise RodaError, "Plugin #{name} did not register itself correctly in Roda::RodaPlugins" unless plugin = h[name]
118
73
  end
119
74
  plugin
120
75
  end
@@ -122,7 +77,7 @@ class Roda
122
77
  # Register the given plugin with Roda, so that it can be loaded using #plugin
123
78
  # with a symbol. Should be used by plugin files.
124
79
  def self.register_plugin(name, mod)
125
- @mutex.synchronize{@plugins[name] = mod}
80
+ @plugins[name] = mod
126
81
  end
127
82
 
128
83
  # The base plugin for Roda, implementing all default functionality.
@@ -145,6 +100,14 @@ class Roda
145
100
  app.call(env)
146
101
  end
147
102
 
103
+ # Create a match_#{key} method in the request class using the given
104
+ # block, so that using a hash key in a request match method will
105
+ # call the block. The block should return nil or false to not
106
+ # match, and anything else to match.
107
+ def hash_matcher(key, &block)
108
+ request_module{define_method(:"match_#{key}", &block)}
109
+ end
110
+
148
111
  # When inheriting Roda, setup a new rack app builder, copy the
149
112
  # default middleware and opts into the subclass, and set the
150
113
  # request and response classes in the subclasses to be subclasses
@@ -159,6 +122,7 @@ class Roda
159
122
 
160
123
  request_class = Class.new(self::RodaRequest)
161
124
  request_class.roda_class = subclass
125
+ request_class.match_pattern_cache = thread_safe_cache
162
126
  subclass.const_set(:RodaRequest, request_class)
163
127
 
164
128
  response_class = Class.new(self::RodaResponse)
@@ -187,9 +151,15 @@ class Roda
187
151
  if defined?(mixin::RequestMethods)
188
152
  self::RodaRequest.send(:include, mixin::RequestMethods)
189
153
  end
154
+ if defined?(mixin::RequestClassMethods)
155
+ self::RodaRequest.extend mixin::RequestClassMethods
156
+ end
190
157
  if defined?(mixin::ResponseMethods)
191
158
  self::RodaResponse.send(:include, mixin::ResponseMethods)
192
159
  end
160
+ if defined?(mixin::ResponseClassMethods)
161
+ self::RodaResponse.extend mixin::ResponseClassMethods
162
+ end
193
163
 
194
164
  if mixin.respond_to?(:configure)
195
165
  mixin.configure(self, *args, &block)
@@ -218,6 +188,12 @@ class Roda
218
188
  @app = @builder.to_app
219
189
  end
220
190
 
191
+ # A new thread safe cache instance. This is a method so it can be
192
+ # easily overridden for alternative implementations.
193
+ def thread_safe_cache
194
+ RodaCache.new
195
+ end
196
+
221
197
  # Add a middleware to use for the rack application. Must be
222
198
  # called before calling #route.
223
199
  def use(*args, &block)
@@ -298,12 +274,50 @@ class Roda
298
274
  # behavior after the request and response have been setup.
299
275
  def _route(&block)
300
276
  catch(:halt) do
301
- request.handle_on_result(instance_exec(@_request, &block))
277
+ request.block_result(instance_exec(@_request, &block))
302
278
  response.finish
303
279
  end
304
280
  end
305
281
  end
306
282
 
283
+ # Class methods for RodaRequest
284
+ module RequestClassMethods
285
+ # Reference to the Roda class related to this request class.
286
+ attr_accessor :roda_class
287
+
288
+ # The cache to use for match patterns for this request class.
289
+ attr_accessor :match_pattern_cache
290
+
291
+ # Return the cached pattern for the given object. If the object is
292
+ # not already cached, yield to get the basic pattern, and convert the
293
+ # basic pattern to a pattern that does not partial segments.
294
+ def cached_matcher(obj)
295
+ cache = @match_pattern_cache
296
+
297
+ unless pattern = cache[obj]
298
+ pattern = cache[obj] = consume_pattern(yield)
299
+ end
300
+
301
+ pattern
302
+ end
303
+
304
+ # Since RodaRequest is anonymously subclassed when Roda is subclassed,
305
+ # and then assigned to a constant of the Roda subclass, make inspect
306
+ # reflect the likely name for the class.
307
+ def inspect
308
+ "#{roda_class.inspect}::RodaRequest"
309
+ end
310
+
311
+ private
312
+
313
+ # The pattern to use for consuming, based on the given argument. The returned
314
+ # pattern requires the path starts with a string and does not match partial
315
+ # segments.
316
+ def consume_pattern(pattern)
317
+ /\A(\/(?:#{pattern}))(\/|\z)/
318
+ end
319
+ end
320
+
307
321
  # Instance methods for RodaRequest, mostly related to handling routing
308
322
  # for the request.
309
323
  module RequestMethods
@@ -312,8 +326,15 @@ class Roda
312
326
  REQUEST_METHOD = "REQUEST_METHOD".freeze
313
327
  EMPTY_STRING = "".freeze
314
328
  SLASH = "/".freeze
315
- TERM = Object.new.freeze
316
329
  SEGMENT = "([^\\/]+)".freeze
330
+ EMPTY_ARRAY = [].freeze
331
+ TERM_INSPECT = "TERM".freeze
332
+
333
+ TERM = Object.new
334
+ def TERM.inspect
335
+ TERM_INSPECT
336
+ end
337
+ TERM.freeze
317
338
 
318
339
  # The current captures for the request. This gets modified as routing
319
340
  # occurs.
@@ -336,24 +357,26 @@ class Roda
336
357
  "#{env[SCRIPT_NAME]}#{env[PATH_INFO]}"
337
358
  end
338
359
 
339
- # If this is not a GET method, returns immediately. Otherwise, calls
340
- # #is if there are any arguments, or #on if there are no arguments.
360
+ # If this is not a GET method, returns immediately. Otherwise, if there
361
+ # are arguments, do a terminal match on the arguments, otherwise do a
362
+ # regular match.
341
363
  def get(*args, &block)
342
- is_or_on(*args, &block) if get?
364
+ _verb(args, &block) if get?
343
365
  end
344
366
 
345
367
  # Immediately stop execution of the route block and return the given
346
- # rack response array of status, headers, and body.
347
- def halt(response)
348
- _halt(response)
368
+ # rack response array of status, headers, and body. If no argument
369
+ # is given, uses the current response.
370
+ def halt(res=response.finish)
371
+ throw :halt, res
349
372
  end
350
373
 
351
374
  # Handle #on block return values. By default, if a string is given
352
375
  # and the response is empty, use the string as the response body.
353
- def handle_on_result(result)
376
+ def block_result(result)
354
377
  res = response
355
- if result.is_a?(String) && res.empty?
356
- res.write(result)
378
+ if res.empty? && (body = block_result_body(result))
379
+ res.write(body)
357
380
  end
358
381
  end
359
382
 
@@ -367,7 +390,7 @@ class Roda
367
390
  # there is only a match if #on has fully matched the path.
368
391
  def is(*args, &block)
369
392
  args << TERM
370
- on(*args, &block)
393
+ _on(args, &block)
371
394
  end
372
395
 
373
396
  # Attempts to match on all of the arguments. If all of the
@@ -376,31 +399,14 @@ class Roda
376
399
  # If any of the arguments fails, ensures the request state is
377
400
  # returned to that before matches were attempted.
378
401
  def on(*args, &block)
379
- try do
380
- # We stop evaluation of this entire matcher unless
381
- # each and every `arg` defined for this matcher evaluates
382
- # to a non-false value.
383
- #
384
- # Short circuit examples:
385
- # on true, false do
386
- #
387
- # # PATH_INFO=/user
388
- # on true, "signup"
389
- return unless args.all?{|arg| match(arg)}
390
-
391
- # The captures we yield here were generated and assembled
392
- # by evaluating each of the `arg`s above. Most of these
393
- # are carried out by #consume.
394
- handle_on_result(yield(*captures))
395
-
396
- _halt response.finish
397
- end
402
+ _on(args, &block)
398
403
  end
399
404
 
400
- # If this is not a POST method, returns immediately. Otherwise, calls
401
- # #is if there are any arguments, or #on if there are no arguments.
405
+ # If this is not a GET method, returns immediately. Otherwise, if there
406
+ # are arguments, do a terminal match on the arguments, otherwise do a
407
+ # regular match.
402
408
  def post(*args, &block)
403
- is_or_on(*args, &block) if post?
409
+ _verb(args, &block) if post?
404
410
  end
405
411
 
406
412
  # The response related to the current request.
@@ -411,31 +417,26 @@ class Roda
411
417
  # Immediately redirect to the given path.
412
418
  def redirect(path, status=302)
413
419
  response.redirect(path, status)
414
- _halt response.finish
420
+ throw :halt, response.finish
415
421
  end
416
422
 
417
- #
423
+ # If the current path is the root ("/"), match on the block. If a request
424
+ # method is given, return immediately if the request does not use the given
425
+ # method.
418
426
  def root(request_method=nil, &block)
419
427
  if env[PATH_INFO] == SLASH && (!request_method || send(:"#{request_method}?"))
420
- on(&block)
428
+ _on(EMPTY_ARRAY, &block)
421
429
  end
422
430
  end
423
431
 
424
432
  # Call the given rack app with the environment and immediately return
425
433
  # the response as the response for this request.
426
434
  def run(app)
427
- _halt app.call(env)
435
+ throw :halt, app.call(env)
428
436
  end
429
437
 
430
438
  private
431
439
 
432
- # Internal halt method, used so that halt can be overridden to handle
433
- # non-rack response arrays, but internal code that always generates
434
- # rack response arrays can use this for performance.
435
- def _halt(response)
436
- throw :halt, response
437
- end
438
-
439
440
  # Match any of the elements in the given array. Return at the
440
441
  # first match without evaluating future matches. Returns false
441
442
  # if no elements in the array match.
@@ -465,12 +466,54 @@ class Roda
465
466
  # string so that regexp metacharacters are not matched, and recognizes
466
467
  # colon tokens for placeholders.
467
468
  def _match_string(str)
468
- consume(self.class.cached_matcher(str){Regexp.escape(str).gsub(/:\w+/, SEGMENT)})
469
+ consume(self.class.cached_matcher(str){Regexp.escape(str).gsub(/:(\w+)/){|m| _match_symbol_regexp($1)}})
469
470
  end
470
471
 
471
472
  # Match the given symbol if any segment matches.
472
473
  def _match_symbol(sym)
473
- consume(self.class.cached_matcher(sym){SEGMENT})
474
+ consume(self.class.cached_matcher(sym){_match_symbol_regexp(sym)})
475
+ end
476
+
477
+ # The regular expression to use for matching symbols. By default, any non-empty
478
+ # segment matches.
479
+ def _match_symbol_regexp(s)
480
+ SEGMENT
481
+ end
482
+
483
+ # Internal match method taking array of matchers instead of multiple
484
+ # arguments.
485
+ def _on(args)
486
+ script = env[SCRIPT_NAME]
487
+ path = env[PATH_INFO]
488
+
489
+ # For every block, we make sure to reset captures so that
490
+ # nesting matchers won't mess with each other's captures.
491
+ captures.clear
492
+
493
+ return unless match_all(args)
494
+ block_result(yield(*captures))
495
+ throw :halt, response.finish
496
+ ensure
497
+ env[SCRIPT_NAME] = script
498
+ env[PATH_INFO] = path
499
+ end
500
+
501
+ # Backbone of the verb method support, using a terminal match if
502
+ # args is not empty, or a regular match if it is empty.
503
+ def _verb(args, &block)
504
+ unless args.empty?
505
+ args << TERM
506
+ end
507
+ _on(args, &block)
508
+ end
509
+
510
+ # The body to use for the response if the response does not return
511
+ # a body. By default, a String is returned directly, and nil is
512
+ # returned otherwise.
513
+ def block_result_body(result)
514
+ if result.is_a?(String)
515
+ result
516
+ end
474
517
  end
475
518
 
476
519
  # Attempts to match the pattern to the current path. If there is no
@@ -478,9 +521,7 @@ class Roda
478
521
  # SCRIPT_NAME to include the matched path, removes the matched
479
522
  # path from PATH_INFO, and updates captures with any regex captures.
480
523
  def consume(pattern)
481
- matchdata = env[PATH_INFO].match(pattern)
482
-
483
- return false unless matchdata
524
+ return unless matchdata = env[PATH_INFO].match(pattern)
484
525
 
485
526
  vars = matchdata.captures
486
527
 
@@ -491,16 +532,6 @@ class Roda
491
532
  captures.concat(vars)
492
533
  end
493
534
 
494
- # Backbone of the verb method support, calling #is if there are any
495
- # arguments, or #on if there are none.
496
- def is_or_on(*args, &block)
497
- if args.empty?
498
- on(*args, &block)
499
- else
500
- is(*args, &block)
501
- end
502
- end
503
-
504
535
  # Attempt to match the argument to the given request, handling
505
536
  # common ruby types.
506
537
  def match(matcher)
@@ -524,6 +555,11 @@ class Roda
524
555
  end
525
556
  end
526
557
 
558
+ # Match only if all of the arguments in the given array match.
559
+ def match_all(args)
560
+ args.all?{|arg| match(arg)}
561
+ end
562
+
527
563
  # Match files with the given extension. Requires that the
528
564
  # request path end with the extension.
529
565
  def match_extension(ext)
@@ -555,22 +591,18 @@ class Roda
555
591
  captures << v
556
592
  end
557
593
  end
594
+ end
558
595
 
559
- # Yield to the given block, clearing any captures before
560
- # yielding and restoring the SCRIPT_NAME and PATH_INFO on exit.
561
- def try
562
- script = env[SCRIPT_NAME]
563
- path = env[PATH_INFO]
564
-
565
- # For every block, we make sure to reset captures so that
566
- # nesting matchers won't mess with each other's captures.
567
- captures.clear
568
-
569
- yield
596
+ # Class methods for RodaResponse
597
+ module ResponseClassMethods
598
+ # Reference to the Roda class related to this response class.
599
+ attr_accessor :roda_class
570
600
 
571
- ensure
572
- env[SCRIPT_NAME] = script
573
- env[PATH_INFO] = path
601
+ # Since RodaResponse is anonymously subclassed when Roda is subclassed,
602
+ # and then assigned to a constant of the Roda subclass, make inspect
603
+ # reflect the likely name for the class.
604
+ def inspect
605
+ "#{roda_class.inspect}::RodaResponse"
574
606
  end
575
607
  end
576
608