roda-cj 0.9.3 → 0.9.4
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.
- checksums.yaml +4 -4
- data/CHANGELOG +13 -1
- data/README.rdoc +14 -9
- data/lib/roda/plugins/all_verbs.rb +6 -8
- data/lib/roda/plugins/backtracking_array.rb +1 -0
- data/lib/roda/plugins/content_for.rb +46 -0
- data/lib/roda/plugins/halt.rb +3 -9
- data/lib/roda/plugins/head.rb +56 -0
- data/lib/roda/plugins/header_matchers.rb +2 -2
- data/lib/roda/plugins/json.rb +1 -1
- data/lib/roda/plugins/not_allowed.rb +133 -0
- data/lib/roda/plugins/pass.rb +13 -6
- data/lib/roda/plugins/render_each.rb +61 -0
- data/lib/roda/plugins/symbol_matchers.rb +8 -4
- data/lib/roda/plugins/symbol_views.rb +1 -1
- data/lib/roda/version.rb +1 -1
- data/lib/roda.rb +80 -55
- data/spec/matchers_spec.rb +4 -15
- data/spec/plugin/content_for_spec.rb +34 -0
- data/spec/plugin/head_spec.rb +35 -0
- data/spec/plugin/not_allowed_spec.rb +42 -0
- data/spec/plugin/pass_spec.rb +7 -1
- data/spec/plugin/render_each_spec.rb +30 -0
- data/spec/plugin/symbol_matchers_spec.rb +6 -0
- metadata +10 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0381614904fadaf43af33f351a4edfc17d054c4d
|
4
|
+
data.tar.gz: a1f3866849f764552a2de2f987c636d20cf33099
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d2930467e112ec168fd41c4f9005e21984b028ff45aa8fb58d843e4343bb098f888774c94b989b70ce0aea4f6f69e8eae2b2a62fc5c6626816f78eea9257fd58
|
7
|
+
data.tar.gz: 1f506448329172d622492c22167f39b8690ca812c50f734c3dec306fd537855b0d4767a8507c555471963a4f053e4b65874d277cc9d53e44fabe2471c3e9374e
|
data/CHANGELOG
CHANGED
@@ -1,5 +1,17 @@
|
|
1
1
|
= HEAD
|
2
2
|
|
3
|
+
* Add head plugin, for handling HEAD requests like GET requests with an empty body (jeremyevans)
|
4
|
+
|
5
|
+
* Optimize consuming patterns by using a positive lookahead assertion (jeremyevans)
|
6
|
+
|
7
|
+
* Add not_allowed plugin, for automatically returning 405 Method Not Allowed responses (jeremyevans)
|
8
|
+
|
9
|
+
* Optimize match blocks with no arguments (jeremyevans)
|
10
|
+
|
11
|
+
* Add content_for plugin, for storing content in one template and retrieving it in another (jeremyevans)
|
12
|
+
|
13
|
+
* Add render_each plugin, for rendering a template for each value in an enumerable (jeremyevans)
|
14
|
+
|
3
15
|
* Add backtracking_array plugin, allowing array matchers to backtrack if later matchers do not match (jeremyevans)
|
4
16
|
|
5
17
|
* Add :all hash matcher, allowing array matchers to include conditions where you want to match multiple conditions (jeremyevans)
|
@@ -28,7 +40,7 @@
|
|
28
40
|
|
29
41
|
* Optimize matching by caching consume regexp for strings, regexp, symbol, and :extension matchers (jeremyevans)
|
30
42
|
|
31
|
-
* Add r.root for
|
43
|
+
* Add r.root for GET / requests, for easier to read version of r.get "" (jeremyevans)
|
32
44
|
|
33
45
|
* Optimize r.is terminal matcher, remove :term hash matcher (jeremyevans)
|
34
46
|
|
data/README.rdoc
CHANGED
@@ -434,18 +434,17 @@ hash matcher:
|
|
434
434
|
== Root Method
|
435
435
|
|
436
436
|
As displayed above, you can also use +r.root+ as a match method. This
|
437
|
-
method matches
|
438
|
-
is similar to <tt>r.is ""</tt>, except that it does not
|
439
|
-
consume the +/+ from the path.
|
440
|
-
|
441
|
-
Unlike the other matching methods, +r.root+ does not take multiple
|
442
|
-
arguments and pass them to +r.on+. It only accepts an optional request
|
443
|
-
method symbol, so <tt>r.root :get</tt> is similar to <tt>r.get ""</tt>,
|
437
|
+
method matches <tt>GET /</tt> requests. +r.root+ is similar to <tt>r.get ""</tt>,
|
444
438
|
except that it does not consume the +/+ from the path.
|
445
439
|
|
440
|
+
Unlike the other matching methods, +r.root+ takes no arguments.
|
441
|
+
|
446
442
|
Note that +r.root+ does not match if the path is empty, you should use
|
447
|
-
|
448
|
-
the empty path or +/+, you can use <tt>r.
|
443
|
+
<tt>r.get true</tt> for that. If you want to match either the
|
444
|
+
the empty path or +/+, you can use <tt>r.get ["", true]</tt>.
|
445
|
+
|
446
|
+
Note that +r.root+ does not match non-GET requests, so to handle
|
447
|
+
<tt>POST /</tt> requests, use <tt>r.post ''</tt>.
|
449
448
|
|
450
449
|
== Request and Response
|
451
450
|
|
@@ -638,6 +637,8 @@ These plugins ship with roda:
|
|
638
637
|
all_verbs :: Adds routing methods to the request for all http verbs.
|
639
638
|
backtracking_array :: Allows array matchers to backtrack if later matchers
|
640
639
|
do not match.
|
640
|
+
content_for :: Allows storage of content in one template and retrieval of
|
641
|
+
that content in a different template.
|
641
642
|
csrf :: Adds CSRF protection and helper methods using
|
642
643
|
{rack_csrf}[https://github.com/baldowl/rack_csrf].
|
643
644
|
default_headers :: Override the default response headers used.
|
@@ -647,6 +648,7 @@ flash :: Adds a flash handler.
|
|
647
648
|
h :: Adds h method for html escaping.
|
648
649
|
halt :: Augments request#halt method to take status and/or body or status,
|
649
650
|
headers, and body.
|
651
|
+
head :: Treat HEAD requests like GET requests with an empty response body.
|
650
652
|
header_matchers :: Adds host, header, and accept hash matchers.
|
651
653
|
hooks :: Adds before and after methods to run code before and after requests.
|
652
654
|
indifferent_params :: Adds params method with indifferent access to params,
|
@@ -657,6 +659,8 @@ middleware :: Allows the Roda app to be used as a rack middleware, calling the
|
|
657
659
|
next middleware if no route matches.
|
658
660
|
multi_route :: Adds the ability for multiple named route blocks, with the
|
659
661
|
ability to dispatch to them add any point in the main route block.
|
662
|
+
not_allowed :: Adds support for automatically returning 405 Method Not Allowed
|
663
|
+
responses.
|
660
664
|
not_found :: Adds a +not_found+ block that is called for all 404 responses
|
661
665
|
without bodies.
|
662
666
|
pass :: Adds a pass method allowing you to skip the current +r.on+ block as if
|
@@ -664,6 +668,7 @@ pass :: Adds a pass method allowing you to skip the current +r.on+ block as if
|
|
664
668
|
per_thread_caching :: Switches the thread-safe cache from a shared cache to a
|
665
669
|
per-thread cache.
|
666
670
|
render :: Adds support for rendering templates via tilt, as described above.
|
671
|
+
render_each :: Render a template for each value in an enumerable.
|
667
672
|
streaming :: Adds support for streaming responses.
|
668
673
|
symbol_matchers :: Adds support for symbol-specific matching regexps.
|
669
674
|
symbol_views :: Allows match blocks to return template name symbols, uses the
|
@@ -30,14 +30,12 @@ class Roda
|
|
30
30
|
# The verb methods are defined via metaprogramming, so there
|
31
31
|
# isn't documentation for the individual methods created.
|
32
32
|
module AllVerbs
|
33
|
-
|
34
|
-
%w'delete head options link patch put trace unlink'.each do |
|
35
|
-
if ::Rack::Request.method_defined?("#{
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
end
|
40
|
-
END
|
33
|
+
def self.configure(app)
|
34
|
+
%w'delete head options link patch put trace unlink'.each do |v|
|
35
|
+
if ::Rack::Request.method_defined?("#{v}?")
|
36
|
+
app.request_module do
|
37
|
+
app::RodaRequest.def_verb_method(self, v)
|
38
|
+
end
|
41
39
|
end
|
42
40
|
end
|
43
41
|
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
class Roda
|
2
|
+
module RodaPlugins
|
3
|
+
# The content_for plugin is designed to be used with the
|
4
|
+
# render plugin, allowing you to store content inside one
|
5
|
+
# template, and retrieve that content inside a separate
|
6
|
+
# template. Most commonly, this is so view templates
|
7
|
+
# can set content for the layout template to display outside
|
8
|
+
# of the normal content pane.
|
9
|
+
#
|
10
|
+
# The content_for template probably only works with erb
|
11
|
+
# templates, and requires that you don't override the
|
12
|
+
# +:outvar+ render option. In the template in which you
|
13
|
+
# want to store content, call content_for with a block:
|
14
|
+
#
|
15
|
+
# <% content_for :foo do %>
|
16
|
+
# Some content here.
|
17
|
+
# <% end %>
|
18
|
+
#
|
19
|
+
# In the template in which you want to retrieve content,
|
20
|
+
# call content_for without the block:
|
21
|
+
#
|
22
|
+
# <%= content_for :foo %>
|
23
|
+
module ContentFor
|
24
|
+
module InstanceMethods
|
25
|
+
# If called with a block, store content enclosed by block
|
26
|
+
# under the given key. If called without a block, retrieve
|
27
|
+
# stored content with the given key, or return nil if there
|
28
|
+
# is no content stored with that key.
|
29
|
+
def content_for(key, &block)
|
30
|
+
if block
|
31
|
+
@_content_for ||= {}
|
32
|
+
buf_was = @_out_buf
|
33
|
+
@_out_buf = ''
|
34
|
+
yield
|
35
|
+
@_content_for[key] = @_out_buf
|
36
|
+
@_out_buf = buf_was
|
37
|
+
elsif @_content_for
|
38
|
+
@_content_for[key]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
register_plugin(:content_for, ContentFor)
|
45
|
+
end
|
46
|
+
end
|
data/lib/roda/plugins/halt.rb
CHANGED
@@ -1,18 +1,12 @@
|
|
1
1
|
class Roda
|
2
2
|
module RodaPlugins
|
3
|
-
# The halt plugin augments the standard request +halt+ method to
|
4
|
-
#
|
3
|
+
# The halt plugin augments the standard request +halt+ method to allow the response
|
4
|
+
# status, body, or headers to be changed when halting.
|
5
5
|
#
|
6
6
|
# After loading the halt plugin:
|
7
7
|
#
|
8
8
|
# plugin :halt
|
9
9
|
#
|
10
|
-
# You can call halt with no arguments to immediately stop processing:
|
11
|
-
#
|
12
|
-
# route do |r|
|
13
|
-
# r.halt
|
14
|
-
# end
|
15
|
-
#
|
16
10
|
# You can call the halt method with an integer to set the response status and return:
|
17
11
|
#
|
18
12
|
# route do |r|
|
@@ -38,7 +32,7 @@ class Roda
|
|
38
32
|
# end
|
39
33
|
#
|
40
34
|
# Note that there is a difference between provide status, headers, and body as separate
|
41
|
-
# arguments and providing them as a rack response array. With a rack response array,
|
35
|
+
# arguments and providing them as a single rack response array. With a rack response array,
|
42
36
|
# the values are used directly, while with 3 arguments, the headers given are merged into
|
43
37
|
# the existing headers and the given body is written to the existing response body.
|
44
38
|
module Halt
|
@@ -0,0 +1,56 @@
|
|
1
|
+
class Roda
|
2
|
+
module RodaPlugins
|
3
|
+
# The head plugin attempts to automatically handle HEAD requests,
|
4
|
+
# by treating them as GET requests and returning an empty body
|
5
|
+
# without modifying the response status or response headers.
|
6
|
+
#
|
7
|
+
# So for the following routes,
|
8
|
+
#
|
9
|
+
# route do |r|
|
10
|
+
# r.root do
|
11
|
+
# 'root'
|
12
|
+
# end
|
13
|
+
#
|
14
|
+
# r.get 'a' do
|
15
|
+
# 'a'
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# r.is 'b', :method=>[:get, :post] do
|
19
|
+
# 'b'
|
20
|
+
# end
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# HEAD requests for +/+, +/a+, and +/b+ will all return 200 status
|
24
|
+
# with an empty body.
|
25
|
+
module Head
|
26
|
+
module InstanceMethods
|
27
|
+
# Always use an empty response body for head requests, with a
|
28
|
+
# content length of 0.
|
29
|
+
def call(*)
|
30
|
+
res = super
|
31
|
+
if request.head?
|
32
|
+
res[2] = []
|
33
|
+
end
|
34
|
+
res
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
module RequestMethods
|
39
|
+
# Consider HEAD requests as GET requests.
|
40
|
+
def is_get?
|
41
|
+
super || head?
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
# If the current request is a HEAD request, match if one of
|
47
|
+
# the given methods is a GET request.
|
48
|
+
def match_method(method)
|
49
|
+
super || (!method.is_a?(Array) && head? && method.to_s.upcase == 'GET')
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
register_plugin(:head, Head)
|
55
|
+
end
|
56
|
+
end
|
@@ -35,14 +35,14 @@ class Roda
|
|
35
35
|
|
36
36
|
# Match if the given mimetype is one of the accepted mimetypes.
|
37
37
|
def match_accept(mimetype)
|
38
|
-
if env["HTTP_ACCEPT"].to_s.split(',').any?{|s| s.strip == mimetype}
|
38
|
+
if @env["HTTP_ACCEPT"].to_s.split(',').any?{|s| s.strip == mimetype}
|
39
39
|
response["Content-Type"] = mimetype
|
40
40
|
end
|
41
41
|
end
|
42
42
|
|
43
43
|
# Match if the given uppercase key is present inside the environment.
|
44
44
|
def match_header(key)
|
45
|
-
env[key.upcase.tr("-","_")]
|
45
|
+
@env[key.upcase.tr("-","_")]
|
46
46
|
end
|
47
47
|
|
48
48
|
# Match if the host of the request is the same as the hostname.
|
data/lib/roda/plugins/json.rb
CHANGED
@@ -2,7 +2,7 @@ require 'json'
|
|
2
2
|
|
3
3
|
class Roda
|
4
4
|
module RodaPlugins
|
5
|
-
# The json plugin allows
|
5
|
+
# The json plugin allows match blocks to return
|
6
6
|
# arrays or hashes, and have those arrays or hashes be
|
7
7
|
# converted to json which is used as the response body.
|
8
8
|
# It also sets the response content type to application/json.
|
@@ -0,0 +1,133 @@
|
|
1
|
+
class Roda
|
2
|
+
module RodaPlugins
|
3
|
+
# The not_allowed plugin makes Roda attempt to automatically
|
4
|
+
# support the 405 Method Not Allowed response status. The plugin
|
5
|
+
# changes the +r.get+ and +r.post+ verb methods to automatically
|
6
|
+
# return a 405 status if they are called with any arguments, and
|
7
|
+
# the arguments match but the request method does not match. So
|
8
|
+
# this code:
|
9
|
+
#
|
10
|
+
# r.get '' do
|
11
|
+
# "a"
|
12
|
+
# end
|
13
|
+
#
|
14
|
+
# will return a 200 response for <tt>GET /</tt> and a 405
|
15
|
+
# response for <tt>POST /</tt>.
|
16
|
+
#
|
17
|
+
# This plugin also changes the +r.is+ method so that if you use
|
18
|
+
# a verb method inside +r.is+, it returns a 405 status if none
|
19
|
+
# of the verb methods match. So this code:
|
20
|
+
#
|
21
|
+
# r.is '' do
|
22
|
+
# r.get do
|
23
|
+
# "a"
|
24
|
+
# end
|
25
|
+
#
|
26
|
+
# r.post do
|
27
|
+
# "b"
|
28
|
+
# end
|
29
|
+
# end
|
30
|
+
#
|
31
|
+
# will return a 200 response for <tt>GET /</tt> and <tt>POST /</tt>,
|
32
|
+
# but a 405 response for <tt>PUT /</tt>.
|
33
|
+
#
|
34
|
+
# Note that this plugin will probably not do what you want for
|
35
|
+
# code such as:
|
36
|
+
#
|
37
|
+
# r.get '' do
|
38
|
+
# "a"
|
39
|
+
# end
|
40
|
+
#
|
41
|
+
# r.post '' do
|
42
|
+
# "b"
|
43
|
+
# end
|
44
|
+
#
|
45
|
+
# Since for a <tt>POST /</tt> request, when +r.get+ method matches
|
46
|
+
# the path but not the request method, it will return an immediate
|
47
|
+
# 405 response. You must DRY up this code for it work correctly,
|
48
|
+
# like this:
|
49
|
+
#
|
50
|
+
# r.is '' do
|
51
|
+
# r.get do
|
52
|
+
# "a"
|
53
|
+
# end
|
54
|
+
#
|
55
|
+
# r.post do
|
56
|
+
# "b"
|
57
|
+
# end
|
58
|
+
# end
|
59
|
+
#
|
60
|
+
# In all cases where it uses a 405 response, it also sets the +Allow+
|
61
|
+
# header in the response to contain the request methods supported.
|
62
|
+
#
|
63
|
+
# To make this affect the verb methods added by the all_verbs plugin,
|
64
|
+
# load this plugin first.
|
65
|
+
module NotAllowed
|
66
|
+
# Redefine the +r.get+ and +r.post+ methods when loading the plugin.
|
67
|
+
def self.configure(app)
|
68
|
+
app.request_module do
|
69
|
+
app::RodaRequest.def_verb_method(self, :get)
|
70
|
+
app::RodaRequest.def_verb_method(self, :post)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
module RequestClassMethods
|
75
|
+
# Define a method named +verb+ in the given module which will
|
76
|
+
# return a 405 response if the method is called with any
|
77
|
+
# arguments and the arguments terminally match but the
|
78
|
+
# request method does not.
|
79
|
+
#
|
80
|
+
# If called without any arguments, check to see if the call
|
81
|
+
# is inside a terminal match, and in that case record the
|
82
|
+
# request method used.
|
83
|
+
def def_verb_method(mod, verb)
|
84
|
+
mod.class_eval(<<-END, __FILE__, __LINE__+1)
|
85
|
+
def #{verb}(*args, &block)
|
86
|
+
if args.empty?
|
87
|
+
@_is_verbs << "#{verb.to_s.upcase}" if @_is_verbs
|
88
|
+
always(&block) if #{verb == :get ? :is_get : verb}?
|
89
|
+
else
|
90
|
+
args << ::Roda::RodaPlugins::Base::RequestMethods::TERM
|
91
|
+
if_match(args) do
|
92
|
+
#{verb}(&block)
|
93
|
+
response.status = 405
|
94
|
+
response['Allow'] = '#{verb.to_s.upcase}'
|
95
|
+
nil
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
END
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
module RequestMethods
|
104
|
+
# Keep track of verb calls inside the block. If there are any
|
105
|
+
# verb calls inside the block, but the block returned, assume
|
106
|
+
# that the verb calls inside the block did not match, and
|
107
|
+
# since there was already a successful terminal match, the
|
108
|
+
# request method must not be allowed, so return a 405
|
109
|
+
# response in that case.
|
110
|
+
def is(*verbs)
|
111
|
+
super(*verbs) do
|
112
|
+
begin
|
113
|
+
@_is_verbs = []
|
114
|
+
|
115
|
+
ret = yield
|
116
|
+
|
117
|
+
unless @_is_verbs.empty?
|
118
|
+
response.status = 405
|
119
|
+
response['Allow'] = @_is_verbs.join(', ')
|
120
|
+
end
|
121
|
+
|
122
|
+
ret
|
123
|
+
ensure
|
124
|
+
@_is_verbs = nil
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
register_plugin(:not_allowed, NotAllowed)
|
132
|
+
end
|
133
|
+
end
|
data/lib/roda/plugins/pass.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
class Roda
|
2
2
|
module RodaPlugins
|
3
|
-
# The pass plugin adds a request +pass+ method to skip the current
|
3
|
+
# The pass plugin adds a request +pass+ method to skip the current match
|
4
4
|
# block as if it did not match.
|
5
5
|
#
|
6
6
|
# plugin :pass
|
@@ -17,14 +17,21 @@ class Roda
|
|
17
17
|
# end
|
18
18
|
module Pass
|
19
19
|
module RequestMethods
|
20
|
-
#
|
21
|
-
def
|
20
|
+
# Skip the current match block as if it did not match.
|
21
|
+
def pass
|
22
|
+
throw :pass
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
# Handle passing inside the match block.
|
28
|
+
def always
|
22
29
|
catch(:pass){super}
|
23
30
|
end
|
24
31
|
|
25
|
-
#
|
26
|
-
def
|
27
|
-
|
32
|
+
# Handle passing inside the match block.
|
33
|
+
def if_match(_)
|
34
|
+
catch(:pass){super}
|
28
35
|
end
|
29
36
|
end
|
30
37
|
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
class Roda
|
2
|
+
module RodaPlugins
|
3
|
+
# The render_each plugin allows you to render a template for each
|
4
|
+
# value in an enumerable, returning the concatention of all of the
|
5
|
+
# template renderings. For example:
|
6
|
+
#
|
7
|
+
# render_each([1,2,3], :foo)
|
8
|
+
#
|
9
|
+
# will render the +foo+ template 3 times. Each time the template
|
10
|
+
# is rendered, the local variable +foo+ will contain the given
|
11
|
+
# value (e.g. on the first rendering +foo+ is 1).
|
12
|
+
#
|
13
|
+
# You can pass additional render options via an options hash:
|
14
|
+
#
|
15
|
+
# render_each([1,2,3], :foo, :views=>'partials')
|
16
|
+
#
|
17
|
+
# One additional option supported by is +:local+, which sets the
|
18
|
+
# local variable containing the current value to use. So:
|
19
|
+
#
|
20
|
+
# render_each([1,2,3], :foo, :local=>:bar)
|
21
|
+
#
|
22
|
+
# Will render the +foo+ template, but the local variable used inside
|
23
|
+
# the template will be +bar+. You can use <tt>:local=>nil</tt> to
|
24
|
+
# not set a local variable inside the template.
|
25
|
+
module RenderEach
|
26
|
+
module InstanceMethods
|
27
|
+
EMPTY_STRING = ''.freeze
|
28
|
+
|
29
|
+
# For each value in enum, render the given template using the
|
30
|
+
# given opts. The template and options hash are passed to +render+.
|
31
|
+
# Additional options supported:
|
32
|
+
# :local :: The local variable to use for the current enum value
|
33
|
+
# inside the template. An explicit +nil+ value does not
|
34
|
+
# set a local variable. If not set, uses the template name.
|
35
|
+
def render_each(enum, template, opts={})
|
36
|
+
if as = opts.has_key?(:local)
|
37
|
+
as = opts[:local]
|
38
|
+
else
|
39
|
+
as = template.to_s.to_sym
|
40
|
+
end
|
41
|
+
|
42
|
+
if as
|
43
|
+
opts = opts.dup
|
44
|
+
if locals = opts[:locals]
|
45
|
+
locals = opts[:locals] = locals.dup
|
46
|
+
else
|
47
|
+
locals = opts[:locals] = {}
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
enum.map do |v|
|
52
|
+
locals[as] = v if as
|
53
|
+
render(template, opts)
|
54
|
+
end.join
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
register_plugin(:render_each, RenderEach)
|
60
|
+
end
|
61
|
+
end
|
@@ -7,7 +7,7 @@ class Roda
|
|
7
7
|
# # ...
|
8
8
|
# end
|
9
9
|
#
|
10
|
-
# By default this will match all segments. However, if your usernames
|
10
|
+
# By default this will match all nonempty segments. However, if your usernames
|
11
11
|
# must be 6-20 characters, and can only contain +a-z+ and +0-9+, you can do:
|
12
12
|
#
|
13
13
|
# plugin :symbol_matchers
|
@@ -28,11 +28,12 @@ class Roda
|
|
28
28
|
#
|
29
29
|
# By default, this plugin sets up the following symbol matchers:
|
30
30
|
#
|
31
|
-
# :d :: <tt
|
32
|
-
# :format :: <tt>/(?:\.(\w+))?/</tt>, an optional format
|
31
|
+
# :d :: <tt>/(\d+)/</tt>, a decimal segment
|
32
|
+
# :format :: <tt>/(?:\.(\w+))?/</tt>, an optional format/extension
|
33
33
|
# :opt :: <tt>/(?:\/([^\/]+))?</tt>, an optional segment
|
34
34
|
# :optd :: <tt>/(?:\/(\d+))?</tt>, an optional decimal segment
|
35
|
-
# :
|
35
|
+
# :rest :: <tt>/(.*)/</tt>, all remaining characters, if any
|
36
|
+
# :w :: <tt>/(\w+)/</tt>, a alphanumeric segment
|
36
37
|
#
|
37
38
|
# Note that because of how segment matching works, :format, :opt, and :optd
|
38
39
|
# are only going to work inside of a string, like this:
|
@@ -46,6 +47,7 @@ class Roda
|
|
46
47
|
app.symbol_matcher(:format, /(?:\.(\w+))?/)
|
47
48
|
app.symbol_matcher(:opt, /(?:\/([^\/]+))?/)
|
48
49
|
app.symbol_matcher(:optd, /(?:\/(\d+))?/)
|
50
|
+
app.symbol_matcher(:rest, /(.*)/)
|
49
51
|
app.symbol_matcher(:w, /(\w+)/)
|
50
52
|
end
|
51
53
|
|
@@ -57,6 +59,8 @@ class Roda
|
|
57
59
|
end
|
58
60
|
|
59
61
|
module RequestMethods
|
62
|
+
private
|
63
|
+
|
60
64
|
# Allow for symbol specific regexps, by using match_symbol_#{s} if
|
61
65
|
# defined. If not defined, calls super for the default behavior.
|
62
66
|
def _match_symbol_regexp(s)
|
data/lib/roda/version.rb
CHANGED
data/lib/roda.rb
CHANGED
@@ -301,6 +301,17 @@ class Roda
|
|
301
301
|
pattern
|
302
302
|
end
|
303
303
|
|
304
|
+
# Define a verb method in the given that will yield to the match block
|
305
|
+
# if the request method matches and there are either no arguments or
|
306
|
+
# there is a successful terminal match on the arguments.
|
307
|
+
def def_verb_method(mod, verb)
|
308
|
+
mod.class_eval(<<-END, __FILE__, __LINE__+1)
|
309
|
+
def #{verb}(*args, &block)
|
310
|
+
_verb(args, &block) if #{verb == :get ? :is_get : verb}?
|
311
|
+
end
|
312
|
+
END
|
313
|
+
end
|
314
|
+
|
304
315
|
# Since RodaRequest is anonymously subclassed when Roda is subclassed,
|
305
316
|
# and then assigned to a constant of the Roda subclass, make inspect
|
306
317
|
# reflect the likely name for the class.
|
@@ -314,7 +325,7 @@ class Roda
|
|
314
325
|
# pattern requires the path starts with a string and does not match partial
|
315
326
|
# segments.
|
316
327
|
def consume_pattern(pattern)
|
317
|
-
/\A(\/(?:#{pattern}))(
|
328
|
+
/\A(\/(?:#{pattern}))(?=\/|\z)/
|
318
329
|
end
|
319
330
|
end
|
320
331
|
|
@@ -327,8 +338,8 @@ class Roda
|
|
327
338
|
EMPTY_STRING = "".freeze
|
328
339
|
SLASH = "/".freeze
|
329
340
|
SEGMENT = "([^\\/]+)".freeze
|
330
|
-
EMPTY_ARRAY = [].freeze
|
331
341
|
TERM_INSPECT = "TERM".freeze
|
342
|
+
GET_REQUEST_METHOD = 'GET'.freeze
|
332
343
|
|
333
344
|
TERM = Object.new
|
334
345
|
def TERM.inspect
|
@@ -354,14 +365,7 @@ class Roda
|
|
354
365
|
# As request routing modifies SCRIPT_NAME and PATH_INFO, this exists
|
355
366
|
# as a helper method to get the full request of the path info.
|
356
367
|
def full_path_info
|
357
|
-
"#{env[SCRIPT_NAME]}#{env[PATH_INFO]}"
|
358
|
-
end
|
359
|
-
|
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.
|
363
|
-
def get(*args, &block)
|
364
|
-
_verb(args, &block) if get?
|
368
|
+
"#{@env[SCRIPT_NAME]}#{@env[PATH_INFO]}"
|
365
369
|
end
|
366
370
|
|
367
371
|
# Immediately stop execution of the route block and return the given
|
@@ -371,7 +375,14 @@ class Roda
|
|
371
375
|
throw :halt, res
|
372
376
|
end
|
373
377
|
|
374
|
-
#
|
378
|
+
# Whether this request is a get request. Similar to the default
|
379
|
+
# Rack::Request get? method, but can be overridden without changing
|
380
|
+
# rack's behavior.
|
381
|
+
def is_get?
|
382
|
+
@env[REQUEST_METHOD] == GET_REQUEST_METHOD
|
383
|
+
end
|
384
|
+
|
385
|
+
# Handle match block return values. By default, if a string is given
|
375
386
|
# and the response is empty, use the string as the response body.
|
376
387
|
def block_result(result)
|
377
388
|
res = response
|
@@ -383,14 +394,20 @@ class Roda
|
|
383
394
|
# Show information about current request, including request class,
|
384
395
|
# request method and full path.
|
385
396
|
def inspect
|
386
|
-
"#<#{self.class.inspect} #{env[REQUEST_METHOD]} #{full_path_info}>"
|
397
|
+
"#<#{self.class.inspect} #{@env[REQUEST_METHOD]} #{full_path_info}>"
|
387
398
|
end
|
388
399
|
|
389
|
-
#
|
390
|
-
#
|
400
|
+
# Does a terminal match on the input, matching only if the arguments
|
401
|
+
# have fully matched the patch.
|
391
402
|
def is(*args, &block)
|
392
|
-
args
|
393
|
-
|
403
|
+
if args.empty?
|
404
|
+
if @env[PATH_INFO] == EMPTY_STRING
|
405
|
+
always(&block)
|
406
|
+
end
|
407
|
+
else
|
408
|
+
args << TERM
|
409
|
+
if_match(args, &block)
|
410
|
+
end
|
394
411
|
end
|
395
412
|
|
396
413
|
# Attempts to match on all of the arguments. If all of the
|
@@ -399,14 +416,11 @@ class Roda
|
|
399
416
|
# If any of the arguments fails, ensures the request state is
|
400
417
|
# returned to that before matches were attempted.
|
401
418
|
def on(*args, &block)
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
# regular match.
|
408
|
-
def post(*args, &block)
|
409
|
-
_verb(args, &block) if post?
|
419
|
+
if args.empty?
|
420
|
+
always(&block)
|
421
|
+
else
|
422
|
+
if_match(args, &block)
|
423
|
+
end
|
410
424
|
end
|
411
425
|
|
412
426
|
# The response related to the current request.
|
@@ -420,19 +434,17 @@ class Roda
|
|
420
434
|
throw :halt, response.finish
|
421
435
|
end
|
422
436
|
|
423
|
-
# If
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
if env[PATH_INFO] == SLASH && (!request_method || send(:"#{request_method}?"))
|
428
|
-
_on(EMPTY_ARRAY, &block)
|
437
|
+
# If this is a GET request for the root ("/"), yield to the match block.
|
438
|
+
def root(&block)
|
439
|
+
if @env[PATH_INFO] == SLASH && is_get?
|
440
|
+
always(&block)
|
429
441
|
end
|
430
442
|
end
|
431
443
|
|
432
444
|
# Call the given rack app with the environment and immediately return
|
433
445
|
# the response as the response for this request.
|
434
446
|
def run(app)
|
435
|
-
throw :halt, app.call(env)
|
447
|
+
throw :halt, app.call(@env)
|
436
448
|
end
|
437
449
|
|
438
450
|
private
|
@@ -480,31 +492,21 @@ class Roda
|
|
480
492
|
SEGMENT
|
481
493
|
end
|
482
494
|
|
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
495
|
# Backbone of the verb method support, using a terminal match if
|
502
496
|
# args is not empty, or a regular match if it is empty.
|
503
497
|
def _verb(args, &block)
|
504
|
-
|
498
|
+
if args.empty?
|
499
|
+
always(&block)
|
500
|
+
else
|
505
501
|
args << TERM
|
502
|
+
if_match(args, &block)
|
506
503
|
end
|
507
|
-
|
504
|
+
end
|
505
|
+
|
506
|
+
# Yield to the match block and return rack response after the block returns.
|
507
|
+
def always
|
508
|
+
block_result(yield)
|
509
|
+
throw :halt, response.finish
|
508
510
|
end
|
509
511
|
|
510
512
|
# The body to use for the response if the response does not return
|
@@ -521,17 +523,38 @@ class Roda
|
|
521
523
|
# SCRIPT_NAME to include the matched path, removes the matched
|
522
524
|
# path from PATH_INFO, and updates captures with any regex captures.
|
523
525
|
def consume(pattern)
|
526
|
+
env = @env
|
524
527
|
return unless matchdata = env[PATH_INFO].match(pattern)
|
525
528
|
|
526
529
|
vars = matchdata.captures
|
527
530
|
|
528
531
|
# Don't mutate SCRIPT_NAME, breaks try
|
529
532
|
env[SCRIPT_NAME] += vars.shift
|
530
|
-
env[PATH_INFO] =
|
533
|
+
env[PATH_INFO] = matchdata.post_match
|
531
534
|
|
532
535
|
captures.concat(vars)
|
533
536
|
end
|
534
537
|
|
538
|
+
# If all of the arguments match, yields to the match block and
|
539
|
+
# returns the rack response when the block returns. If any of
|
540
|
+
# the match arguments doesn't match, does nothing.
|
541
|
+
def if_match(args)
|
542
|
+
env = @env
|
543
|
+
script = env[SCRIPT_NAME]
|
544
|
+
path = env[PATH_INFO]
|
545
|
+
|
546
|
+
# For every block, we make sure to reset captures so that
|
547
|
+
# nesting matchers won't mess with each other's captures.
|
548
|
+
captures.clear
|
549
|
+
|
550
|
+
return unless match_all(args)
|
551
|
+
block_result(yield(*captures))
|
552
|
+
throw :halt, response.finish
|
553
|
+
ensure
|
554
|
+
env[SCRIPT_NAME] = script
|
555
|
+
env[PATH_INFO] = path
|
556
|
+
end
|
557
|
+
|
535
558
|
# Attempt to match the argument to the given request, handling
|
536
559
|
# common ruby types.
|
537
560
|
def match(matcher)
|
@@ -543,7 +566,7 @@ class Roda
|
|
543
566
|
when Symbol
|
544
567
|
_match_symbol(matcher)
|
545
568
|
when TERM
|
546
|
-
env[PATH_INFO] == EMPTY_STRING
|
569
|
+
@env[PATH_INFO] == EMPTY_STRING
|
547
570
|
when Hash
|
548
571
|
_match_hash(matcher)
|
549
572
|
when Array
|
@@ -563,7 +586,7 @@ class Roda
|
|
563
586
|
# Match files with the given extension. Requires that the
|
564
587
|
# request path end with the extension.
|
565
588
|
def match_extension(ext)
|
566
|
-
consume(self.class.cached_matcher(ext){"([^\\/]+?)\.#{ext}\\z"})
|
589
|
+
consume(self.class.cached_matcher([:extension, ext]){"([^\\/]+?)\.#{ext}\\z"})
|
567
590
|
end
|
568
591
|
|
569
592
|
# Match by request method. This can be an array if you want
|
@@ -572,7 +595,7 @@ class Roda
|
|
572
595
|
if type.is_a?(Array)
|
573
596
|
type.any?{|t| match_method(t)}
|
574
597
|
else
|
575
|
-
type.to_s.upcase == env[REQUEST_METHOD]
|
598
|
+
type.to_s.upcase == @env[REQUEST_METHOD]
|
576
599
|
end
|
577
600
|
end
|
578
601
|
|
@@ -698,4 +721,6 @@ class Roda
|
|
698
721
|
|
699
722
|
extend RodaPlugins::Base::ClassMethods
|
700
723
|
plugin RodaPlugins::Base
|
724
|
+
RodaRequest.def_verb_method(RodaPlugins::Base::RequestMethods, :get)
|
725
|
+
RodaRequest.def_verb_method(RodaPlugins::Base::RequestMethods, :post)
|
701
726
|
end
|
data/spec/matchers_spec.rb
CHANGED
@@ -487,21 +487,10 @@ describe "path matchers" do
|
|
487
487
|
end
|
488
488
|
|
489
489
|
body.should == 'Home'
|
490
|
+
status('REQUEST_METHOD'=>'POST').should == 404
|
490
491
|
status("//").should == 404
|
491
492
|
status("/foo").should == 404
|
492
493
|
end
|
493
|
-
|
494
|
-
it "matching the root with the root method and request method symbol" do
|
495
|
-
app do |r|
|
496
|
-
r.root(:get) do
|
497
|
-
"Home"
|
498
|
-
end
|
499
|
-
end
|
500
|
-
|
501
|
-
body.should == 'Home'
|
502
|
-
status("//").should == 404
|
503
|
-
status('REQUEST_METHOD'=>"POST").should == 404
|
504
|
-
end
|
505
494
|
end
|
506
495
|
|
507
496
|
describe "root/empty segment matching" do
|
@@ -658,15 +647,15 @@ end
|
|
658
647
|
describe "extension matcher" do
|
659
648
|
it "should match given file extensions" do
|
660
649
|
app do |r|
|
661
|
-
r.on "
|
650
|
+
r.on "css" do
|
662
651
|
r.on :extension=>"css" do |file|
|
663
652
|
file
|
664
653
|
end
|
665
654
|
end
|
666
655
|
end
|
667
656
|
|
668
|
-
body("/
|
669
|
-
status("/
|
657
|
+
body("/css/reset.css").should == 'reset'
|
658
|
+
status("/css/reset.bar").should == 404
|
670
659
|
end
|
671
660
|
end
|
672
661
|
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require File.expand_path("spec_helper", File.dirname(File.dirname(__FILE__)))
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'tilt/erb'
|
5
|
+
rescue LoadError
|
6
|
+
warn "tilt not installed, skipping content_for plugin test"
|
7
|
+
else
|
8
|
+
describe "content_for plugin" do
|
9
|
+
before do
|
10
|
+
app(:bare) do
|
11
|
+
plugin :render
|
12
|
+
render_opts[:views] = "./spec/views"
|
13
|
+
plugin :content_for
|
14
|
+
|
15
|
+
route do |r|
|
16
|
+
r.root do
|
17
|
+
view(:inline=>"<% content_for :foo do %>foo<% end %>bar", :layout=>{:inline=>'<%= yield %> <%= content_for(:foo) %>'})
|
18
|
+
end
|
19
|
+
r.get 'a' do
|
20
|
+
view(:inline=>"bar", :layout=>{:inline=>'<%= content_for(:foo) %> <%= yield %>'})
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should be able to set content in template and get that content in the layout" do
|
27
|
+
body.strip.should == "bar foo"
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should work if content is not set by the template" do
|
31
|
+
body('/a').strip.should == "bar"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require File.expand_path("spec_helper", File.dirname(File.dirname(__FILE__)))
|
2
|
+
|
3
|
+
describe "head plugin" do
|
4
|
+
it "considers HEAD requests as GET requests which return no body" do
|
5
|
+
app(:head) do |r|
|
6
|
+
r.root do
|
7
|
+
'root'
|
8
|
+
end
|
9
|
+
|
10
|
+
r.get 'a' do
|
11
|
+
'a'
|
12
|
+
end
|
13
|
+
|
14
|
+
r.is 'b', :method=>[:get, :post] do
|
15
|
+
'b'
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
s, h, b = req
|
20
|
+
s.should == 200
|
21
|
+
h['Content-Length'].should == '4'
|
22
|
+
b.should == ['root']
|
23
|
+
|
24
|
+
s, h, b = req('REQUEST_METHOD' => 'HEAD')
|
25
|
+
s.should == 200
|
26
|
+
h['Content-Length'].should == '4'
|
27
|
+
b.should == []
|
28
|
+
|
29
|
+
body('/a').should == 'a'
|
30
|
+
status('/a', 'REQUEST_METHOD' => 'HEAD').should == 200
|
31
|
+
|
32
|
+
body('/b').should == 'b'
|
33
|
+
status('/b', 'REQUEST_METHOD' => 'HEAD').should == 200
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require File.expand_path("spec_helper", File.dirname(File.dirname(__FILE__)))
|
2
|
+
|
3
|
+
describe "not_allowed plugin" do
|
4
|
+
it "skips the current block if pass is called" do
|
5
|
+
app(:not_allowed) do |r|
|
6
|
+
r.get '' do
|
7
|
+
'a'
|
8
|
+
end
|
9
|
+
|
10
|
+
r.is "c" do
|
11
|
+
r.get do
|
12
|
+
"cg"
|
13
|
+
end
|
14
|
+
|
15
|
+
r.post do
|
16
|
+
"cp"
|
17
|
+
end
|
18
|
+
|
19
|
+
"c"
|
20
|
+
end
|
21
|
+
|
22
|
+
r.get do
|
23
|
+
r.is 'b' do
|
24
|
+
'b'
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
body.should == 'a'
|
30
|
+
status('REQUEST_METHOD'=>'POST').should == 405
|
31
|
+
header('Allow', 'REQUEST_METHOD'=>'POST').should == 'GET'
|
32
|
+
|
33
|
+
body('/b').should == 'b'
|
34
|
+
status('/b', 'REQUEST_METHOD'=>'POST').should == 404
|
35
|
+
|
36
|
+
body('/c').should == 'cg'
|
37
|
+
body('/c', 'REQUEST_METHOD'=>'POST').should == 'cp'
|
38
|
+
body('/c', 'REQUEST_METHOD'=>'PATCH').should == 'c'
|
39
|
+
status('/c', 'REQUEST_METHOD'=>'PATCH').should == 405
|
40
|
+
header('Allow', '/c', 'REQUEST_METHOD'=>'PATCH').should == 'GET, POST'
|
41
|
+
end
|
42
|
+
end
|
data/spec/plugin/pass_spec.rb
CHANGED
@@ -3,6 +3,11 @@ require File.expand_path("spec_helper", File.dirname(File.dirname(__FILE__)))
|
|
3
3
|
describe "pass plugin" do
|
4
4
|
it "skips the current block if pass is called" do
|
5
5
|
app(:pass) do |r|
|
6
|
+
r.root do
|
7
|
+
r.pass if env['FOO']
|
8
|
+
'root'
|
9
|
+
end
|
10
|
+
|
6
11
|
r.on :id do |id|
|
7
12
|
r.pass if id == 'foo'
|
8
13
|
id
|
@@ -13,11 +18,12 @@ describe "pass plugin" do
|
|
13
18
|
end
|
14
19
|
end
|
15
20
|
|
21
|
+
body.should == 'root'
|
22
|
+
status('FOO'=>true).should == 404
|
16
23
|
body("/a").should == 'a'
|
17
24
|
body("/a/b").should == 'a'
|
18
25
|
body("/foo/a").should == 'fooa'
|
19
26
|
body("/foo/a/b").should == 'fooa'
|
20
27
|
status("/foo").should == 404
|
21
|
-
status.should == 404
|
22
28
|
end
|
23
29
|
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require File.expand_path("spec_helper", File.dirname(File.dirname(__FILE__)))
|
2
|
+
|
3
|
+
describe "render_each plugin" do
|
4
|
+
it "calls render with each argument, returning joined string with all results" do
|
5
|
+
app(:bare) do
|
6
|
+
plugin :render_each
|
7
|
+
def render(t, opts)
|
8
|
+
"r#{t}#{opts[:locals][:foo] if opts[:locals]}#{opts[:bar]} "
|
9
|
+
end
|
10
|
+
|
11
|
+
route do |r|
|
12
|
+
r.root do
|
13
|
+
render_each([1,2,3], :foo)
|
14
|
+
end
|
15
|
+
|
16
|
+
r.is 'a' do
|
17
|
+
render_each([1,2,3], :bar, :local=>:foo, :bar=>4)
|
18
|
+
end
|
19
|
+
|
20
|
+
r.is 'b' do
|
21
|
+
render_each([1,2,3], :bar, :local=>nil)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
body.should == 'rfoo1 rfoo2 rfoo3 '
|
27
|
+
body("/a").should == 'rbar14 rbar24 rbar34 '
|
28
|
+
body("/b").should == 'rbar rbar rbar '
|
29
|
+
end
|
30
|
+
end
|
@@ -27,6 +27,10 @@ describe "symbol_matchers plugin" do
|
|
27
27
|
"f#{f}"
|
28
28
|
end
|
29
29
|
|
30
|
+
r.is 'q:rest' do |r|
|
31
|
+
"rest#{r}"
|
32
|
+
end
|
33
|
+
|
30
34
|
r.is :w do |w|
|
31
35
|
"w#{w}"
|
32
36
|
end
|
@@ -58,5 +62,7 @@ describe "symbol_matchers plugin" do
|
|
58
62
|
body("/1/1a/f").should == 'dwf11af'
|
59
63
|
body("/12/1azy/fffff").should == 'dwf121azyfffff'
|
60
64
|
status("/1/f/a").should == 404
|
65
|
+
body("/qa/b/c/d//f/g").should == 'resta/b/c/d//f/g'
|
66
|
+
body('/q').should == 'rest'
|
61
67
|
end
|
62
68
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: roda-cj
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.9.
|
4
|
+
version: 0.9.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jeremy Evans
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-08-
|
11
|
+
date: 2014-08-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rack
|
@@ -88,12 +88,16 @@ files:
|
|
88
88
|
- lib/roda/plugins/flash.rb
|
89
89
|
- lib/roda/plugins/csrf.rb
|
90
90
|
- lib/roda/plugins/symbol_views.rb
|
91
|
+
- lib/roda/plugins/content_for.rb
|
91
92
|
- lib/roda/plugins/halt.rb
|
93
|
+
- lib/roda/plugins/not_allowed.rb
|
92
94
|
- lib/roda/plugins/header_matchers.rb
|
93
95
|
- lib/roda/plugins/per_thread_caching.rb
|
94
96
|
- lib/roda/plugins/error_handler.rb
|
95
97
|
- lib/roda/plugins/indifferent_params.rb
|
98
|
+
- lib/roda/plugins/head.rb
|
96
99
|
- lib/roda/plugins/json.rb
|
100
|
+
- lib/roda/plugins/render_each.rb
|
97
101
|
- lib/roda/plugins/streaming.rb
|
98
102
|
- lib/roda/plugins/all_verbs.rb
|
99
103
|
- lib/roda/plugins/not_found.rb
|
@@ -118,6 +122,7 @@ files:
|
|
118
122
|
- spec/plugin/error_handler_spec.rb
|
119
123
|
- spec/plugin/json_spec.rb
|
120
124
|
- spec/plugin/symbol_views_spec.rb
|
125
|
+
- spec/plugin/head_spec.rb
|
121
126
|
- spec/plugin/all_verbs_spec.rb
|
122
127
|
- spec/plugin/render_spec.rb
|
123
128
|
- spec/plugin/symbol_matchers_spec.rb
|
@@ -126,12 +131,15 @@ files:
|
|
126
131
|
- spec/plugin/default_headers_spec.rb
|
127
132
|
- spec/plugin/backtracking_array_spec.rb
|
128
133
|
- spec/plugin/per_thread_caching_spec.rb
|
134
|
+
- spec/plugin/content_for_spec.rb
|
129
135
|
- spec/plugin/hooks_spec.rb
|
130
136
|
- spec/plugin/streaming_spec.rb
|
131
137
|
- spec/plugin/flash_spec.rb
|
132
138
|
- spec/plugin/pass_spec.rb
|
139
|
+
- spec/plugin/render_each_spec.rb
|
133
140
|
- spec/plugin/not_found_spec.rb
|
134
141
|
- spec/plugin/middleware_spec.rb
|
142
|
+
- spec/plugin/not_allowed_spec.rb
|
135
143
|
- spec/plugin/h_spec.rb
|
136
144
|
- spec/plugin/halt_spec.rb
|
137
145
|
- spec/plugin/view_subdirs_spec.rb
|