roda 3.65.0 → 3.68.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/CHANGELOG +16 -0
- data/doc/release_notes/3.66.0.txt +23 -0
- data/doc/release_notes/3.67.0.txt +25 -0
- data/doc/release_notes/3.68.0.txt +21 -0
- data/lib/roda/plugins/custom_block_results.rb +68 -0
- data/lib/roda/plugins/exception_page.rb +14 -4
- data/lib/roda/plugins/json.rb +27 -19
- data/lib/roda/plugins/multi_run.rb +51 -17
- data/lib/roda/plugins/render.rb +32 -11
- data/lib/roda/plugins/render_coverage.rb +86 -0
- data/lib/roda/plugins/route_csrf.rb +1 -1
- data/lib/roda/plugins/symbol_views.rb +3 -12
- data/lib/roda/request.rb +7 -1
- data/lib/roda/version.rb +1 -1
- metadata +11 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: faad040bd1e251d705afbfe6ced40794aa2f135658168c85603a6c8a83a497be
|
|
4
|
+
data.tar.gz: 6e1d78ffdf0442835a754e54fc6216d8f668f3d0cb8e9c5635c1a582b17d5746
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a663960b5f5c5391a44102b1b255fbb5546f241c2386b6fdbe8a5050f434407332f239eb0c862ab877de239ec1b4ea39c4f9202c488672a846c5f8eabaef6379
|
|
7
|
+
data.tar.gz: 508fa08ad66e1b11b5abc552345067fc2517aeb10ea060c6298a337b44db453c5daf35a0bbafe399dd9167286fd9ea35c07d75c340f640a6198beb52367352d5
|
data/CHANGELOG
CHANGED
|
@@ -1,3 +1,19 @@
|
|
|
1
|
+
= 3.68.0 (2023-05-11)
|
|
2
|
+
|
|
3
|
+
* Make Roda.run in multi_run plugin accept blocks to allow autoloading the apps to dispatch to (jeremyevans)
|
|
4
|
+
|
|
5
|
+
= 3.67.0 (2023-04-12)
|
|
6
|
+
|
|
7
|
+
* Add custom_block_results plugin for registering custom block result handlers (jeremyevans)
|
|
8
|
+
|
|
9
|
+
= 3.66.0 (2023-03-13)
|
|
10
|
+
|
|
11
|
+
* Support overriding exception page assets via exception_page_{css,js} instance methods (jeremyevans) (#306)
|
|
12
|
+
|
|
13
|
+
* Avoid keeping reference to Roda instance that caches an inline template (jeremyevans)
|
|
14
|
+
|
|
15
|
+
* Add render_coverage plugin, using tilt 2.1 features to allow for compiled templates in Ruby <3.2 (jeremyevans)
|
|
16
|
+
|
|
1
17
|
= 3.65.0 (2023-02-13)
|
|
2
18
|
|
|
3
19
|
* Make indifferent_params plugin work with changes in rack main branch (jeremyevans)
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
= New Features
|
|
2
|
+
|
|
3
|
+
* A render_coverage plugin has been added, which will cause compiled
|
|
4
|
+
template code to be saved to a folder and loaded using load instead
|
|
5
|
+
of eval. This allows for coverage to work for the compiled template
|
|
6
|
+
code in Ruby versions before 3.2. It can also allow for verbose
|
|
7
|
+
syntax warnings in compiled template code (ignored by eval), and
|
|
8
|
+
can also be useful for static analysis of compiled template code.
|
|
9
|
+
This plugin requires tilt 2.1+.
|
|
10
|
+
|
|
11
|
+
* The exception_page plugin now supports exception_page_{css,js}
|
|
12
|
+
instance methods for overriding the CSS and JavaScript on the
|
|
13
|
+
generated exception page.
|
|
14
|
+
|
|
15
|
+
= Other Improvements
|
|
16
|
+
|
|
17
|
+
* Using inline templates (render/view :inline option) no longer keeps
|
|
18
|
+
a reference to the Roda instance that caches the template.
|
|
19
|
+
|
|
20
|
+
= Backwards Compatibility
|
|
21
|
+
|
|
22
|
+
* The Render::TemplateMtimeWrapper API has changed. Any external
|
|
23
|
+
use of this class needs to be updated.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
= New Feature
|
|
2
|
+
|
|
3
|
+
* A custom_block_results plugin has been added for custom handling
|
|
4
|
+
of block results. This allows routing blocks to return
|
|
5
|
+
arbitrary objects instead of just String, nil, and false, and
|
|
6
|
+
to have custom handling for them. For example, if you want to
|
|
7
|
+
be able to have your routing blocks return the status code to use,
|
|
8
|
+
you could do:
|
|
9
|
+
|
|
10
|
+
plugin :custom_block_results
|
|
11
|
+
|
|
12
|
+
handle_block_result Integer do |result|
|
|
13
|
+
response.status_code = result
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
route do |r|
|
|
17
|
+
200
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
While the expected use of the handle_block_result method is with
|
|
21
|
+
class arguments, you can use any argument that implements an
|
|
22
|
+
appropriate === method.
|
|
23
|
+
|
|
24
|
+
The symbol_views and json plugins, which support additional block
|
|
25
|
+
results, now use the custom_block_results plugin internally.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
= New Feature
|
|
2
|
+
|
|
3
|
+
* Roda.run in the multi_run plugin now accepts blocks, to allow
|
|
4
|
+
autoloading of apps to dispatch to:
|
|
5
|
+
|
|
6
|
+
class App < Roda
|
|
7
|
+
plugin :multi_run
|
|
8
|
+
|
|
9
|
+
run("other_app"){OtherApp}
|
|
10
|
+
|
|
11
|
+
route do |r|
|
|
12
|
+
r.multi_run
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
With the above example, the block is not evaluated until a
|
|
17
|
+
request for the /other_app branch is received. If OtherApp is
|
|
18
|
+
autoloaded, this can speed up application startup and partial
|
|
19
|
+
testing. When freezing the application (for production use),
|
|
20
|
+
the block is eagerly loaded, so that requests to the
|
|
21
|
+
/other_app branch do not call the block on every request.
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# frozen-string-literal: true
|
|
2
|
+
|
|
3
|
+
#
|
|
4
|
+
class Roda
|
|
5
|
+
module RodaPlugins
|
|
6
|
+
# The custom_block_results plugin allows you to specify handling
|
|
7
|
+
# for different block results. By default, Roda only supports
|
|
8
|
+
# nil, false, and string block results, but using this plugin,
|
|
9
|
+
# you can support other block results.
|
|
10
|
+
#
|
|
11
|
+
# For example, if you wanted to support returning Integer
|
|
12
|
+
# block results, and have them set the response status code,
|
|
13
|
+
# you could do:
|
|
14
|
+
#
|
|
15
|
+
# plugin :custom_block_results
|
|
16
|
+
#
|
|
17
|
+
# handle_block_result Integer do |result|
|
|
18
|
+
# response.status_code = result
|
|
19
|
+
# end
|
|
20
|
+
#
|
|
21
|
+
# route do |r|
|
|
22
|
+
# 200
|
|
23
|
+
# end
|
|
24
|
+
#
|
|
25
|
+
# The expected use case for this is to customize behavior by
|
|
26
|
+
# class, but matching uses ===, so it is possible to use non-class
|
|
27
|
+
# objects that respond to === appropriately.
|
|
28
|
+
#
|
|
29
|
+
# Note that custom block result handling only occurs if the types
|
|
30
|
+
# are not handled by Roda itself. You cannot use this to modify
|
|
31
|
+
# the handling of nil, false, or string results.
|
|
32
|
+
module CustomBlockResults
|
|
33
|
+
def self.configure(app)
|
|
34
|
+
app.opts[:custom_block_results] ||= {}
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
module ClassMethods
|
|
38
|
+
# Freeze the configured custom block results when freezing the app.
|
|
39
|
+
def freeze
|
|
40
|
+
opts[:custom_block_results].freeze
|
|
41
|
+
super
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Specify a block that will be called when an instance of klass
|
|
45
|
+
# is returned as a block result. The block defines a method.
|
|
46
|
+
def handle_block_result(klass, &block)
|
|
47
|
+
opts[:custom_block_results][klass] = define_roda_method(opts[:custom_block_results][klass] || "custom_block_result_#{klass}", 1, &block)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
module RequestMethods
|
|
52
|
+
private
|
|
53
|
+
|
|
54
|
+
# Try each configured custom block result, and call the related method
|
|
55
|
+
# to get the block result.
|
|
56
|
+
def unsupported_block_result(result)
|
|
57
|
+
roda_class.opts[:custom_block_results].each do |klass, meth|
|
|
58
|
+
return scope.send(meth, result) if klass === result
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
super
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
register_plugin(:custom_block_results, CustomBlockResults)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
@@ -227,7 +227,7 @@ END
|
|
|
227
227
|
|
|
228
228
|
css = case css_file
|
|
229
229
|
when nil
|
|
230
|
-
"<style type=\"text/css\">#{
|
|
230
|
+
"<style type=\"text/css\">#{exception_page_css}</style>"
|
|
231
231
|
when false
|
|
232
232
|
# :nothing
|
|
233
233
|
else
|
|
@@ -236,7 +236,7 @@ END
|
|
|
236
236
|
|
|
237
237
|
js = case js_file
|
|
238
238
|
when nil
|
|
239
|
-
"<script type=\"text/javascript\">\n//<!--\n#{
|
|
239
|
+
"<script type=\"text/javascript\">\n//<!--\n#{exception_page_js}\n//-->\n</script>"
|
|
240
240
|
when false
|
|
241
241
|
# :nothing
|
|
242
242
|
else
|
|
@@ -399,6 +399,16 @@ END
|
|
|
399
399
|
end
|
|
400
400
|
end
|
|
401
401
|
|
|
402
|
+
# The CSS to use on the exception page
|
|
403
|
+
def exception_page_css
|
|
404
|
+
ExceptionPage.css
|
|
405
|
+
end
|
|
406
|
+
|
|
407
|
+
# The JavaScript to use on the exception page
|
|
408
|
+
def exception_page_js
|
|
409
|
+
ExceptionPage.js
|
|
410
|
+
end
|
|
411
|
+
|
|
402
412
|
private
|
|
403
413
|
|
|
404
414
|
if RUBY_VERSION >= '3.2'
|
|
@@ -420,11 +430,11 @@ END
|
|
|
420
430
|
def exception_page_assets
|
|
421
431
|
get 'exception_page.css' do
|
|
422
432
|
response['Content-Type'] = "text/css"
|
|
423
|
-
|
|
433
|
+
scope.exception_page_css
|
|
424
434
|
end
|
|
425
435
|
get 'exception_page.js' do
|
|
426
436
|
response['Content-Type'] = "application/javascript"
|
|
427
|
-
|
|
437
|
+
scope.exception_page_js
|
|
428
438
|
end
|
|
429
439
|
end
|
|
430
440
|
end
|
data/lib/roda/plugins/json.rb
CHANGED
|
@@ -55,11 +55,17 @@ class Roda
|
|
|
55
55
|
module Json
|
|
56
56
|
# Set the classes to automatically convert to JSON, and the serializer to use.
|
|
57
57
|
def self.configure(app, opts=OPTS)
|
|
58
|
+
app.plugin :custom_block_results
|
|
59
|
+
|
|
58
60
|
classes = opts[:classes] || [Array, Hash]
|
|
59
61
|
app.opts[:json_result_classes] ||= []
|
|
60
62
|
app.opts[:json_result_classes] += classes
|
|
61
|
-
app.opts[:json_result_classes]
|
|
62
|
-
|
|
63
|
+
classes = app.opts[:json_result_classes]
|
|
64
|
+
classes.uniq!
|
|
65
|
+
classes.freeze
|
|
66
|
+
classes.each do |klass|
|
|
67
|
+
app.opts[:custom_block_results][klass] = :handle_json_block_result
|
|
68
|
+
end
|
|
63
69
|
|
|
64
70
|
app.opts[:json_result_serializer] = opts[:serializer] || app.opts[:json_result_serializer] || app.opts[:json_serializer] || :to_json.to_proc
|
|
65
71
|
|
|
@@ -71,32 +77,34 @@ class Roda
|
|
|
71
77
|
module ClassMethods
|
|
72
78
|
# The classes that should be automatically converted to json
|
|
73
79
|
def json_result_classes
|
|
80
|
+
# RODA4: remove, only used by previous implementation.
|
|
74
81
|
opts[:json_result_classes]
|
|
75
82
|
end
|
|
76
83
|
end
|
|
77
84
|
|
|
85
|
+
module InstanceMethods
|
|
86
|
+
# Handle a result for one of the registered JSON result classes
|
|
87
|
+
# by converting the result to JSON.
|
|
88
|
+
def handle_json_block_result(result)
|
|
89
|
+
@_response['Content-Type'] ||= opts[:json_result_content_type]
|
|
90
|
+
@_request.send(:convert_to_json, result)
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
78
94
|
module RequestMethods
|
|
79
95
|
private
|
|
80
96
|
|
|
81
|
-
# If the result is an instance of one of the json_result_classes,
|
|
82
|
-
# convert the result to json and return it as the body, using the
|
|
83
|
-
# application/json content-type.
|
|
84
|
-
def block_result_body(result)
|
|
85
|
-
case result
|
|
86
|
-
when *roda_class.json_result_classes
|
|
87
|
-
response['Content-Type'] ||= roda_class.opts[:json_result_content_type]
|
|
88
|
-
convert_to_json(result)
|
|
89
|
-
else
|
|
90
|
-
super
|
|
91
|
-
end
|
|
92
|
-
end
|
|
93
|
-
|
|
94
97
|
# Convert the given object to JSON. Uses to_json by default,
|
|
95
98
|
# but can use a custom serializer passed to the plugin.
|
|
96
|
-
def convert_to_json(
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
99
|
+
def convert_to_json(result)
|
|
100
|
+
opts = roda_class.opts
|
|
101
|
+
serializer = opts[:json_result_serializer]
|
|
102
|
+
|
|
103
|
+
if opts[:json_result_include_request]
|
|
104
|
+
serializer.call(result, self)
|
|
105
|
+
else
|
|
106
|
+
serializer.call(result)
|
|
107
|
+
end
|
|
100
108
|
end
|
|
101
109
|
end
|
|
102
110
|
end
|
|
@@ -17,7 +17,7 @@ class Roda
|
|
|
17
17
|
# App.run "ro", OtherRodaApp
|
|
18
18
|
# App.run "si", SinatraApp
|
|
19
19
|
#
|
|
20
|
-
# Inside your route block, you can call r.multi_run to dispatch to all
|
|
20
|
+
# Inside your route block, you can call +r.multi_run+ to dispatch to all
|
|
21
21
|
# three rack applications based on the prefix:
|
|
22
22
|
#
|
|
23
23
|
# App.route do |r|
|
|
@@ -28,7 +28,7 @@ class Roda
|
|
|
28
28
|
# starting with +/ro+ to +OtherRodaApp+, and routes starting with +/si+ to
|
|
29
29
|
# SinatraApp.
|
|
30
30
|
#
|
|
31
|
-
# You can pass a block to +multi_run+ that will be called with the prefix,
|
|
31
|
+
# You can pass a block to +r.multi_run+ that will be called with the prefix,
|
|
32
32
|
# before dispatching to the rack app:
|
|
33
33
|
#
|
|
34
34
|
# App.route do |r|
|
|
@@ -39,12 +39,26 @@ class Roda
|
|
|
39
39
|
#
|
|
40
40
|
# This is useful for modifying the environment before passing it to the rack app.
|
|
41
41
|
#
|
|
42
|
-
#
|
|
43
|
-
#
|
|
44
|
-
#
|
|
45
|
-
#
|
|
46
|
-
#
|
|
47
|
-
#
|
|
42
|
+
# You can also call +Roda.run+ with a block:
|
|
43
|
+
#
|
|
44
|
+
# App.run("ra"){PlainRackApp}
|
|
45
|
+
# App.run("ro"){OtherRodaApp}
|
|
46
|
+
# App.run("si"){SinatraApp}
|
|
47
|
+
#
|
|
48
|
+
# When called with a block, Roda will call the block to get the app to dispatch to
|
|
49
|
+
# every time the block is called. The expected usage is with autoloaded classes,
|
|
50
|
+
# so that the related classes are not loaded until there is a request for the
|
|
51
|
+
# related route. This can sigficantly speedup startup or testing a subset of the
|
|
52
|
+
# application. When freezing an application, the blocks are called once to get the
|
|
53
|
+
# app to dispatch to, and that is cached, to ensure the any autoloads are completed
|
|
54
|
+
# before the application is frozen.
|
|
55
|
+
#
|
|
56
|
+
# The multi_run plugin is similar to the hash_branches and multi_route plugins, with
|
|
57
|
+
# the difference being the hash_branches and multi_route plugins keep all routing
|
|
58
|
+
# subtrees in the same Roda app/class, while multi_run dispatches to other rack apps.
|
|
59
|
+
# If you want to isolate your routing subtrees, multi_run is a better approach, but
|
|
60
|
+
# it does not let you set instance variables in the main Roda app and have those
|
|
61
|
+
# instance variables usable in the routing subtrees.
|
|
48
62
|
#
|
|
49
63
|
# To handle development environments that reload code, you can call the
|
|
50
64
|
# +run+ class method without an app to remove dispatching for the prefix.
|
|
@@ -52,12 +66,21 @@ class Roda
|
|
|
52
66
|
# Initialize the storage for the dispatched applications
|
|
53
67
|
def self.configure(app)
|
|
54
68
|
app.opts[:multi_run_apps] ||= {}
|
|
69
|
+
app.opts[:multi_run_app_blocks] ||= {}
|
|
55
70
|
end
|
|
56
71
|
|
|
57
72
|
module ClassMethods
|
|
73
|
+
# Convert app blocks into apps by calling them, in order to force autoloads
|
|
74
|
+
# and to speed up subsequent calls.
|
|
58
75
|
# Freeze the multi_run apps so that there can be no thread safety issues at runtime.
|
|
59
76
|
def freeze
|
|
60
|
-
opts[:
|
|
77
|
+
app_blocks = opts[:multi_run_app_blocks]
|
|
78
|
+
apps = opts[:multi_run_apps]
|
|
79
|
+
app_blocks.each do |prefix, block|
|
|
80
|
+
apps[prefix] = block.call
|
|
81
|
+
end
|
|
82
|
+
app_blocks.clear.freeze
|
|
83
|
+
apps.freeze
|
|
61
84
|
self::RodaRequest.refresh_multi_run_regexp!
|
|
62
85
|
super
|
|
63
86
|
end
|
|
@@ -69,12 +92,22 @@ class Roda
|
|
|
69
92
|
end
|
|
70
93
|
|
|
71
94
|
# Add a rack application to dispatch to for the given prefix when
|
|
72
|
-
# r.multi_run is called.
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
95
|
+
# r.multi_run is called. If a block is given, it is called every time
|
|
96
|
+
# there is a request for the route to get the app to call. If neither
|
|
97
|
+
# a block or an app is provided, any stored route for the prefix is
|
|
98
|
+
# removed. It is an error to provide both an app and block in the same call.
|
|
99
|
+
def run(prefix, app=nil, &block)
|
|
100
|
+
prefix = prefix.to_s
|
|
101
|
+
if app
|
|
102
|
+
raise Roda::RodaError, "cannot provide both app and block to Roda.run" if block
|
|
103
|
+
opts[:multi_run_apps][prefix] = app
|
|
104
|
+
opts[:multi_run_app_blocks].delete(prefix)
|
|
105
|
+
elsif block
|
|
106
|
+
opts[:multi_run_apps].delete(prefix)
|
|
107
|
+
opts[:multi_run_app_blocks][prefix] = block
|
|
76
108
|
else
|
|
77
|
-
multi_run_apps.delete(prefix
|
|
109
|
+
opts[:multi_run_apps].delete(prefix)
|
|
110
|
+
opts[:multi_run_app_blocks].delete(prefix)
|
|
78
111
|
end
|
|
79
112
|
self::RodaRequest.refresh_multi_run_regexp!
|
|
80
113
|
end
|
|
@@ -84,7 +117,7 @@ class Roda
|
|
|
84
117
|
# Refresh the multi_run_regexp, using the stored route prefixes,
|
|
85
118
|
# preferring longer routes before shorter routes.
|
|
86
119
|
def refresh_multi_run_regexp!
|
|
87
|
-
@multi_run_regexp = /(#{Regexp.union(roda_class.multi_run_apps.keys.sort.reverse)})/
|
|
120
|
+
@multi_run_regexp = /(#{Regexp.union((roda_class.opts[:multi_run_apps].keys + roda_class.opts[:multi_run_app_blocks].keys).sort.reverse)})/
|
|
88
121
|
end
|
|
89
122
|
|
|
90
123
|
# Refresh the multi_run_regexp if it hasn't been loaded yet.
|
|
@@ -95,11 +128,12 @@ class Roda
|
|
|
95
128
|
|
|
96
129
|
module RequestMethods
|
|
97
130
|
# If one of the stored route prefixes match the current request,
|
|
98
|
-
# dispatch the request to the
|
|
131
|
+
# dispatch the request to the appropriate rack application.
|
|
99
132
|
def multi_run
|
|
100
133
|
on self.class.multi_run_regexp do |prefix|
|
|
101
134
|
yield prefix if defined?(yield)
|
|
102
|
-
|
|
135
|
+
opts = scope.opts
|
|
136
|
+
run(opts[:multi_run_apps][prefix] || opts[:multi_run_app_blocks][prefix].call)
|
|
103
137
|
end
|
|
104
138
|
end
|
|
105
139
|
end
|
data/lib/roda/plugins/render.rb
CHANGED
|
@@ -320,14 +320,16 @@ class Roda
|
|
|
320
320
|
# template file has been modified. This is an internal class and
|
|
321
321
|
# the API is subject to change at any time.
|
|
322
322
|
class TemplateMtimeWrapper
|
|
323
|
-
def initialize(
|
|
324
|
-
@
|
|
325
|
-
@
|
|
326
|
-
@
|
|
327
|
-
|
|
323
|
+
def initialize(roda_class, opts, template_opts)
|
|
324
|
+
@roda_class = roda_class
|
|
325
|
+
@opts = opts
|
|
326
|
+
@template_opts = template_opts
|
|
327
|
+
reset_template
|
|
328
328
|
|
|
329
|
-
@
|
|
330
|
-
|
|
329
|
+
@path = opts[:path]
|
|
330
|
+
deps = opts[:dependencies]
|
|
331
|
+
@dependencies = ([@path] + Array(deps)) if deps
|
|
332
|
+
@mtime = template_last_modified
|
|
331
333
|
end
|
|
332
334
|
|
|
333
335
|
# If the template file exists and the modification time has
|
|
@@ -358,7 +360,7 @@ class Roda
|
|
|
358
360
|
else
|
|
359
361
|
if mtime != @mtime
|
|
360
362
|
@mtime = mtime
|
|
361
|
-
|
|
363
|
+
reset_template
|
|
362
364
|
return true
|
|
363
365
|
end
|
|
364
366
|
end
|
|
@@ -407,6 +409,14 @@ class Roda
|
|
|
407
409
|
end
|
|
408
410
|
end
|
|
409
411
|
end
|
|
412
|
+
|
|
413
|
+
private
|
|
414
|
+
|
|
415
|
+
# Reset the template, done every time the template or one of its
|
|
416
|
+
# dependencies is modified.
|
|
417
|
+
def reset_template
|
|
418
|
+
@template = @roda_class.create_template(@opts, @template_opts)
|
|
419
|
+
end
|
|
410
420
|
end
|
|
411
421
|
|
|
412
422
|
module ClassMethods
|
|
@@ -427,6 +437,17 @@ class Roda
|
|
|
427
437
|
end
|
|
428
438
|
end
|
|
429
439
|
|
|
440
|
+
# Return an Tilt::Template object based on the given opts and template_opts.
|
|
441
|
+
def create_template(opts, template_opts)
|
|
442
|
+
opts[:template_class].new(opts[:path], 1, template_opts, &opts[:template_block])
|
|
443
|
+
end
|
|
444
|
+
|
|
445
|
+
# A proc that returns content, used for inline templates, so that the template
|
|
446
|
+
# doesn't hold a reference to the instance of the class
|
|
447
|
+
def inline_template_block(content)
|
|
448
|
+
Proc.new{content}
|
|
449
|
+
end
|
|
450
|
+
|
|
430
451
|
# Copy the rendering options into the subclass, duping
|
|
431
452
|
# them as necessary to prevent changes in the subclass
|
|
432
453
|
# affecting the parent class.
|
|
@@ -656,7 +677,7 @@ class Roda
|
|
|
656
677
|
if content = opts[:inline]
|
|
657
678
|
path = opts[:path] = content
|
|
658
679
|
template_class = opts[:template_class] ||= ::Tilt[engine]
|
|
659
|
-
opts[:template_block] =
|
|
680
|
+
opts[:template_block] = self.class.inline_template_block(content)
|
|
660
681
|
else
|
|
661
682
|
opts[:views] ||= render_opts[:views]
|
|
662
683
|
path = opts[:path] ||= template_path(opts)
|
|
@@ -730,14 +751,14 @@ class Roda
|
|
|
730
751
|
!opts[:inline]
|
|
731
752
|
|
|
732
753
|
if render_opts[:check_template_mtime] && !opts[:template_block] && !cache
|
|
733
|
-
template = TemplateMtimeWrapper.new(
|
|
754
|
+
template = TemplateMtimeWrapper.new(self.class, opts, template_opts)
|
|
734
755
|
|
|
735
756
|
if define_compiled_method
|
|
736
757
|
method_name = :"_roda_template_#{self.class.object_id}_#{method_cache_key}"
|
|
737
758
|
method_cache[method_cache_key] = template.define_compiled_method(self.class, method_name)
|
|
738
759
|
end
|
|
739
760
|
else
|
|
740
|
-
template =
|
|
761
|
+
template = self.class.create_template(opts, template_opts)
|
|
741
762
|
|
|
742
763
|
if define_compiled_method && cache != false
|
|
743
764
|
begin
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# frozen-string-literal: true
|
|
2
|
+
|
|
3
|
+
require 'tilt'
|
|
4
|
+
# :nocov:
|
|
5
|
+
raise 'Tilt version does not support coverable templates' unless Tilt::Template.method_defined?(:compiled_path=)
|
|
6
|
+
# :nocov:
|
|
7
|
+
|
|
8
|
+
#
|
|
9
|
+
class Roda
|
|
10
|
+
module RodaPlugins
|
|
11
|
+
# The render_coverage plugin builds on top of the render plugin
|
|
12
|
+
# and sets compiled_path on created templates. This allows
|
|
13
|
+
# Ruby's coverage library before Ruby 3.2 to consider code created
|
|
14
|
+
# by templates. You may not need this plugin on Ruby 3.2+, since
|
|
15
|
+
# on Ruby 3.2+, coverage can consider code loaded with +eval+.
|
|
16
|
+
# This plugin is only supported when using tilt 2.1+, since it
|
|
17
|
+
# requires the compiled_path supported added in tilt 2.1.
|
|
18
|
+
#
|
|
19
|
+
# By default, the render_coverage plugin will use +coverage/views+
|
|
20
|
+
# as the directory containing the compiled template files. You can
|
|
21
|
+
# change this by passing the :dir option when loading the plugin.
|
|
22
|
+
# By default, the plugin will set the compiled_path by taking the
|
|
23
|
+
# template file path, stripping off any of the allowed_paths used
|
|
24
|
+
# by the render plugin, and converting slashes to dashes. You can
|
|
25
|
+
# override the allowed_paths to strip by passing the :strip_paths
|
|
26
|
+
# option when loading the plugin. Paths outside :strip_paths (or
|
|
27
|
+
# the render plugin allowed_paths if :strip_paths is not set) will
|
|
28
|
+
# not have a compiled_path set.
|
|
29
|
+
#
|
|
30
|
+
# Due to how Ruby's coverage library works in regards to loading
|
|
31
|
+
# a compiled template file with identical code more than once,
|
|
32
|
+
# it may be beneficial to run coverage testing with the
|
|
33
|
+
# +RODA_RENDER_COMPILED_METHOD_SUPPORT+ environment variable set
|
|
34
|
+
# to +no+ if using this plugin.
|
|
35
|
+
module RenderCoverage
|
|
36
|
+
def self.load_dependencies(app, opts=OPTS)
|
|
37
|
+
app.plugin :render
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Use the :dir option to set the directory to store the compiled
|
|
41
|
+
# template files, and the :strip_paths directory for paths to
|
|
42
|
+
# strip.
|
|
43
|
+
def self.configure(app, opts=OPTS)
|
|
44
|
+
app.opts[:render_coverage_strip_paths] = opts[:strip_paths].map{|f| File.expand_path(f)} if opts.has_key?(:strip_paths)
|
|
45
|
+
coverage_dir = app.opts[:render_coverage_dir] = opts[:dir] || app.opts[:render_coverage_dir] || 'coverage/views'
|
|
46
|
+
Dir.mkdir(coverage_dir) unless File.directory?(coverage_dir)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
module ClassMethods
|
|
50
|
+
# Set a compiled path on the created template, if the path for
|
|
51
|
+
# the template is in one of the allowed_views.
|
|
52
|
+
def create_template(opts, template_opts)
|
|
53
|
+
template = super
|
|
54
|
+
return template if opts[:template_block]
|
|
55
|
+
|
|
56
|
+
path = File.expand_path(opts[:path])
|
|
57
|
+
(self.opts[:render_coverage_strip_paths] || render_opts[:allowed_paths]).each do |dir|
|
|
58
|
+
if path.start_with?(dir + '/')
|
|
59
|
+
template.compiled_path = File.join(self.opts[:render_coverage_dir], path[dir.length+1, 10000000].gsub('/', '-'))
|
|
60
|
+
break
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
template
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
module InstanceMethods
|
|
69
|
+
private
|
|
70
|
+
|
|
71
|
+
# Convert template paths to real paths to try to ensure the same template is cached.
|
|
72
|
+
def template_path(opts)
|
|
73
|
+
path = super
|
|
74
|
+
|
|
75
|
+
if File.file?(path)
|
|
76
|
+
File.realpath(path)
|
|
77
|
+
else
|
|
78
|
+
path
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
register_plugin(:render_coverage, RenderCoverage)
|
|
85
|
+
end
|
|
86
|
+
end
|
|
@@ -90,7 +90,7 @@ class Roda
|
|
|
90
90
|
# action attribute, and returns a path you can pass to csrf_token
|
|
91
91
|
# that should be valid for the form submission. The argument should
|
|
92
92
|
# either be nil or a string representing a relative path, absolute
|
|
93
|
-
# path, or full URL.
|
|
93
|
+
# path, or full URL (using appropriate URL encoding).
|
|
94
94
|
# csrf_tag(path=nil, method='POST') :: An HTML hidden input tag string containing the CSRF token, suitable
|
|
95
95
|
# for placing in an HTML form. Takes the same arguments as csrf_token.
|
|
96
96
|
# csrf_token(path=nil, method='POST') :: The value of the csrf token, in case it needs to be accessed
|
|
@@ -23,18 +23,9 @@ class Roda
|
|
|
23
23
|
# :foo
|
|
24
24
|
# end
|
|
25
25
|
module SymbolViews
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
# If the block result is a symbol, consider the symbol a
|
|
30
|
-
# template name and use the template view as the body.
|
|
31
|
-
def block_result_body(result)
|
|
32
|
-
if result.is_a?(Symbol)
|
|
33
|
-
scope.view(result)
|
|
34
|
-
else
|
|
35
|
-
super
|
|
36
|
-
end
|
|
37
|
-
end
|
|
26
|
+
def self.configure(app)
|
|
27
|
+
app.plugin :custom_block_results
|
|
28
|
+
app.opts[:custom_block_results][Symbol] = :view
|
|
38
29
|
end
|
|
39
30
|
end
|
|
40
31
|
|
data/lib/roda/request.rb
CHANGED
|
@@ -547,7 +547,7 @@ class Roda
|
|
|
547
547
|
when nil, false
|
|
548
548
|
# nothing
|
|
549
549
|
else
|
|
550
|
-
|
|
550
|
+
unsupported_block_result(result)
|
|
551
551
|
end
|
|
552
552
|
end
|
|
553
553
|
|
|
@@ -652,6 +652,12 @@ class Roda
|
|
|
652
652
|
end
|
|
653
653
|
end
|
|
654
654
|
|
|
655
|
+
# How to handle block results that are not nil, false, or a String.
|
|
656
|
+
# By default raises an exception.
|
|
657
|
+
def unsupported_block_result(result)
|
|
658
|
+
raise RodaError, "unsupported block result: #{result.inspect}"
|
|
659
|
+
end
|
|
660
|
+
|
|
655
661
|
# Handle an unsupported matcher.
|
|
656
662
|
def unsupported_matcher(matcher)
|
|
657
663
|
raise RodaError, "unsupported matcher: #{matcher.inspect}"
|
data/lib/roda/version.rb
CHANGED
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.68.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: 2023-
|
|
11
|
+
date: 2023-05-11 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rack
|
|
@@ -238,6 +238,9 @@ extra_rdoc_files:
|
|
|
238
238
|
- doc/release_notes/3.63.0.txt
|
|
239
239
|
- doc/release_notes/3.64.0.txt
|
|
240
240
|
- doc/release_notes/3.65.0.txt
|
|
241
|
+
- doc/release_notes/3.66.0.txt
|
|
242
|
+
- doc/release_notes/3.67.0.txt
|
|
243
|
+
- doc/release_notes/3.68.0.txt
|
|
241
244
|
- doc/release_notes/3.7.0.txt
|
|
242
245
|
- doc/release_notes/3.8.0.txt
|
|
243
246
|
- doc/release_notes/3.9.0.txt
|
|
@@ -310,6 +313,9 @@ files:
|
|
|
310
313
|
- doc/release_notes/3.63.0.txt
|
|
311
314
|
- doc/release_notes/3.64.0.txt
|
|
312
315
|
- doc/release_notes/3.65.0.txt
|
|
316
|
+
- doc/release_notes/3.66.0.txt
|
|
317
|
+
- doc/release_notes/3.67.0.txt
|
|
318
|
+
- doc/release_notes/3.68.0.txt
|
|
313
319
|
- doc/release_notes/3.7.0.txt
|
|
314
320
|
- doc/release_notes/3.8.0.txt
|
|
315
321
|
- doc/release_notes/3.9.0.txt
|
|
@@ -340,6 +346,7 @@ files:
|
|
|
340
346
|
- lib/roda/plugins/content_security_policy.rb
|
|
341
347
|
- lib/roda/plugins/cookies.rb
|
|
342
348
|
- lib/roda/plugins/csrf.rb
|
|
349
|
+
- lib/roda/plugins/custom_block_results.rb
|
|
343
350
|
- lib/roda/plugins/custom_matchers.rb
|
|
344
351
|
- lib/roda/plugins/default_headers.rb
|
|
345
352
|
- lib/roda/plugins/default_status.rb
|
|
@@ -409,6 +416,7 @@ files:
|
|
|
409
416
|
- lib/roda/plugins/recheck_precompiled_assets.rb
|
|
410
417
|
- lib/roda/plugins/relative_path.rb
|
|
411
418
|
- lib/roda/plugins/render.rb
|
|
419
|
+
- lib/roda/plugins/render_coverage.rb
|
|
412
420
|
- lib/roda/plugins/render_each.rb
|
|
413
421
|
- lib/roda/plugins/render_locals.rb
|
|
414
422
|
- lib/roda/plugins/request_aref.rb
|
|
@@ -466,7 +474,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
466
474
|
- !ruby/object:Gem::Version
|
|
467
475
|
version: '0'
|
|
468
476
|
requirements: []
|
|
469
|
-
rubygems_version: 3.4.
|
|
477
|
+
rubygems_version: 3.4.10
|
|
470
478
|
signing_key:
|
|
471
479
|
specification_version: 4
|
|
472
480
|
summary: Routing tree web toolkit
|