roda 3.46.0 → 3.50.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG +22 -0
- data/README.rdoc +2 -1
- data/doc/release_notes/3.47.0.txt +13 -0
- data/doc/release_notes/3.48.0.txt +10 -0
- data/doc/release_notes/3.49.0.txt +18 -0
- data/doc/release_notes/3.50.0.txt +21 -0
- data/lib/roda/plugins/_optimized_matching.rb +86 -8
- data/lib/roda/plugins/capture_erb.rb +41 -0
- data/lib/roda/plugins/content_for.rb +7 -13
- data/lib/roda/plugins/content_security_policy.rb +2 -2
- data/lib/roda/plugins/hash_routes.rb +16 -4
- data/lib/roda/plugins/inject_erb.rb +33 -0
- data/lib/roda/plugins/mail_processor.rb +1 -1
- data/lib/roda/plugins/module_include.rb +1 -1
- data/lib/roda/plugins/multi_route.rb +22 -94
- data/lib/roda/plugins/multi_run.rb +1 -1
- data/lib/roda/plugins/named_routes.rb +160 -0
- data/lib/roda/plugins/render.rb +81 -3
- data/lib/roda/plugins/run_handler.rb +1 -1
- data/lib/roda/plugins/shared_vars.rb +1 -1
- data/lib/roda/plugins/sinatra_helpers.rb +1 -1
- data/lib/roda/plugins/status_303.rb +1 -2
- data/lib/roda/plugins/type_routing.rb +1 -1
- data/lib/roda/plugins/typecast_params.rb +2 -2
- data/lib/roda/request.rb +2 -2
- data/lib/roda/version.rb +1 -1
- data/lib/roda.rb +1 -1
- metadata +13 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b9da24f38f427b9256f05e92f40d67fe98bbdefb86419e5b8c13243f88418f8a
|
4
|
+
data.tar.gz: 3acabff7d8879126d6fdf2199e0935c96d0ea1c975feef874122d67d2afe7315
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7535b67bed981d52d337643c24f13b206163392594b3f969e611b79020a8578994b934a34abc186cf07850725c1ce8a99fbcb859b58cce289b8138b329ca4c68
|
7
|
+
data.tar.gz: 3fdbb4aa63d1cd82a7f6cbdfe01a9804388ab8b4461f9dda825332f030a1179737a21be4ea59c9e52fa856f6952d77578e972263397c62de4c42680340472313
|
data/CHANGELOG
CHANGED
@@ -1,3 +1,25 @@
|
|
1
|
+
= 3.50.0 (2021-11-12)
|
2
|
+
|
3
|
+
* Add capture_erb plugin for capturing ERB template blocks, instead of injecting them into the template output (jeremyevans)
|
4
|
+
|
5
|
+
* Add inject_erb plugin for injecting content directly into ERB template output (jeremyevans)
|
6
|
+
|
7
|
+
* Allow hash_branch and hash_path in hash_routes plugin to be called without a block to remove existing handler (jeremyevans)
|
8
|
+
|
9
|
+
= 3.49.0 (2021-10-13)
|
10
|
+
|
11
|
+
* Switch block_given? to defined?(yield) (jeremyevans)
|
12
|
+
|
13
|
+
* Automatically optimize remaining r.is/r.get/r.post calls with a single argument (jeremyevans)
|
14
|
+
|
15
|
+
= 3.48.0 (2021-09-13)
|
16
|
+
|
17
|
+
* Extract named_routes plugin from multi_route plugin (jeremyevans)
|
18
|
+
|
19
|
+
= 3.47.0 (2021-08-13)
|
20
|
+
|
21
|
+
* Automatically optimize remaining r.on calls with a single argument (jeremyevans)
|
22
|
+
|
1
23
|
= 3.46.0 (2021-07-12)
|
2
24
|
|
3
25
|
* Automatically optimize r.on/r.is/r.get/r.post methods with a single string, String, Integer, or regexp argument (jeremyevans)
|
data/README.rdoc
CHANGED
@@ -1109,7 +1109,8 @@ Roda's plugin system is based on the plugin system used by
|
|
1109
1109
|
Roda fully supports the currently supported versions of Ruby (MRI) and JRuby. It may
|
1110
1110
|
support unsupported versions of Ruby or JRuby, but such support may be dropped in any
|
1111
1111
|
minor version if keeping it becomes a support issue. The minimum Ruby version
|
1112
|
-
required to run the current version of Roda is 1.9.2
|
1112
|
+
required to run the current version of Roda is 1.9.2, and the minimum JRuby version is
|
1113
|
+
9.0.0.0.
|
1113
1114
|
|
1114
1115
|
== License
|
1115
1116
|
|
@@ -0,0 +1,13 @@
|
|
1
|
+
= Improvements
|
2
|
+
|
3
|
+
* The r.on optimization added in 3.46.0 has been extended to optimize
|
4
|
+
all single argument calls. This results in the following speedups
|
5
|
+
based on argument type:
|
6
|
+
|
7
|
+
* Hash matching: 10%
|
8
|
+
* Array/Symbol/Class matching: 15%
|
9
|
+
* Proc matching: 25%
|
10
|
+
* true matching: 45%
|
11
|
+
* false/nil matching: 65%
|
12
|
+
|
13
|
+
* Other minor performance improvements have been made.
|
@@ -0,0 +1,10 @@
|
|
1
|
+
= New Features
|
2
|
+
|
3
|
+
* A named_routes plugin has been added, for defining named route
|
4
|
+
blocks that you can dispatch to with r.route. This feature was
|
5
|
+
previously available as part of the multi_route plugin, but there
|
6
|
+
are cases where the r.route method and support for named routes is
|
7
|
+
helpful even when the multi_route plugin is not used (such as when
|
8
|
+
the hash_routes plugin is used instead of the multi_route plugin).
|
9
|
+
The multi_route plugin now depends on the named_routes plugin, so
|
10
|
+
this change should not cause any backwards compatibility issues.
|
@@ -0,0 +1,18 @@
|
|
1
|
+
= Improvements
|
2
|
+
|
3
|
+
* The r.is optimization added in 3.46.0 has been extended to optimize
|
4
|
+
all single argument calls. This results in the following speedups
|
5
|
+
based on argument type:
|
6
|
+
|
7
|
+
* Hash/Class matching: 20%
|
8
|
+
* Symbol matching: 25%
|
9
|
+
* Array matching: 35%
|
10
|
+
* Proc matching: 50%
|
11
|
+
* false/nil matching: 65%
|
12
|
+
|
13
|
+
* Roda now uses defined?(yield) instead of block_given? internally
|
14
|
+
for better performance on CRuby. defined?(yield) is faster as it is
|
15
|
+
built into the VM, while block_given? is a regular method and has
|
16
|
+
the overhead of calling a regular method. Note that defined?(yield)
|
17
|
+
is not implemented correctly on JRuby before 9.0.0.0, so this
|
18
|
+
release of Roda drops support for JRuby versions before 9.0.0.0.
|
@@ -0,0 +1,21 @@
|
|
1
|
+
= New Features
|
2
|
+
|
3
|
+
* An inject_erb plugin has been added, adding an inject_erb method
|
4
|
+
that allows for injecting content directly into the template output
|
5
|
+
for the template currently being rendered. This allows you to more
|
6
|
+
easily wrap blocks in templates, by calling methods that accept
|
7
|
+
template blocks and injecting content before and after the block.
|
8
|
+
|
9
|
+
* A capture_erb plugin has been added, adding a capture_erb method
|
10
|
+
for capturing a template block in an erb template and returning
|
11
|
+
the content appended during the block as a string, instead of
|
12
|
+
having the content of the template block be included directly into
|
13
|
+
the template output. This can be combined with the inject_erb
|
14
|
+
plugin to inject modified versions of captured blocks into template
|
15
|
+
output.
|
16
|
+
|
17
|
+
* The hash_routes plugin now allows calling hash_branch and hash_path
|
18
|
+
without a block in order to remove the existing route handler. This
|
19
|
+
is designed to be used with code reloading libraries, so that if a
|
20
|
+
route file is deleted, the related hash branches/paths are also
|
21
|
+
removed, without having to reload all route files.
|
@@ -52,20 +52,60 @@ class Roda
|
|
52
52
|
end
|
53
53
|
end
|
54
54
|
elsif matcher == Integer
|
55
|
-
if matchdata =
|
55
|
+
if matchdata = /\A\/(\d+)(?=\/|\z)/.match(@remaining_path)
|
56
56
|
@remaining_path = matchdata.post_match
|
57
57
|
always{yield(matchdata[1].to_i)}
|
58
58
|
end
|
59
59
|
else
|
60
|
-
|
60
|
+
path = @remaining_path
|
61
|
+
captures = @captures.clear
|
62
|
+
meth = :"_match_class_#{matcher}"
|
63
|
+
if respond_to?(meth, true)
|
64
|
+
# Allow calling private methods, as match methods are generally private
|
65
|
+
if send(meth, &block)
|
66
|
+
block_result(yield(*captures))
|
67
|
+
throw :halt, response.finish
|
68
|
+
else
|
69
|
+
@remaining_path = path
|
70
|
+
false
|
71
|
+
end
|
72
|
+
else
|
73
|
+
unsupported_matcher(matcher)
|
74
|
+
end
|
61
75
|
end
|
62
76
|
when Regexp
|
63
|
-
if matchdata =
|
77
|
+
if matchdata = self.class.cached_matcher(matcher){matcher}.match(@remaining_path)
|
64
78
|
@remaining_path = matchdata.post_match
|
65
79
|
always{yield(*matchdata.captures)}
|
66
80
|
end
|
81
|
+
when true
|
82
|
+
always(&block)
|
83
|
+
when false, nil
|
84
|
+
# nothing
|
67
85
|
else
|
68
|
-
|
86
|
+
path = @remaining_path
|
87
|
+
captures = @captures.clear
|
88
|
+
|
89
|
+
matched = case matcher
|
90
|
+
when Array
|
91
|
+
_match_array(matcher)
|
92
|
+
when Hash
|
93
|
+
_match_hash(matcher)
|
94
|
+
when Symbol
|
95
|
+
_match_symbol(matcher)
|
96
|
+
when Proc
|
97
|
+
matcher.call
|
98
|
+
else
|
99
|
+
unsupported_matcher(matcher)
|
100
|
+
end
|
101
|
+
|
102
|
+
if matched
|
103
|
+
block_result(yield(*captures))
|
104
|
+
throw :halt, response.finish
|
105
|
+
else
|
106
|
+
@remaining_path = path
|
107
|
+
false
|
108
|
+
end
|
69
109
|
end
|
70
110
|
when 0
|
71
111
|
always(&block)
|
@@ -111,22 +151,60 @@ class Roda
|
|
111
151
|
always{yield rp[1, len]}
|
112
152
|
end
|
113
153
|
elsif matcher == Integer
|
114
|
-
if matchdata =
|
154
|
+
if matchdata = /\A\/(\d+)\z/.match(@remaining_path)
|
115
155
|
@remaining_path = ''
|
116
156
|
always{yield(matchdata[1].to_i)}
|
117
157
|
end
|
118
158
|
else
|
119
|
-
|
159
|
+
path = @remaining_path
|
160
|
+
captures = @captures.clear
|
161
|
+
meth = :"_match_class_#{matcher}"
|
162
|
+
if respond_to?(meth, true)
|
163
|
+
# Allow calling private methods, as match methods are generally private
|
164
|
+
if send(meth, &block) && @remaining_path.empty?
|
165
|
+
block_result(yield(*captures))
|
166
|
+
throw :halt, response.finish
|
167
|
+
else
|
168
|
+
@remaining_path = path
|
169
|
+
false
|
170
|
+
end
|
171
|
+
else
|
172
|
+
unsupported_matcher(matcher)
|
173
|
+
end
|
120
174
|
end
|
121
175
|
when Regexp
|
122
|
-
if (matchdata =
|
176
|
+
if (matchdata = self.class.cached_matcher(matcher){matcher}.match(@remaining_path)) && matchdata.post_match.empty?
|
123
177
|
@remaining_path = ''
|
124
178
|
always{yield(*matchdata.captures)}
|
125
179
|
end
|
126
180
|
when true
|
127
181
|
always(&block) if @remaining_path.empty?
|
182
|
+
when false, nil
|
183
|
+
# nothing
|
128
184
|
else
|
129
|
-
|
185
|
+
path = @remaining_path
|
186
|
+
captures = @captures.clear
|
187
|
+
|
188
|
+
matched = case matcher
|
189
|
+
when Array
|
190
|
+
_match_array(matcher)
|
191
|
+
when Hash
|
192
|
+
_match_hash(matcher)
|
193
|
+
when Symbol
|
194
|
+
_match_symbol(matcher)
|
195
|
+
when Proc
|
196
|
+
matcher.call
|
197
|
+
else
|
198
|
+
unsupported_matcher(matcher)
|
199
|
+
end
|
200
|
+
|
201
|
+
if matched && @remaining_path.empty?
|
202
|
+
block_result(yield(*captures))
|
203
|
+
throw :halt, response.finish
|
204
|
+
else
|
205
|
+
@remaining_path = path
|
206
|
+
false
|
207
|
+
end
|
130
208
|
end
|
131
209
|
end
|
132
210
|
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
class Roda
|
5
|
+
module RodaPlugins
|
6
|
+
# The capture_erb plugin allows you to capture the content of a block
|
7
|
+
# in an ERB template, and return it as a value, instead of
|
8
|
+
# injecting the template block into the template output.
|
9
|
+
#
|
10
|
+
# <% value = capture_erb do %>
|
11
|
+
# Some content here.
|
12
|
+
# <% end %>
|
13
|
+
#
|
14
|
+
# +capture_erb+ can be used inside other methods that are called
|
15
|
+
# inside templates. It can be combined with the inject_erb plugin
|
16
|
+
# to wrap template blocks with arbitrary output and then inject the
|
17
|
+
# wrapped output into the template.
|
18
|
+
module CaptureERB
|
19
|
+
def self.load_dependencies(app)
|
20
|
+
app.plugin :render
|
21
|
+
end
|
22
|
+
|
23
|
+
module InstanceMethods
|
24
|
+
# Temporarily replace the ERB output buffer
|
25
|
+
# with an empty string, and then yield to the block.
|
26
|
+
# Return the value of the block, converted to a string.
|
27
|
+
# Restore the previous ERB output buffer before returning.
|
28
|
+
def capture_erb
|
29
|
+
outvar = render_opts[:template_opts][:outvar]
|
30
|
+
buf_was = instance_variable_get(outvar)
|
31
|
+
instance_variable_set(outvar, String.new)
|
32
|
+
yield.to_s
|
33
|
+
ensure
|
34
|
+
instance_variable_set(outvar, buf_was) if outvar && buf_was
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
register_plugin(:capture_erb, CaptureERB)
|
40
|
+
end
|
41
|
+
end
|
@@ -55,10 +55,10 @@ class Roda
|
|
55
55
|
#
|
56
56
|
# plugin :content_for, append: false
|
57
57
|
module ContentFor
|
58
|
-
# Depend on the
|
59
|
-
#
|
58
|
+
# Depend on the capture_erb plugin, since it uses capture_erb
|
59
|
+
# to capture the content.
|
60
60
|
def self.load_dependencies(app, _opts = OPTS)
|
61
|
-
app.plugin :
|
61
|
+
app.plugin :capture_erb
|
62
62
|
end
|
63
63
|
|
64
64
|
# Configure whether to append or overwrite if content_for
|
@@ -72,18 +72,12 @@ class Roda
|
|
72
72
|
# under the given key. If called without a block, retrieve
|
73
73
|
# stored content with the given key, or return nil if there
|
74
74
|
# is no content stored with that key.
|
75
|
-
def content_for(key, value=nil)
|
75
|
+
def content_for(key, value=nil, &block)
|
76
76
|
append = opts[:append_content_for]
|
77
77
|
|
78
|
-
if
|
79
|
-
if
|
80
|
-
|
81
|
-
buf_was = instance_variable_get(outvar)
|
82
|
-
|
83
|
-
# Use temporary output buffer for ERB-based rendering systems
|
84
|
-
instance_variable_set(outvar, String.new)
|
85
|
-
value = yield.to_s
|
86
|
-
instance_variable_set(outvar, buf_was)
|
78
|
+
if block || value
|
79
|
+
if block
|
80
|
+
value = capture_erb(&block)
|
87
81
|
end
|
88
82
|
|
89
83
|
@_content_for ||= {}
|
@@ -278,7 +278,7 @@ class Roda
|
|
278
278
|
Policy.new
|
279
279
|
end
|
280
280
|
|
281
|
-
yield policy if
|
281
|
+
yield policy if defined?(yield)
|
282
282
|
policy.freeze
|
283
283
|
end
|
284
284
|
|
@@ -287,7 +287,7 @@ class Roda
|
|
287
287
|
# current content security policy.
|
288
288
|
def content_security_policy
|
289
289
|
policy = @_response.content_security_policy
|
290
|
-
yield policy if
|
290
|
+
yield policy if defined?(yield)
|
291
291
|
policy
|
292
292
|
end
|
293
293
|
end
|
@@ -394,20 +394,32 @@ class Roda
|
|
394
394
|
dsl
|
395
395
|
end
|
396
396
|
|
397
|
-
# Add branch handler for the given namespace and segment.
|
397
|
+
# Add branch handler for the given namespace and segment. If called without
|
398
|
+
# a block, removes the existing branch handler if it exists.
|
398
399
|
def hash_branch(namespace='', segment, &block)
|
399
400
|
segment = "/#{segment}"
|
400
401
|
routes = opts[:hash_branches][namespace] ||= {}
|
401
|
-
|
402
|
+
if block
|
403
|
+
routes[segment] = define_roda_method(routes[segment] || "hash_branch_#{namespace}_#{segment}", 1, &convert_route_block(block))
|
404
|
+
elsif meth = routes[segment]
|
405
|
+
routes.delete(segment)
|
406
|
+
remove_method(meth)
|
407
|
+
end
|
402
408
|
end
|
403
409
|
|
404
410
|
# Add path handler for the given namespace and path. When the
|
405
411
|
# r.hash_paths method is called, checks the matching namespace
|
406
412
|
# for the full remaining path, and dispatch to that block if
|
407
|
-
# there is one.
|
413
|
+
# there is one. If called without a block, removes the existing
|
414
|
+
# path handler if it exists.
|
408
415
|
def hash_path(namespace='', path, &block)
|
409
416
|
routes = opts[:hash_paths][namespace] ||= {}
|
410
|
-
|
417
|
+
if block
|
418
|
+
routes[path] = define_roda_method(routes[path] || "hash_path_#{namespace}_#{path}", 1, &convert_route_block(block))
|
419
|
+
elsif meth = routes[path]
|
420
|
+
routes.delete(path)
|
421
|
+
remove_method(meth)
|
422
|
+
end
|
411
423
|
end
|
412
424
|
end
|
413
425
|
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
class Roda
|
5
|
+
module RodaPlugins
|
6
|
+
# The inject_erb plugin allows you to inject content directly
|
7
|
+
# into the template output:
|
8
|
+
#
|
9
|
+
# <% inject_erb("Some HTML Here") %>
|
10
|
+
#
|
11
|
+
# This will inject <tt>Some HTML Here</tt> into the template output,
|
12
|
+
# even though the tag being used is <tt><%</tt> and not <tt><%=</tt>.
|
13
|
+
#
|
14
|
+
# This method can be used inside methods, such as to wrap calls to
|
15
|
+
# methods that accept template blocks, to inject code before and after
|
16
|
+
# the template blocks.
|
17
|
+
module InjectERB
|
18
|
+
def self.load_dependencies(app)
|
19
|
+
app.plugin :render
|
20
|
+
end
|
21
|
+
|
22
|
+
module InstanceMethods
|
23
|
+
# Inject into the template output for the template currently being
|
24
|
+
# rendered.
|
25
|
+
def inject_erb(value)
|
26
|
+
instance_variable_get(render_opts[:template_opts][:outvar]) << value.to_s
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
register_plugin(:inject_erb, InjectERB)
|
32
|
+
end
|
33
|
+
end
|
@@ -72,7 +72,7 @@ class Roda
|
|
72
72
|
end
|
73
73
|
|
74
74
|
if mod
|
75
|
-
raise RodaError, "can't provide both argument and block to response_module" if
|
75
|
+
raise RodaError, "can't provide both argument and block to response_module" if defined?(yield)
|
76
76
|
klass.send(:include, mod)
|
77
77
|
else
|
78
78
|
if instance_variable_defined?(iv)
|
@@ -3,15 +3,10 @@
|
|
3
3
|
#
|
4
4
|
class Roda
|
5
5
|
module RodaPlugins
|
6
|
-
# The multi_route plugin
|
7
|
-
#
|
8
|
-
#
|
9
|
-
# and
|
10
|
-
# the named route will be returned.
|
11
|
-
#
|
12
|
-
# In addition, this plugin adds the +r.multi_route+ method, which will check
|
13
|
-
# if the first segment in the path matches a named route, and dispatch
|
14
|
-
# to that named route.
|
6
|
+
# The multi_route plugin builds on the named_routes plugin and allows for
|
7
|
+
# dispatching to multiple named routes # by calling the +r.multi_route+ method,
|
8
|
+
# which will check # if the first segment in the path matches a named route,
|
9
|
+
# and dispatch to that named route.
|
15
10
|
#
|
16
11
|
# The hash_routes plugin offers a +r.hash_routes+ method that is similar to
|
17
12
|
# and performs better than the +r.multi_route+ method, and it is recommended
|
@@ -35,23 +30,14 @@ class Roda
|
|
35
30
|
#
|
36
31
|
# route do |r|
|
37
32
|
# r.multi_route
|
38
|
-
#
|
39
|
-
# # or
|
40
|
-
#
|
41
|
-
# r.on "foo" do
|
42
|
-
# r.route 'foo'
|
43
|
-
# end
|
44
|
-
#
|
45
|
-
# r.on "bar" do
|
46
|
-
# r.route 'bar'
|
47
|
-
# end
|
48
33
|
# end
|
49
34
|
#
|
50
|
-
# Note that
|
51
|
-
#
|
35
|
+
# Note that only named routes with string names will be dispatched to by the
|
36
|
+
# +r.multi_route+ method. Named routes with other names can be dispatched to
|
37
|
+
# using the named_routes plugin API, but will not be automatically dispatched
|
38
|
+
# to by +r.multi_route+.
|
52
39
|
#
|
53
|
-
#
|
54
|
-
# named routes. Also, you can provide a block to +r.multi_route+ that is
|
40
|
+
# You can provide a block to +r.multi_route+ that is
|
55
41
|
# called if the route matches but the named route did not handle the
|
56
42
|
# request:
|
57
43
|
#
|
@@ -62,20 +48,10 @@ class Roda
|
|
62
48
|
# If a block is not provided to multi_route, the return value of the named
|
63
49
|
# route block will be used.
|
64
50
|
#
|
65
|
-
# == Routing Files
|
66
|
-
#
|
67
|
-
# The convention when using the multi_route plugin is to have a single
|
68
|
-
# named route per file, and these routing files should be stored in
|
69
|
-
# a routes subdirectory in your application. So for the above example, you
|
70
|
-
# would use the following files:
|
71
|
-
#
|
72
|
-
# routes/bar.rb
|
73
|
-
# routes/foo.rb
|
74
|
-
#
|
75
51
|
# == Namespace Support
|
76
52
|
#
|
77
53
|
# The multi_route plugin also has support for namespaces, allowing you to
|
78
|
-
# use r.multi_route at multiple levels in your routing tree. Example:
|
54
|
+
# use +r.multi_route+ at multiple levels in your routing tree. Example:
|
79
55
|
#
|
80
56
|
# route('foo') do |r|
|
81
57
|
# r.multi_route('foo')
|
@@ -103,81 +79,38 @@ class Roda
|
|
103
79
|
#
|
104
80
|
# route do |r|
|
105
81
|
# r.multi_route
|
106
|
-
#
|
107
|
-
# # or
|
108
|
-
#
|
109
|
-
# r.on "foo" do
|
110
|
-
# r.on("baz"){r.route("baz", "foo")}
|
111
|
-
# r.on("quux"){r.route("quux", "foo")}
|
112
|
-
# end
|
113
|
-
#
|
114
|
-
# r.on "bar" do
|
115
|
-
# r.on("baz"){r.route("baz", "bar")}
|
116
|
-
# r.on("quux"){r.route("quux", "bar")}
|
117
|
-
# end
|
118
82
|
# end
|
119
|
-
#
|
120
|
-
# === Routing Files
|
121
|
-
#
|
122
|
-
# The convention when using namespaces with the multi_route plugin is to
|
123
|
-
# store the routing files in subdirectories per namespace. So for the
|
124
|
-
# above example, you would have the following routing files:
|
125
|
-
#
|
126
|
-
# routes/bar.rb
|
127
|
-
# routes/bar/baz.rb
|
128
|
-
# routes/bar/quux.rb
|
129
|
-
# routes/foo.rb
|
130
|
-
# routes/foo/baz.rb
|
131
|
-
# routes/foo/quux.rb
|
132
83
|
module MultiRoute
|
84
|
+
def self.load_dependencies(app)
|
85
|
+
app.plugin :named_routes
|
86
|
+
end
|
87
|
+
|
133
88
|
# Initialize storage for the named routes.
|
134
89
|
def self.configure(app)
|
135
|
-
app.opts[:namespaced_routes] ||= {}
|
136
90
|
app::RodaRequest.instance_variable_set(:@namespaced_route_regexps, {})
|
137
91
|
end
|
138
92
|
|
139
93
|
module ClassMethods
|
140
|
-
# Freeze the
|
94
|
+
# Freeze the multi_route regexp matchers so that there can be no thread safety issues at runtime.
|
141
95
|
def freeze
|
142
|
-
|
143
|
-
|
96
|
+
super
|
97
|
+
opts[:namespaced_routes].each_key do |k|
|
144
98
|
self::RodaRequest.named_route_regexp(k)
|
145
99
|
end
|
146
100
|
self::RodaRequest.instance_variable_get(:@namespaced_route_regexps).freeze
|
147
|
-
super
|
148
101
|
end
|
149
102
|
|
150
103
|
# Copy the named routes into the subclass when inheriting.
|
151
104
|
def inherited(subclass)
|
152
105
|
super
|
153
|
-
nsr = subclass.opts[:namespaced_routes]
|
154
|
-
opts[:namespaced_routes].each{|k, v| nsr[k] = v.dup}
|
155
106
|
subclass::RodaRequest.instance_variable_set(:@namespaced_route_regexps, {})
|
156
107
|
end
|
157
108
|
|
158
|
-
#
|
159
|
-
def named_routes(namespace=nil)
|
160
|
-
unless routes = opts[:namespaced_routes][namespace]
|
161
|
-
raise RodaError, "unsupported multi_route namespace used: #{namespace.inspect}"
|
162
|
-
end
|
163
|
-
routes.keys
|
164
|
-
end
|
165
|
-
|
166
|
-
# Return the named route with the given name.
|
167
|
-
def named_route(name, namespace=nil)
|
168
|
-
opts[:namespaced_routes][namespace][name]
|
169
|
-
end
|
170
|
-
|
171
|
-
# If the given route has a name, treat it as a named route and
|
172
|
-
# store the route block. Otherwise, this is the main route, so
|
173
|
-
# call super.
|
109
|
+
# Clear the multi_route regexp matcher for the namespace.
|
174
110
|
def route(name=nil, namespace=nil, &block)
|
111
|
+
super
|
175
112
|
if name
|
176
|
-
routes = opts[:namespaced_routes][namespace] ||= {}
|
177
|
-
routes[name] = define_roda_method(routes[name] || "multi_route_#{namespace}_#{name}", 1, &convert_route_block(block))
|
178
113
|
self::RodaRequest.clear_named_route_regexp!(namespace)
|
179
|
-
else
|
180
|
-
super(&block)
|
181
114
|
end
|
182
115
|
end
|
183
116
|
end
|
@@ -206,19 +139,14 @@ class Roda
|
|
206
139
|
# is given, yield to the block.
|
207
140
|
def multi_route(namespace=nil)
|
208
141
|
on self.class.named_route_regexp(namespace) do |section|
|
209
|
-
|
210
|
-
if
|
142
|
+
res = route(section, namespace)
|
143
|
+
if defined?(yield)
|
211
144
|
yield
|
212
145
|
else
|
213
|
-
|
146
|
+
res
|
214
147
|
end
|
215
148
|
end
|
216
149
|
end
|
217
|
-
|
218
|
-
# Dispatch to the named route with the given name.
|
219
|
-
def route(name, namespace=nil)
|
220
|
-
scope.send(roda_class.named_route(name, namespace), self)
|
221
|
-
end
|
222
150
|
end
|
223
151
|
end
|
224
152
|
|
@@ -0,0 +1,160 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
class Roda
|
5
|
+
module RodaPlugins
|
6
|
+
# The named_routes plugin allows for multiple named routes, which the
|
7
|
+
# main route block can dispatch to by name at any point by calling +r.route+.
|
8
|
+
# If the named route doesn't handle the request, execution will continue,
|
9
|
+
# and if the named route does handle the request, the response returned by
|
10
|
+
# the named route will be returned.
|
11
|
+
#
|
12
|
+
# Example:
|
13
|
+
#
|
14
|
+
# plugin :multi_route
|
15
|
+
#
|
16
|
+
# route('foo') do |r|
|
17
|
+
# r.is 'bar' do
|
18
|
+
# '/foo/bar'
|
19
|
+
# end
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# route('bar') do |r|
|
23
|
+
# r.is 'foo' do
|
24
|
+
# '/bar/foo'
|
25
|
+
# end
|
26
|
+
# end
|
27
|
+
#
|
28
|
+
# route do |r|
|
29
|
+
# r.on "foo" do
|
30
|
+
# r.route 'foo'
|
31
|
+
# end
|
32
|
+
#
|
33
|
+
# r.on "bar" do
|
34
|
+
# r.route 'bar'
|
35
|
+
# end
|
36
|
+
# end
|
37
|
+
#
|
38
|
+
# Note that in multi-threaded code, you should not attempt to add a
|
39
|
+
# named route after accepting requests.
|
40
|
+
#
|
41
|
+
# == Routing Files
|
42
|
+
#
|
43
|
+
# The convention when using the named_routes plugin is to have a single
|
44
|
+
# named route per file, and these routing files should be stored in
|
45
|
+
# a routes subdirectory in your application. So for the above example, you
|
46
|
+
# would use the following files:
|
47
|
+
#
|
48
|
+
# routes/bar.rb
|
49
|
+
# routes/foo.rb
|
50
|
+
#
|
51
|
+
# == Namespace Support
|
52
|
+
#
|
53
|
+
# The named_routes plugin also has support for namespaces, allowing you to
|
54
|
+
# use +r.route+ at multiple levels in your routing tree. Example:
|
55
|
+
#
|
56
|
+
# route('foo') do |r|
|
57
|
+
# r.on("baz"){r.route("baz", "foo")}
|
58
|
+
# r.on("quux"){r.route("quux", "foo")}
|
59
|
+
# end
|
60
|
+
#
|
61
|
+
# route('bar') do |r|
|
62
|
+
# r.on("baz"){r.route("baz", "bar")}
|
63
|
+
# r.on("quux"){r.route("quux", "bar")}
|
64
|
+
# end
|
65
|
+
#
|
66
|
+
# route('baz', 'foo') do |r|
|
67
|
+
# # handles /foo/baz prefix
|
68
|
+
# end
|
69
|
+
#
|
70
|
+
# route('quux', 'foo') do |r|
|
71
|
+
# # handles /foo/quux prefix
|
72
|
+
# end
|
73
|
+
#
|
74
|
+
# route('baz', 'bar') do |r|
|
75
|
+
# # handles /bar/baz prefix
|
76
|
+
# end
|
77
|
+
#
|
78
|
+
# route('quux', 'bar') do |r|
|
79
|
+
# # handles /bar/quux prefix
|
80
|
+
# end
|
81
|
+
#
|
82
|
+
# route do |r|
|
83
|
+
# r.on "foo" do
|
84
|
+
# r.route("foo")
|
85
|
+
# end
|
86
|
+
#
|
87
|
+
# r.on "bar" do
|
88
|
+
# r.route("bar")
|
89
|
+
# end
|
90
|
+
# end
|
91
|
+
#
|
92
|
+
# === Routing Files
|
93
|
+
#
|
94
|
+
# The convention when using namespaces with the multi_route plugin is to
|
95
|
+
# store the routing files in subdirectories per namespace. So for the
|
96
|
+
# above example, you would have the following routing files:
|
97
|
+
#
|
98
|
+
# routes/bar.rb
|
99
|
+
# routes/bar/baz.rb
|
100
|
+
# routes/bar/quux.rb
|
101
|
+
# routes/foo.rb
|
102
|
+
# routes/foo/baz.rb
|
103
|
+
# routes/foo/quux.rb
|
104
|
+
module NamedRoutes
|
105
|
+
# Initialize storage for the named routes.
|
106
|
+
def self.configure(app)
|
107
|
+
app.opts[:namespaced_routes] ||= {}
|
108
|
+
end
|
109
|
+
|
110
|
+
module ClassMethods
|
111
|
+
# Freeze the namespaced routes so that there can be no thread safety issues at runtime.
|
112
|
+
def freeze
|
113
|
+
opts[:namespaced_routes].freeze.each_value(&:freeze)
|
114
|
+
super
|
115
|
+
end
|
116
|
+
|
117
|
+
# Copy the named routes into the subclass when inheriting.
|
118
|
+
def inherited(subclass)
|
119
|
+
super
|
120
|
+
nsr = subclass.opts[:namespaced_routes]
|
121
|
+
opts[:namespaced_routes].each{|k, v| nsr[k] = v.dup}
|
122
|
+
end
|
123
|
+
|
124
|
+
# The names for the currently stored named routes
|
125
|
+
def named_routes(namespace=nil)
|
126
|
+
unless routes = opts[:namespaced_routes][namespace]
|
127
|
+
raise RodaError, "unsupported multi_route namespace used: #{namespace.inspect}"
|
128
|
+
end
|
129
|
+
routes.keys
|
130
|
+
end
|
131
|
+
|
132
|
+
# Return the named route with the given name.
|
133
|
+
def named_route(name, namespace=nil)
|
134
|
+
opts[:namespaced_routes][namespace][name]
|
135
|
+
end
|
136
|
+
|
137
|
+
# If the given route has a name, treat it as a named route and
|
138
|
+
# store the route block. Otherwise, this is the main route, so
|
139
|
+
# call super.
|
140
|
+
def route(name=nil, namespace=nil, &block)
|
141
|
+
if name
|
142
|
+
routes = opts[:namespaced_routes][namespace] ||= {}
|
143
|
+
routes[name] = define_roda_method(routes[name] || "multi_route_#{namespace}_#{name}", 1, &convert_route_block(block))
|
144
|
+
else
|
145
|
+
super(&block)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
module RequestMethods
|
151
|
+
# Dispatch to the named route with the given name.
|
152
|
+
def route(name, namespace=nil)
|
153
|
+
scope.send(roda_class.named_route(name, namespace), self)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
register_plugin(:named_routes, NamedRoutes)
|
159
|
+
end
|
160
|
+
end
|
data/lib/roda/plugins/render.rb
CHANGED
@@ -47,7 +47,7 @@ class Roda
|
|
47
47
|
# The following plugin options are supported:
|
48
48
|
#
|
49
49
|
# :allowed_paths :: Set the template paths to allow. Attempts to render paths outside
|
50
|
-
# of
|
50
|
+
# of these paths will raise an error. Defaults to the +:views+ directory.
|
51
51
|
# :cache :: nil/false to explicitly disable premanent template caching. By default, permanent
|
52
52
|
# template caching is disabled by default if RACK_ENV is development. When permanent
|
53
53
|
# template caching is disabled, for templates with paths in the file system, the
|
@@ -59,9 +59,9 @@ class Roda
|
|
59
59
|
# templates, defaults to 'erb'.
|
60
60
|
# :escape :: Use Erubi as the ERB template engine, and enable escaping by default,
|
61
61
|
# which makes <tt><%= %></tt> escape output and <tt><%== %></tt> not escape output.
|
62
|
-
# If given, sets the <tt
|
62
|
+
# If given, sets the <tt>escape: true</tt> option for all template engines, which
|
63
63
|
# can break some non-ERB template engines. You can use a string or array of strings
|
64
|
-
# as the value for this option to only set the <tt
|
64
|
+
# as the value for this option to only set the <tt>escape: true</tt> option for those
|
65
65
|
# specific template engines.
|
66
66
|
# :layout :: The base name of the layout file, defaults to 'layout'. This can be provided as a hash
|
67
67
|
# with the :template or :inline options.
|
@@ -130,6 +130,84 @@ class Roda
|
|
130
130
|
# only argument, you can speed things up by specifying a +:cache_key+ option in
|
131
131
|
# the hash, making sure the +:cache_key+ is unique to the template you are
|
132
132
|
# rendering.
|
133
|
+
#
|
134
|
+
# = Accepting Template Blocks in Methods
|
135
|
+
#
|
136
|
+
# If you are used to Rails, you may be surprised that this type of template code
|
137
|
+
# doesn't work in Roda:
|
138
|
+
#
|
139
|
+
# <%= some_method do %>
|
140
|
+
# Some HTML
|
141
|
+
# <% end %>
|
142
|
+
#
|
143
|
+
# The reason this doesn't work is that this is not valid ERB syntax, it is Rails syntax,
|
144
|
+
# and requires attempting to parse the <tt>some_method do</tt> Ruby code with a regular
|
145
|
+
# expression. Since Roda uses ERB syntax, it does not support this.
|
146
|
+
#
|
147
|
+
# In general, these methods are used to wrap the content of the block and
|
148
|
+
# inject the content into the output. To get similar behavior with Roda, you have
|
149
|
+
# a few different options you can use.
|
150
|
+
#
|
151
|
+
# == Directly Inject Template Output
|
152
|
+
#
|
153
|
+
# You can switch from a <tt><%=</tt> tag to using a <tt><%</tt> tag:
|
154
|
+
#
|
155
|
+
# <% some_method do %>
|
156
|
+
# Some HTML
|
157
|
+
# <% end %>
|
158
|
+
#
|
159
|
+
# While this would output <tt>Some HTML</tt> into the template, it would not be able
|
160
|
+
# to inject content before or after the block. However, you can use the inject_erb_plugin
|
161
|
+
# to handle the injection:
|
162
|
+
#
|
163
|
+
# def some_method
|
164
|
+
# inject_erb "content before block"
|
165
|
+
# yield
|
166
|
+
# inject_erb "content after block"
|
167
|
+
# end
|
168
|
+
#
|
169
|
+
# If you need to modify the captured block before injecting it, you can use the
|
170
|
+
# capture_erb plugin to capture content from the template block, and modify that content,
|
171
|
+
# then use inject_erb to inject it into the template output:
|
172
|
+
#
|
173
|
+
# def some_method(&block)
|
174
|
+
# inject_erb "content before block"
|
175
|
+
# inject_erb capture_erb(&block).upcase
|
176
|
+
# inject_erb "content after block"
|
177
|
+
# end
|
178
|
+
#
|
179
|
+
# This is the recommended approach for handling this type of method, if you want to keep
|
180
|
+
# the template block in the same template.
|
181
|
+
#
|
182
|
+
# == Separate Block Output Into Separate Template
|
183
|
+
#
|
184
|
+
# By moving the <tt>Some HTML</tt> into a separate template, you can render that
|
185
|
+
# template inside the block:
|
186
|
+
#
|
187
|
+
# <%= some_method{render('template_name')} %>
|
188
|
+
#
|
189
|
+
# It's also possible to use an inline template:
|
190
|
+
#
|
191
|
+
# <%= some_method do render(:inline=><<-END)
|
192
|
+
# Some HTML
|
193
|
+
# END
|
194
|
+
# end %>
|
195
|
+
#
|
196
|
+
# This approach is useful if it makes sense to separate the template block into its
|
197
|
+
# own template. You lose the ability to use local variable from outside the
|
198
|
+
# template block inside the template block with this approach.
|
199
|
+
#
|
200
|
+
# == Separate Header and Footer
|
201
|
+
#
|
202
|
+
# You can define two separate methods, one that outputs the content before the block,
|
203
|
+
# and one that outputs the content after the block, and use those instead of a single
|
204
|
+
# call:
|
205
|
+
#
|
206
|
+
# <%= some_method_before %>
|
207
|
+
# Some HTML
|
208
|
+
# <%= some_method_after %>
|
209
|
+
#
|
210
|
+
# This is the simplest option to setup, but it is fairly tedious to use.
|
133
211
|
module Render
|
134
212
|
# Support for using compiled methods directly requires Ruby 2.3 for the
|
135
213
|
# method binding to work, and Tilt 1.2 for Tilt::Template#compiled_method.
|
@@ -384,7 +384,7 @@ class Roda
|
|
384
384
|
|
385
385
|
# Set or retrieve the response body. When a block is given,
|
386
386
|
# evaluation is deferred until the body is needed.
|
387
|
-
def body(value = (return @body unless
|
387
|
+
def body(value = (return @body unless defined?(yield); nil), &block)
|
388
388
|
if block
|
389
389
|
@body = DelayedBody.new(&block)
|
390
390
|
else
|
@@ -6,8 +6,7 @@ class Roda
|
|
6
6
|
# The status_303 plugin sets the default redirect status to be 303
|
7
7
|
# rather than 302 when the request is not a GET and the
|
8
8
|
# redirection occurs on an HTTP 1.1 connection as per RFC 7231.
|
9
|
-
#
|
10
|
-
# practice.
|
9
|
+
# There are some frontend frameworks that require this behavior.
|
11
10
|
#
|
12
11
|
# Example:
|
13
12
|
#
|
@@ -564,7 +564,7 @@ class Roda
|
|
564
564
|
end
|
565
565
|
|
566
566
|
v = @obj[key]
|
567
|
-
v = yield if v.nil? &&
|
567
|
+
v = yield if v.nil? && defined?(yield)
|
568
568
|
|
569
569
|
begin
|
570
570
|
sub = self.class.nest(v, Array(@nesting) + [key])
|
@@ -580,7 +580,7 @@ class Roda
|
|
580
580
|
# Return the nested value for key. If there is no nested_value for +key+,
|
581
581
|
# calls the block to return the value, or returns nil if there is no block given.
|
582
582
|
def fetch(key)
|
583
|
-
send(:[], key){return(yield if
|
583
|
+
send(:[], key){return(yield if defined?(yield))}
|
584
584
|
end
|
585
585
|
|
586
586
|
# Captures conversions inside the given block, and returns a hash of all conversions,
|
data/lib/roda/request.rb
CHANGED
@@ -517,10 +517,10 @@ class Roda
|
|
517
517
|
# SCRIPT_NAME to include the matched path, removes the matched
|
518
518
|
# path from PATH_INFO, and updates captures with any regex captures.
|
519
519
|
def consume(pattern)
|
520
|
-
if matchdata =
|
520
|
+
if matchdata = pattern.match(@remaining_path)
|
521
521
|
@remaining_path = matchdata.post_match
|
522
522
|
captures = matchdata.captures
|
523
|
-
captures = yield(*captures) if
|
523
|
+
captures = yield(*captures) if defined?(yield)
|
524
524
|
@captures.concat(captures)
|
525
525
|
end
|
526
526
|
end
|
data/lib/roda/version.rb
CHANGED
data/lib/roda.rb
CHANGED
@@ -213,7 +213,7 @@ class Roda
|
|
213
213
|
plugin :direct_call
|
214
214
|
end
|
215
215
|
|
216
|
-
if ([:on, :is, :_verb, :_match_class_String, :_match_class_Integer, :_match_string, :_match_regexp, :empty_path
|
216
|
+
if ([:on, :is, :_verb, :_match_class_String, :_match_class_Integer, :_match_string, :_match_regexp, :empty_path?, :if_match, :match, :_match_class]).all?{|m| self::RodaRequest.instance_method(m).owner == RequestMethods}
|
217
217
|
plugin :_optimized_matching
|
218
218
|
end
|
219
219
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: roda
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.
|
4
|
+
version: 3.50.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jeremy Evans
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-11-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rack
|
@@ -217,7 +217,11 @@ extra_rdoc_files:
|
|
217
217
|
- doc/release_notes/3.44.0.txt
|
218
218
|
- doc/release_notes/3.45.0.txt
|
219
219
|
- doc/release_notes/3.46.0.txt
|
220
|
+
- doc/release_notes/3.47.0.txt
|
221
|
+
- doc/release_notes/3.48.0.txt
|
222
|
+
- doc/release_notes/3.49.0.txt
|
220
223
|
- doc/release_notes/3.5.0.txt
|
224
|
+
- doc/release_notes/3.50.0.txt
|
221
225
|
- doc/release_notes/3.6.0.txt
|
222
226
|
- doc/release_notes/3.7.0.txt
|
223
227
|
- doc/release_notes/3.8.0.txt
|
@@ -270,7 +274,11 @@ files:
|
|
270
274
|
- doc/release_notes/3.44.0.txt
|
271
275
|
- doc/release_notes/3.45.0.txt
|
272
276
|
- doc/release_notes/3.46.0.txt
|
277
|
+
- doc/release_notes/3.47.0.txt
|
278
|
+
- doc/release_notes/3.48.0.txt
|
279
|
+
- doc/release_notes/3.49.0.txt
|
273
280
|
- doc/release_notes/3.5.0.txt
|
281
|
+
- doc/release_notes/3.50.0.txt
|
274
282
|
- doc/release_notes/3.6.0.txt
|
275
283
|
- doc/release_notes/3.7.0.txt
|
276
284
|
- doc/release_notes/3.8.0.txt
|
@@ -288,6 +296,7 @@ files:
|
|
288
296
|
- lib/roda/plugins/backtracking_array.rb
|
289
297
|
- lib/roda/plugins/branch_locals.rb
|
290
298
|
- lib/roda/plugins/caching.rb
|
299
|
+
- lib/roda/plugins/capture_erb.rb
|
291
300
|
- lib/roda/plugins/chunked.rb
|
292
301
|
- lib/roda/plugins/class_level_routing.rb
|
293
302
|
- lib/roda/plugins/class_matchers.rb
|
@@ -323,6 +332,7 @@ files:
|
|
323
332
|
- lib/roda/plugins/hooks.rb
|
324
333
|
- lib/roda/plugins/host_authorization.rb
|
325
334
|
- lib/roda/plugins/indifferent_params.rb
|
335
|
+
- lib/roda/plugins/inject_erb.rb
|
326
336
|
- lib/roda/plugins/json.rb
|
327
337
|
- lib/roda/plugins/json_parser.rb
|
328
338
|
- lib/roda/plugins/mail_processor.rb
|
@@ -337,6 +347,7 @@ files:
|
|
337
347
|
- lib/roda/plugins/multi_run.rb
|
338
348
|
- lib/roda/plugins/multi_view.rb
|
339
349
|
- lib/roda/plugins/multibyte_string_matcher.rb
|
350
|
+
- lib/roda/plugins/named_routes.rb
|
340
351
|
- lib/roda/plugins/named_templates.rb
|
341
352
|
- lib/roda/plugins/not_allowed.rb
|
342
353
|
- lib/roda/plugins/not_found.rb
|