roda 3.107.0 → 3.109.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/roda/opts.rb +22 -0
- data/lib/roda/plugins/Integer_matcher_max.rb +1 -1
- data/lib/roda/plugins/_add_script_name.rb +14 -0
- data/lib/roda/plugins/_base64.rb +2 -2
- data/lib/roda/plugins/_json.rb +22 -0
- data/lib/roda/plugins/_optimized_matching.rb +1 -0
- data/lib/roda/plugins/_symbol_class_matchers.rb +6 -6
- data/lib/roda/plugins/additional_render_engines.rb +5 -1
- data/lib/roda/plugins/additional_view_directories.rb +5 -1
- data/lib/roda/plugins/all_verbs.rb +2 -2
- data/lib/roda/plugins/assets.rb +11 -14
- data/lib/roda/plugins/autoload_hash_branches.rb +9 -3
- data/lib/roda/plugins/autoload_named_routes.rb +9 -3
- data/lib/roda/plugins/bearer_token.rb +2 -2
- data/lib/roda/plugins/break.rb +9 -1
- data/lib/roda/plugins/caching.rb +12 -12
- data/lib/roda/plugins/capture_erb.rb +5 -1
- data/lib/roda/plugins/chunked.rb +11 -5
- data/lib/roda/plugins/class_level_routing.rb +5 -3
- data/lib/roda/plugins/class_matchers.rb +3 -1
- data/lib/roda/plugins/common_logger.rb +11 -7
- data/lib/roda/plugins/conditional_sessions.rb +1 -1
- data/lib/roda/plugins/content_for.rb +5 -1
- data/lib/roda/plugins/content_security_policy.rb +6 -2
- data/lib/roda/plugins/cookie_flags.rb +8 -4
- data/lib/roda/plugins/cookies.rb +7 -3
- data/lib/roda/plugins/custom_block_results.rb +5 -3
- data/lib/roda/plugins/custom_matchers.rb +4 -2
- data/lib/roda/plugins/default_headers.rb +2 -5
- data/lib/roda/plugins/default_status.rb +1 -0
- data/lib/roda/plugins/each_part.rb +23 -27
- data/lib/roda/plugins/environments.rb +3 -7
- data/lib/roda/plugins/error_email.rb +11 -7
- data/lib/roda/plugins/error_handler.rb +5 -3
- data/lib/roda/plugins/error_mail.rb +6 -2
- data/lib/roda/plugins/exception_page.rb +2 -2
- data/lib/roda/plugins/h.rb +4 -4
- data/lib/roda/plugins/hash_branch_view_subdir.rb +6 -4
- data/lib/roda/plugins/hash_branches.rb +15 -9
- data/lib/roda/plugins/hash_paths.rb +11 -7
- data/lib/roda/plugins/hash_public.rb +13 -9
- data/lib/roda/plugins/hash_public_cache.rb +9 -7
- data/lib/roda/plugins/hash_routes.rb +5 -3
- data/lib/roda/plugins/heartbeat.rb +6 -2
- data/lib/roda/plugins/hmac_paths.rb +36 -32
- data/lib/roda/plugins/hooks.rb +13 -10
- data/lib/roda/plugins/host_authorization.rb +7 -2
- data/lib/roda/plugins/host_routing.rb +36 -6
- data/lib/roda/plugins/invalid_request_body.rb +22 -4
- data/lib/roda/plugins/ip_from_header.rb +5 -1
- data/lib/roda/plugins/json.rb +10 -8
- data/lib/roda/plugins/json_parser.rb +43 -11
- data/lib/roda/plugins/mail_processor.rb +11 -8
- data/lib/roda/plugins/mailer.rb +9 -7
- data/lib/roda/plugins/match_affix.rb +6 -1
- data/lib/roda/plugins/match_hook_args.rb +7 -5
- data/lib/roda/plugins/middleware.rb +13 -7
- data/lib/roda/plugins/module_include.rb +1 -1
- data/lib/roda/plugins/multi_public.rb +6 -2
- data/lib/roda/plugins/multi_route.rb +1 -1
- data/lib/roda/plugins/multi_run.rb +13 -17
- data/lib/roda/plugins/named_routes.rb +8 -6
- data/lib/roda/plugins/named_templates.rb +5 -3
- data/lib/roda/plugins/optimized_segment_matchers.rb +13 -6
- data/lib/roda/plugins/optimized_string_matchers.rb +6 -3
- data/lib/roda/plugins/params_capturing.rb +16 -7
- data/lib/roda/plugins/params_capturing_restore.rb +72 -0
- data/lib/roda/plugins/part.rb +4 -4
- data/lib/roda/plugins/pass.rb +4 -1
- data/lib/roda/plugins/path.rb +22 -20
- data/lib/roda/plugins/path_matchers.rb +5 -1
- data/lib/roda/plugins/path_rewriter.rb +8 -5
- data/lib/roda/plugins/permissions_policy.rb +7 -3
- data/lib/roda/plugins/public.rb +15 -14
- data/lib/roda/plugins/recheck_precompiled_assets.rb +1 -1
- data/lib/roda/plugins/redirect_http_to_https.rb +6 -3
- data/lib/roda/plugins/render.rb +58 -61
- data/lib/roda/plugins/render_coverage.rb +12 -8
- data/lib/roda/plugins/render_each.rb +2 -2
- data/lib/roda/plugins/render_locals.rb +12 -6
- data/lib/roda/plugins/request_aref.rb +1 -1
- data/lib/roda/plugins/response_content_type.rb +7 -3
- data/lib/roda/plugins/route_csrf.rb +42 -5
- data/lib/roda/plugins/run_append_slash.rb +12 -4
- data/lib/roda/plugins/sec_fetch_site_csrf.rb +11 -4
- data/lib/roda/plugins/sessions.rb +17 -13
- data/lib/roda/plugins/shape_friendly.rb +7 -5
- data/lib/roda/plugins/sinatra_helpers.rb +7 -3
- data/lib/roda/plugins/static.rb +3 -0
- data/lib/roda/plugins/status_handler.rb +5 -3
- data/lib/roda/plugins/strip_path_prefix.rb +4 -2
- data/lib/roda/plugins/symbol_matchers.rb +3 -1
- data/lib/roda/plugins/symbol_views.rb +1 -1
- data/lib/roda/plugins/timestamp_public.rb +8 -4
- data/lib/roda/plugins/type_routing.rb +10 -6
- data/lib/roda/plugins/typecast_params.rb +2 -2
- data/lib/roda/plugins/typecast_params_sized_integers.rb +1 -1
- data/lib/roda/plugins/url_escape.rb +2 -14
- data/lib/roda/plugins/view_options.rb +3 -5
- data/lib/roda/plugins.rb +14 -4
- data/lib/roda/request.rb +25 -9
- data/lib/roda/version.rb +1 -1
- data/lib/roda.rb +32 -21
- metadata +7 -3
|
@@ -285,11 +285,14 @@ class Roda
|
|
|
285
285
|
class UnhandledMail < StandardError; end
|
|
286
286
|
|
|
287
287
|
module ClassMethods
|
|
288
|
+
RodaPlugins.opt_attr_reader(self, :mail_processor_string_routes)
|
|
289
|
+
RodaPlugins.opt_attr_reader(self, :mail_processor_regexp_routes)
|
|
290
|
+
|
|
288
291
|
# Freeze the rcpt routes if they are present.
|
|
289
292
|
def freeze
|
|
290
|
-
if string_routes =
|
|
293
|
+
if string_routes = mail_processor_string_routes.freeze
|
|
291
294
|
string_routes.freeze
|
|
292
|
-
|
|
295
|
+
mail_processor_regexp_routes.freeze
|
|
293
296
|
end
|
|
294
297
|
super
|
|
295
298
|
end
|
|
@@ -337,12 +340,12 @@ class Roda
|
|
|
337
340
|
unless string_meth
|
|
338
341
|
string_meth = define_roda_method("mail_processor_string_route_#{address}", 1, &convert_route_block(block))
|
|
339
342
|
end
|
|
340
|
-
|
|
343
|
+
mail_processor_string_routes[address] = string_meth
|
|
341
344
|
when Regexp
|
|
342
345
|
unless regexp_meth
|
|
343
346
|
regexp_meth = define_roda_method("mail_processor_regexp_route_#{address}", :any, &convert_route_block(block))
|
|
344
347
|
end
|
|
345
|
-
|
|
348
|
+
mail_processor_regexp_routes[address] = regexp_meth
|
|
346
349
|
else
|
|
347
350
|
raise RodaError, "invalid address format passed to rcpt, should be Array or String"
|
|
348
351
|
end
|
|
@@ -382,7 +385,7 @@ class Roda
|
|
|
382
385
|
# routes defined via the class-level +rcpt+ method, and then the
|
|
383
386
|
# normal routing tree passed in as the block.
|
|
384
387
|
def process_mail(&block)
|
|
385
|
-
if string_routes =
|
|
388
|
+
if string_routes = self.class.mail_processor_string_routes
|
|
386
389
|
addresses = mail_recipients
|
|
387
390
|
|
|
388
391
|
addresses.each do |address|
|
|
@@ -392,7 +395,7 @@ class Roda
|
|
|
392
395
|
end
|
|
393
396
|
end
|
|
394
397
|
|
|
395
|
-
|
|
398
|
+
self.class.mail_processor_regexp_routes.each do |regexp, meth|
|
|
396
399
|
addresses.each do |address|
|
|
397
400
|
if md = regexp.match(address)
|
|
398
401
|
_roda_handle_route{send(meth, @_request, *md.captures)}
|
|
@@ -510,11 +513,11 @@ class Roda
|
|
|
510
513
|
a1.casecmp?(a2)
|
|
511
514
|
end
|
|
512
515
|
else
|
|
513
|
-
# :
|
|
516
|
+
# simplecov:disable
|
|
514
517
|
def address_match?(a1, a2)
|
|
515
518
|
a1.downcase == a2.downcase
|
|
516
519
|
end
|
|
517
|
-
# :
|
|
520
|
+
# simplecov:enable
|
|
518
521
|
end
|
|
519
522
|
|
|
520
523
|
# Match if any of the given addresses match the given val, which
|
data/lib/roda/plugins/mailer.rb
CHANGED
|
@@ -136,10 +136,12 @@ class Roda
|
|
|
136
136
|
# Set the options for the mailer. Options:
|
|
137
137
|
# :content_type :: The default content type for emails (default: text/plain)
|
|
138
138
|
def self.configure(app, opts=OPTS)
|
|
139
|
-
app.opts[:mailer] = (app.
|
|
139
|
+
app.opts[:mailer] = (app.mailer_opts||OPTS).merge(opts).freeze
|
|
140
140
|
end
|
|
141
141
|
|
|
142
142
|
module ClassMethods
|
|
143
|
+
RodaPlugins.opt_attr_reader(self, :mailer, name: :mailer_opts)
|
|
144
|
+
|
|
143
145
|
# Return a Mail::Message instance for the email for the given request path
|
|
144
146
|
# and arguments. Any arguments given are yielded to the appropriate +r.mail+
|
|
145
147
|
# block after any usual match block arguments. You can further manipulate the
|
|
@@ -153,9 +155,9 @@ class Roda
|
|
|
153
155
|
mail
|
|
154
156
|
end
|
|
155
157
|
end
|
|
156
|
-
# :
|
|
158
|
+
# simplecov:disable
|
|
157
159
|
ruby2_keywords(:mail) if respond_to?(:ruby2_keywords, true)
|
|
158
|
-
# :
|
|
160
|
+
# simplecov:enable
|
|
159
161
|
|
|
160
162
|
# Calls +mail+ with given arguments and immediately sends the resulting mail.
|
|
161
163
|
def sendmail(*args)
|
|
@@ -163,9 +165,9 @@ class Roda
|
|
|
163
165
|
m.deliver
|
|
164
166
|
end
|
|
165
167
|
end
|
|
166
|
-
# :
|
|
168
|
+
# simplecov:disable
|
|
167
169
|
ruby2_keywords(:sendmail) if respond_to?(:ruby2_keywords, true)
|
|
168
|
-
# :
|
|
170
|
+
# simplecov:enable
|
|
169
171
|
end
|
|
170
172
|
|
|
171
173
|
module RequestMethods
|
|
@@ -177,7 +179,7 @@ class Roda
|
|
|
177
179
|
def mail(*args)
|
|
178
180
|
if @env["REQUEST_METHOD"] == "MAIL"
|
|
179
181
|
# RODA4: Make terminal match the default
|
|
180
|
-
send(roda_class.
|
|
182
|
+
send(roda_class.mailer_opts[:terminal] ? :_verb : :if_match, args) do |*vs|
|
|
181
183
|
yield(*(vs + @env['roda.mail_args']))
|
|
182
184
|
end
|
|
183
185
|
end
|
|
@@ -202,7 +204,7 @@ class Roda
|
|
|
202
204
|
block.call if block
|
|
203
205
|
end
|
|
204
206
|
|
|
205
|
-
if content_type = header_content_type || roda_class.
|
|
207
|
+
if content_type = header_content_type || roda_class.mailer_opts[:content_type]
|
|
206
208
|
if mail.multipart?
|
|
207
209
|
if /multipart\/mixed/ =~ mail.content_type &&
|
|
208
210
|
mail.parts.length >= 2 &&
|
|
@@ -39,13 +39,18 @@ class Roda
|
|
|
39
39
|
app.opts[:match_suffix] = suffix if suffix
|
|
40
40
|
end
|
|
41
41
|
|
|
42
|
+
module ClassMethods
|
|
43
|
+
RodaPlugins.opt_attr_reader(self, :match_prefix)
|
|
44
|
+
RodaPlugins.opt_attr_reader(self, :match_suffix)
|
|
45
|
+
end
|
|
46
|
+
|
|
42
47
|
module RequestClassMethods
|
|
43
48
|
private
|
|
44
49
|
|
|
45
50
|
# Use the match prefix and suffix provided when loading the plugin, or fallback
|
|
46
51
|
# to Roda's default prefix/suffix if one was not provided.
|
|
47
52
|
def consume_pattern(pattern)
|
|
48
|
-
/\A#{roda_class.
|
|
53
|
+
/\A#{roda_class.match_prefix || "/"}(?:#{pattern})#{roda_class.match_suffix || "(?=\/|\z)"}/
|
|
49
54
|
end
|
|
50
55
|
end
|
|
51
56
|
|
|
@@ -40,20 +40,22 @@ class Roda
|
|
|
40
40
|
end
|
|
41
41
|
|
|
42
42
|
module ClassMethods
|
|
43
|
+
RodaPlugins.opt_attr_reader(self, :match_hook_args)
|
|
44
|
+
|
|
43
45
|
# Freeze the array of hook methods when freezing the app
|
|
44
46
|
def freeze
|
|
45
|
-
|
|
47
|
+
match_hook_args.freeze
|
|
46
48
|
super
|
|
47
49
|
end
|
|
48
50
|
|
|
49
51
|
# Add a match hook that will be called with matchers and block args.
|
|
50
52
|
def add_match_hook(&block)
|
|
51
|
-
|
|
53
|
+
match_hook_args << define_roda_method("match_hook_args", :any, &block)
|
|
52
54
|
|
|
53
|
-
if
|
|
54
|
-
class_eval("alias _match_hook_args #{
|
|
55
|
+
if match_hook_args.length == 1
|
|
56
|
+
class_eval("alias _match_hook_args #{match_hook_args.first}", __FILE__, __LINE__)
|
|
55
57
|
else
|
|
56
|
-
class_eval("def _match_hook_args(v, a); #{
|
|
58
|
+
class_eval("def _match_hook_args(v, a); #{match_hook_args.map{|m| "#{m}(v, a)"}.join(';')} end", __FILE__, __LINE__)
|
|
57
59
|
end
|
|
58
60
|
|
|
59
61
|
public :_match_hook_args
|
|
@@ -136,10 +136,10 @@ class Roda
|
|
|
136
136
|
def initialize(mid, app, *args, &block)
|
|
137
137
|
@mid = Class.new(mid)
|
|
138
138
|
RodaPlugins.set_temp_name(@mid){"#{mid}::middleware_subclass"}
|
|
139
|
-
if @mid.
|
|
139
|
+
if @mid.middleware_next_if_not_found?
|
|
140
140
|
@mid.plugin(:not_found, &NEXT_PROC)
|
|
141
141
|
end
|
|
142
|
-
if configure = @mid.
|
|
142
|
+
if configure = @mid.middleware_configure
|
|
143
143
|
configure.call(@mid, *args, &block)
|
|
144
144
|
elsif block || !args.empty?
|
|
145
145
|
raise RodaError, "cannot provide middleware args or block unless loading middleware plugin with a block"
|
|
@@ -154,7 +154,7 @@ class Roda
|
|
|
154
154
|
res = nil
|
|
155
155
|
|
|
156
156
|
call_next = catch(:next) do
|
|
157
|
-
env[@mid.
|
|
157
|
+
env[@mid.middleware_env_var] = true
|
|
158
158
|
res = @mid.call(env)
|
|
159
159
|
false
|
|
160
160
|
end
|
|
@@ -167,7 +167,7 @@ class Roda
|
|
|
167
167
|
end
|
|
168
168
|
end
|
|
169
169
|
|
|
170
|
-
if handle_result = @mid.
|
|
170
|
+
if handle_result = @mid.middleware_handle_result
|
|
171
171
|
handle_result.call(env, res)
|
|
172
172
|
end
|
|
173
173
|
|
|
@@ -176,6 +176,12 @@ class Roda
|
|
|
176
176
|
end
|
|
177
177
|
|
|
178
178
|
module ClassMethods
|
|
179
|
+
RodaPlugins.opt_attr_reader(self, :middleware_env_var)
|
|
180
|
+
RodaPlugins.opt_attr_reader(self, :middleware_configure)
|
|
181
|
+
RodaPlugins.opt_attr_reader(self, :middleware_handle_result)
|
|
182
|
+
RodaPlugins.opt_attr_reader(self, :middleware_forward_response_headers)
|
|
183
|
+
RodaPlugins.opt_attr_reader(self, :middleware_next_if_not_found, name: :middleware_next_if_not_found?)
|
|
184
|
+
|
|
179
185
|
# Create a Forwarder instead of a new instance if a non-Hash is given.
|
|
180
186
|
def new(app, *args, &block)
|
|
181
187
|
if app.is_a?(Hash)
|
|
@@ -193,7 +199,7 @@ class Roda
|
|
|
193
199
|
super do |r|
|
|
194
200
|
res = instance_exec(r, &block) # call Fallback
|
|
195
201
|
if r.forward_next
|
|
196
|
-
r.env['roda.response_headers'] = response.headers if
|
|
202
|
+
r.env['roda.response_headers'] = response.headers if self.class.middleware_forward_response_headers
|
|
197
203
|
throw :next, true
|
|
198
204
|
end
|
|
199
205
|
res
|
|
@@ -205,7 +211,7 @@ class Roda
|
|
|
205
211
|
def _roda_run_main_route(r)
|
|
206
212
|
res = super
|
|
207
213
|
if r.forward_next
|
|
208
|
-
r.env['roda.response_headers'] = response.headers if
|
|
214
|
+
r.env['roda.response_headers'] = response.headers if self.class.middleware_forward_response_headers
|
|
209
215
|
throw :next, true
|
|
210
216
|
end
|
|
211
217
|
res
|
|
@@ -216,7 +222,7 @@ class Roda
|
|
|
216
222
|
# Whether to forward the request to the next application. Set only if
|
|
217
223
|
# this request is being performed for middleware.
|
|
218
224
|
def forward_next
|
|
219
|
-
env[roda_class.
|
|
225
|
+
env[roda_class.middleware_env_var]
|
|
220
226
|
end
|
|
221
227
|
end
|
|
222
228
|
end
|
|
@@ -72,7 +72,7 @@ class Roda
|
|
|
72
72
|
|
|
73
73
|
# Use the given directories to setup servers. Any opts are passed to the public plugin.
|
|
74
74
|
def self.configure(app, directories, _=OPTS)
|
|
75
|
-
roots = app.opts[:multi_public_servers] = (app.
|
|
75
|
+
roots = app.opts[:multi_public_servers] = (app.multi_public_servers || {}).dup
|
|
76
76
|
directories.each do |key, path|
|
|
77
77
|
path, headers, mime = path
|
|
78
78
|
roots[key] = RACK_FILES.new(app.expand_path(path), headers||{}, mime||'text/plain')
|
|
@@ -80,11 +80,15 @@ class Roda
|
|
|
80
80
|
roots.freeze
|
|
81
81
|
end
|
|
82
82
|
|
|
83
|
+
module ClassMethods
|
|
84
|
+
RodaPlugins.opt_attr_reader(self, :multi_public_servers)
|
|
85
|
+
end
|
|
86
|
+
|
|
83
87
|
module RequestMethods
|
|
84
88
|
# Serve files from the directory corresponding to the given key if the file exists and
|
|
85
89
|
# this is a GET request.
|
|
86
90
|
def multi_public(key)
|
|
87
|
-
public_serve_with(roda_class.
|
|
91
|
+
public_serve_with(roda_class.multi_public_servers.fetch(key))
|
|
88
92
|
end
|
|
89
93
|
end
|
|
90
94
|
end
|
|
@@ -94,7 +94,7 @@ class Roda
|
|
|
94
94
|
# Freeze the multi_route regexp matchers so that there can be no thread safety issues at runtime.
|
|
95
95
|
def freeze
|
|
96
96
|
super
|
|
97
|
-
|
|
97
|
+
namespaced_routes.each_key do |k|
|
|
98
98
|
self::RodaRequest.named_route_regexp(k)
|
|
99
99
|
end
|
|
100
100
|
self::RodaRequest.instance_variable_get(:@namespaced_route_regexps).freeze
|
|
@@ -70,12 +70,15 @@ class Roda
|
|
|
70
70
|
end
|
|
71
71
|
|
|
72
72
|
module ClassMethods
|
|
73
|
+
RodaPlugins.opt_attr_reader(self, :multi_run_apps)
|
|
74
|
+
RodaPlugins.opt_attr_reader(self, :multi_run_app_blocks)
|
|
75
|
+
|
|
73
76
|
# Convert app blocks into apps by calling them, in order to force autoloads
|
|
74
77
|
# and to speed up subsequent calls.
|
|
75
78
|
# Freeze the multi_run apps so that there can be no thread safety issues at runtime.
|
|
76
79
|
def freeze
|
|
77
|
-
app_blocks =
|
|
78
|
-
apps =
|
|
80
|
+
app_blocks = multi_run_app_blocks
|
|
81
|
+
apps = multi_run_apps
|
|
79
82
|
app_blocks.each do |prefix, block|
|
|
80
83
|
apps[prefix] = block.call
|
|
81
84
|
end
|
|
@@ -85,12 +88,6 @@ class Roda
|
|
|
85
88
|
super
|
|
86
89
|
end
|
|
87
90
|
|
|
88
|
-
# Hash storing rack applications to dispatch to, keyed by the prefix
|
|
89
|
-
# for the application.
|
|
90
|
-
def multi_run_apps
|
|
91
|
-
opts[:multi_run_apps]
|
|
92
|
-
end
|
|
93
|
-
|
|
94
91
|
# Add a rack application to dispatch to for the given prefix when
|
|
95
92
|
# r.multi_run is called. If a block is given, it is called every time
|
|
96
93
|
# there is a request for the route to get the app to call. If neither
|
|
@@ -100,14 +97,14 @@ class Roda
|
|
|
100
97
|
prefix = prefix.to_s
|
|
101
98
|
if app
|
|
102
99
|
raise Roda::RodaError, "cannot provide both app and block to Roda.run" if block
|
|
103
|
-
|
|
104
|
-
|
|
100
|
+
multi_run_apps[prefix] = app
|
|
101
|
+
multi_run_app_blocks.delete(prefix)
|
|
105
102
|
elsif block
|
|
106
|
-
|
|
107
|
-
|
|
103
|
+
multi_run_apps.delete(prefix)
|
|
104
|
+
multi_run_app_blocks[prefix] = block
|
|
108
105
|
else
|
|
109
|
-
|
|
110
|
-
|
|
106
|
+
multi_run_apps.delete(prefix)
|
|
107
|
+
multi_run_app_blocks.delete(prefix)
|
|
111
108
|
end
|
|
112
109
|
self::RodaRequest.refresh_multi_run_regexp!
|
|
113
110
|
end
|
|
@@ -117,7 +114,7 @@ class Roda
|
|
|
117
114
|
# Refresh the multi_run_regexp, using the stored route prefixes,
|
|
118
115
|
# preferring longer routes before shorter routes.
|
|
119
116
|
def refresh_multi_run_regexp!
|
|
120
|
-
@multi_run_regexp = /(#{Regexp.union((roda_class.
|
|
117
|
+
@multi_run_regexp = /(#{Regexp.union((roda_class.multi_run_apps.keys + roda_class.multi_run_app_blocks.keys).sort.reverse)})/
|
|
121
118
|
end
|
|
122
119
|
|
|
123
120
|
# Refresh the multi_run_regexp if it hasn't been loaded yet.
|
|
@@ -132,8 +129,7 @@ class Roda
|
|
|
132
129
|
def multi_run
|
|
133
130
|
on self.class.multi_run_regexp do |prefix|
|
|
134
131
|
yield prefix if defined?(yield)
|
|
135
|
-
|
|
136
|
-
run(opts[:multi_run_apps][prefix] || opts[:multi_run_app_blocks][prefix].call)
|
|
132
|
+
run(roda_class.multi_run_apps[prefix] || roda_class.multi_run_app_blocks[prefix].call)
|
|
137
133
|
end
|
|
138
134
|
end
|
|
139
135
|
end
|
|
@@ -111,22 +111,24 @@ class Roda
|
|
|
111
111
|
end
|
|
112
112
|
|
|
113
113
|
module ClassMethods
|
|
114
|
+
RodaPlugins.opt_attr_reader(self, :namespaced_routes)
|
|
115
|
+
|
|
114
116
|
# Freeze the namespaced routes so that there can be no thread safety issues at runtime.
|
|
115
117
|
def freeze
|
|
116
|
-
|
|
118
|
+
namespaced_routes.freeze.each_value(&:freeze)
|
|
117
119
|
super
|
|
118
120
|
end
|
|
119
121
|
|
|
120
122
|
# Copy the named routes into the subclass when inheriting.
|
|
121
123
|
def inherited(subclass)
|
|
122
124
|
super
|
|
123
|
-
nsr = subclass.
|
|
124
|
-
|
|
125
|
+
nsr = subclass.namespaced_routes
|
|
126
|
+
namespaced_routes.each{|k, v| nsr[k] = v.dup}
|
|
125
127
|
end
|
|
126
128
|
|
|
127
129
|
# The names for the currently stored named routes
|
|
128
130
|
def named_routes(namespace=nil)
|
|
129
|
-
unless routes =
|
|
131
|
+
unless routes = namespaced_routes[namespace]
|
|
130
132
|
raise RodaError, "unsupported named_routes namespace used: #{namespace.inspect}"
|
|
131
133
|
end
|
|
132
134
|
routes.keys
|
|
@@ -134,7 +136,7 @@ class Roda
|
|
|
134
136
|
|
|
135
137
|
# Return the named route with the given name.
|
|
136
138
|
def named_route(name, namespace=nil)
|
|
137
|
-
|
|
139
|
+
namespaced_routes[namespace][name]
|
|
138
140
|
end
|
|
139
141
|
|
|
140
142
|
# If the given route has a name, treat it as a named route and
|
|
@@ -142,7 +144,7 @@ class Roda
|
|
|
142
144
|
# call super.
|
|
143
145
|
def route(name=nil, namespace=nil, &block)
|
|
144
146
|
if name
|
|
145
|
-
routes =
|
|
147
|
+
routes = namespaced_routes[namespace] ||= {}
|
|
146
148
|
if block
|
|
147
149
|
routes[name] = define_roda_method(routes[name] || "named_routes_#{namespace}_#{name}", 1, &convert_route_block(block))
|
|
148
150
|
elsif meth = routes.delete(name)
|
|
@@ -61,15 +61,17 @@ class Roda
|
|
|
61
61
|
end
|
|
62
62
|
|
|
63
63
|
module ClassMethods
|
|
64
|
+
RodaPlugins.opt_attr_reader(self, :named_templates)
|
|
65
|
+
|
|
64
66
|
# Freeze the named templates so that there can be no thread safety issues at runtime.
|
|
65
67
|
def freeze
|
|
66
|
-
|
|
68
|
+
named_templates.freeze
|
|
67
69
|
super
|
|
68
70
|
end
|
|
69
71
|
|
|
70
72
|
# Store a new template block and options for the given template name.
|
|
71
73
|
def template(name, options=nil, &block)
|
|
72
|
-
|
|
74
|
+
named_templates[name.to_s] = [options, define_roda_method("named_templates_#{name}", 0, &block)].freeze
|
|
73
75
|
nil
|
|
74
76
|
end
|
|
75
77
|
end
|
|
@@ -80,7 +82,7 @@ class Roda
|
|
|
80
82
|
# If a template name is given and it matches a named template, call
|
|
81
83
|
# the named template block to get the inline template to use.
|
|
82
84
|
def find_template(options)
|
|
83
|
-
if options[:template] && (template_opts, meth =
|
|
85
|
+
if options[:template] && (template_opts, meth = self.class.named_templates[template_name(options)]; meth)
|
|
84
86
|
if template_opts
|
|
85
87
|
options = template_opts.merge(options)
|
|
86
88
|
else
|
|
@@ -27,11 +27,16 @@ class Roda
|
|
|
27
27
|
rp = @remaining_path
|
|
28
28
|
if rp.getbyte(0) == 47
|
|
29
29
|
if last = rp.index('/', 1)
|
|
30
|
-
|
|
31
|
-
always
|
|
30
|
+
return false if last == 1
|
|
31
|
+
always do
|
|
32
|
+
@remaining_path = rp[last, rp.length]
|
|
33
|
+
yield rp[1, last-1]
|
|
34
|
+
end
|
|
32
35
|
elsif (len = rp.length) > 1
|
|
33
|
-
|
|
34
|
-
|
|
36
|
+
always do
|
|
37
|
+
@remaining_path = ""
|
|
38
|
+
yield rp[1, len]
|
|
39
|
+
end
|
|
35
40
|
end
|
|
36
41
|
end
|
|
37
42
|
end
|
|
@@ -41,8 +46,10 @@ class Roda
|
|
|
41
46
|
def is_segment
|
|
42
47
|
rp = @remaining_path
|
|
43
48
|
if rp.getbyte(0) == 47 && !rp.index('/', 1) && (len = rp.length) > 1
|
|
44
|
-
|
|
45
|
-
|
|
49
|
+
always do
|
|
50
|
+
@remaining_path = ""
|
|
51
|
+
yield rp[1, len]
|
|
52
|
+
end
|
|
46
53
|
end
|
|
47
54
|
end
|
|
48
55
|
end
|
|
@@ -28,7 +28,11 @@ class Roda
|
|
|
28
28
|
module RequestMethods
|
|
29
29
|
# Optimized version of +on+ that only supports a single string.
|
|
30
30
|
def on_branch(s)
|
|
31
|
-
|
|
31
|
+
rp = @remaining_path
|
|
32
|
+
if _match_string(s)
|
|
33
|
+
always{yield}
|
|
34
|
+
@remaining_path = rp
|
|
35
|
+
end
|
|
32
36
|
end
|
|
33
37
|
|
|
34
38
|
# Optimized version of +is+ that only supports a single string.
|
|
@@ -37,9 +41,8 @@ class Roda
|
|
|
37
41
|
if _match_string(s)
|
|
38
42
|
if @remaining_path.empty?
|
|
39
43
|
always{yield}
|
|
40
|
-
else
|
|
41
|
-
@remaining_path = rp
|
|
42
44
|
end
|
|
45
|
+
@remaining_path = rp
|
|
43
46
|
end
|
|
44
47
|
end
|
|
45
48
|
end
|
|
@@ -54,14 +54,20 @@ class Roda
|
|
|
54
54
|
# Also note that this plugin will not work correctly if you are using
|
|
55
55
|
# the symbol_matchers plugin with custom symbol matching and are using
|
|
56
56
|
# symbols that capture multiple values or no values.
|
|
57
|
+
#
|
|
58
|
+
# If you are using the pass or break plugin along with this plugin,
|
|
59
|
+
# it is recommended you load the params_capturing_restore plugin, as that
|
|
60
|
+
# allows +r.pass+ or +break+ inside param capturing blocking to work
|
|
61
|
+
# correctly.
|
|
57
62
|
module ParamsCapturing
|
|
58
63
|
REQUEST_INSTANCE_VARIABLES = [:@_params_captures].freeze
|
|
59
64
|
|
|
60
65
|
module RequestMethods
|
|
61
66
|
# Lazily initialize captures entry when params is called.
|
|
62
67
|
def params
|
|
68
|
+
return @params if @params
|
|
63
69
|
ret = super
|
|
64
|
-
ret['captures']
|
|
70
|
+
ret['captures'] = []
|
|
65
71
|
ret
|
|
66
72
|
end
|
|
67
73
|
|
|
@@ -100,16 +106,19 @@ class Roda
|
|
|
100
106
|
end
|
|
101
107
|
|
|
102
108
|
super do |*a|
|
|
103
|
-
if pc
|
|
104
|
-
@_params_captures = nil
|
|
105
|
-
pc.zip(a).each do |k,v|
|
|
106
|
-
params[k] = v
|
|
107
|
-
end
|
|
108
|
-
end
|
|
109
|
+
_set_params_captures(a) if pc
|
|
109
110
|
params['captures'].concat(a)
|
|
110
111
|
yield(*a)
|
|
111
112
|
end
|
|
112
113
|
end
|
|
114
|
+
|
|
115
|
+
# Set the captures into the parameters.
|
|
116
|
+
def _set_params_captures(a)
|
|
117
|
+
@_params_captures.zip(a).each do |k,v|
|
|
118
|
+
params[k] = v
|
|
119
|
+
end
|
|
120
|
+
@_params_captures = nil
|
|
121
|
+
end
|
|
113
122
|
end
|
|
114
123
|
end
|
|
115
124
|
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# frozen-string-literal: true
|
|
2
|
+
|
|
3
|
+
#
|
|
4
|
+
class Roda
|
|
5
|
+
module RodaPlugins
|
|
6
|
+
# The params_capturing_restore plugin allows the params_capturing plugin
|
|
7
|
+
# to work with the use of +r.pass+ (pass plugin) or +break+ (break plugin)
|
|
8
|
+
# inside a block that uses param capturing, restoring the previous state of
|
|
9
|
+
# the params.
|
|
10
|
+
#
|
|
11
|
+
# This is not done by the params_capturing plugin as it requires significant
|
|
12
|
+
# extra work for every param capturing block, and most users of the
|
|
13
|
+
# params_capturing plugin are not using the pass or break plugins.
|
|
14
|
+
module ParamsCapturingRestore
|
|
15
|
+
REQUEST_INSTANCE_VARIABLES = [:@_params_captures_restore].freeze
|
|
16
|
+
|
|
17
|
+
ABSENT = Object.new.freeze
|
|
18
|
+
private_constant :ABSENT
|
|
19
|
+
|
|
20
|
+
def self.load_dependencies(app)
|
|
21
|
+
app.plugin :params_capturing
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
module RequestMethods
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
# Restore previous state of the params before capturing when
|
|
28
|
+
# the block exits.
|
|
29
|
+
def if_match(args)
|
|
30
|
+
params = self.params
|
|
31
|
+
num_captures = params['captures'].length
|
|
32
|
+
old_params = nil
|
|
33
|
+
|
|
34
|
+
super do |*a|
|
|
35
|
+
old_params = @_params_captures_restore
|
|
36
|
+
@_params_captures_restore = nil
|
|
37
|
+
yield(*a)
|
|
38
|
+
end
|
|
39
|
+
ensure
|
|
40
|
+
if old_params
|
|
41
|
+
old_params.each_slice(2) do |k, v|
|
|
42
|
+
if v == ABSENT
|
|
43
|
+
params.delete(k)
|
|
44
|
+
else
|
|
45
|
+
params[k] = v
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
captures = params['captures']
|
|
51
|
+
if num_captures != captures.length
|
|
52
|
+
captures.slice!(num_captures, 1000000)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Record the original value of the params before calling super
|
|
57
|
+
# to update the params.
|
|
58
|
+
def _set_params_captures(a)
|
|
59
|
+
restore = @_params_captures_restore = []
|
|
60
|
+
@_params_captures.zip(a).each do |k,v|
|
|
61
|
+
restore << k << params.fetch(k, ABSENT)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
super
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
register_plugin(:params_capturing_restore, ParamsCapturingRestore)
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
data/lib/roda/plugins/part.rb
CHANGED
|
@@ -44,19 +44,19 @@ class Roda
|
|
|
44
44
|
render(template, :locals=>locals, &block)
|
|
45
45
|
end
|
|
46
46
|
RUBY
|
|
47
|
-
# :
|
|
47
|
+
# simplecov:disable
|
|
48
48
|
else
|
|
49
49
|
def part(template, locals=OPTS, &block)
|
|
50
50
|
render(template, :locals=>locals, &block)
|
|
51
51
|
end
|
|
52
52
|
end
|
|
53
|
-
# :
|
|
53
|
+
# simplecov:enable
|
|
54
54
|
end
|
|
55
55
|
|
|
56
56
|
module AssumeFixedLocalsInstanceMethods
|
|
57
|
-
# :
|
|
57
|
+
# simplecov:disable
|
|
58
58
|
if RUBY_VERSION >= '3.0'
|
|
59
|
-
# :
|
|
59
|
+
# simplecov:enable
|
|
60
60
|
class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
|
|
61
61
|
def part(template, ...)
|
|
62
62
|
if optimized_method = _optimized_render_method_for_locals(template, OPTS)
|
data/lib/roda/plugins/pass.rb
CHANGED