roda 3.86.0 → 3.88.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/plugins/custom_block_results.rb +19 -2
- data/lib/roda/plugins/head.rb +1 -1
- data/lib/roda/plugins/header_matchers.rb +4 -3
- data/lib/roda/plugins/host_routing.rb +190 -0
- data/lib/roda/plugins/json_parser.rb +20 -19
- data/lib/roda/plugins/render.rb +158 -40
- data/lib/roda/plugins/render_coverage.rb +22 -3
- data/lib/roda/plugins/render_each.rb +1 -1
- data/lib/roda/response.rb +4 -2
- data/lib/roda/version.rb +1 -1
- metadata +4 -7
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: bfe23e68b3616d0513c55ca0d6006639eaa5595c36e48445e3a4b961f17dc483
|
|
4
|
+
data.tar.gz: 110d2f273b1b2eee5d0717d7238c3c131d7dd9fd2f7eb5a7ff0b575ce0256af0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2c60bdca82724f3f1f468ca408e6d58a7fb61bb49e5819835afdb59743b82a8f1ad77e98775f8ce720bcc4aff4d72ae2e3526419cba509d60588eefd585aedba
|
|
7
|
+
data.tar.gz: 223ed6296acb9829902050161f88356e4bd6d2e5da5dc06e43175b4a59cd38c312a63c79cd93b5f2c648d7bf25e84f66a1ab02c72b603b42866ad434851350d1
|
|
@@ -28,7 +28,16 @@ class Roda
|
|
|
28
28
|
#
|
|
29
29
|
# Note that custom block result handling only occurs if the types
|
|
30
30
|
# are not handled by Roda itself. You cannot use this to modify
|
|
31
|
-
# the handling of nil, false, or string results.
|
|
31
|
+
# the handling of nil, false, or string results. Additionally,
|
|
32
|
+
# if the response body has already been written to before the the
|
|
33
|
+
# route block exits, then the result of the block is ignored,
|
|
34
|
+
# and the related +handle_block_result+ block will not be called
|
|
35
|
+
# (this is standard Roda behavior).
|
|
36
|
+
#
|
|
37
|
+
# The return value of the +handle_block_result+ block is written
|
|
38
|
+
# to the body if the block return value is a String, similar to
|
|
39
|
+
# standard Roda handling of block results. Non-String return
|
|
40
|
+
# values are ignored.
|
|
32
41
|
module CustomBlockResults
|
|
33
42
|
def self.configure(app)
|
|
34
43
|
app.opts[:custom_block_results] ||= {}
|
|
@@ -55,7 +64,15 @@ class Roda
|
|
|
55
64
|
# to get the block result.
|
|
56
65
|
def unsupported_block_result(result)
|
|
57
66
|
roda_class.opts[:custom_block_results].each do |klass, meth|
|
|
58
|
-
|
|
67
|
+
if klass === result
|
|
68
|
+
result = scope.send(meth, result)
|
|
69
|
+
|
|
70
|
+
if String === result
|
|
71
|
+
return result
|
|
72
|
+
else
|
|
73
|
+
return
|
|
74
|
+
end
|
|
75
|
+
end
|
|
59
76
|
end
|
|
60
77
|
|
|
61
78
|
super
|
data/lib/roda/plugins/head.rb
CHANGED
|
@@ -51,7 +51,8 @@ class Roda
|
|
|
51
51
|
|
|
52
52
|
# Match if the given uppercase key is present inside the environment.
|
|
53
53
|
def match_header(key)
|
|
54
|
-
key = key.upcase
|
|
54
|
+
key = key.upcase
|
|
55
|
+
key.tr!("-","_")
|
|
55
56
|
unless key == "CONTENT_TYPE" || key == "CONTENT_LENGTH"
|
|
56
57
|
key = "HTTP_#{key}"
|
|
57
58
|
end
|
|
@@ -75,8 +76,8 @@ class Roda
|
|
|
75
76
|
# Match the submitted user agent to the given pattern, capturing any
|
|
76
77
|
# regexp match groups.
|
|
77
78
|
def match_user_agent(pattern)
|
|
78
|
-
if (user_agent = @env["HTTP_USER_AGENT"]) &&
|
|
79
|
-
@captures.concat(
|
|
79
|
+
if (user_agent = @env["HTTP_USER_AGENT"]) && (match = pattern.match(user_agent))
|
|
80
|
+
@captures.concat(match.captures)
|
|
80
81
|
end
|
|
81
82
|
end
|
|
82
83
|
end
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
# frozen-string-literal: true
|
|
2
|
+
|
|
3
|
+
#
|
|
4
|
+
class Roda
|
|
5
|
+
module RodaPlugins
|
|
6
|
+
# The host_routing plugin adds support for more routing requests based on
|
|
7
|
+
# the requested host. It also adds predicate methods for checking
|
|
8
|
+
# whether a request was requested with the given host.
|
|
9
|
+
#
|
|
10
|
+
# When loading the plugin, you pass a block, which is used for configuring
|
|
11
|
+
# the plugin. For example, if you want to treat requests to api.example.com
|
|
12
|
+
# or api2.example.com as api requests, and treat other requests as www
|
|
13
|
+
# requests, you could use:
|
|
14
|
+
#
|
|
15
|
+
# plugin :host_routing do |hosts|
|
|
16
|
+
# hosts.to :api, "api.example.com", "api2.example.com"
|
|
17
|
+
# hosts.default :www
|
|
18
|
+
# end
|
|
19
|
+
#
|
|
20
|
+
# With this configuration, in your routing tree, you can call the +r.api+ and
|
|
21
|
+
# +r.www+ methods for dispatching to routing blocks only for those types of
|
|
22
|
+
# requests:
|
|
23
|
+
#
|
|
24
|
+
# route do |r|
|
|
25
|
+
# r.api do
|
|
26
|
+
# # requests to api.example.com or api2.example.com
|
|
27
|
+
# end
|
|
28
|
+
#
|
|
29
|
+
# r.www do
|
|
30
|
+
# # requests to other domains
|
|
31
|
+
# end
|
|
32
|
+
# end
|
|
33
|
+
#
|
|
34
|
+
# In addition to the routing methods, predicate methods are also added to the
|
|
35
|
+
# request object:
|
|
36
|
+
#
|
|
37
|
+
# route do |r|
|
|
38
|
+
# "#{r.api?}-#{r.www?}"
|
|
39
|
+
# end
|
|
40
|
+
# # Requests to api.example.com or api2.example.com return "true-false"
|
|
41
|
+
# # Other requests return "false-true"
|
|
42
|
+
#
|
|
43
|
+
# If the +:scope_predicates+ plugin option is given, predicate methods are also
|
|
44
|
+
# created in route block scope:
|
|
45
|
+
#
|
|
46
|
+
# plugin :host_routing, scope_predicates: true do |hosts|
|
|
47
|
+
# hosts.to :api, "api.example.com"
|
|
48
|
+
# hosts.default :www
|
|
49
|
+
# end
|
|
50
|
+
#
|
|
51
|
+
# route do |r|
|
|
52
|
+
# "#{api?}-#{www?}"
|
|
53
|
+
# end
|
|
54
|
+
#
|
|
55
|
+
# To handle hosts that match a certain format (such as all subdomains),
|
|
56
|
+
# where the specific host names are not known up front, you can provide a block
|
|
57
|
+
# when calling +hosts.default+. This block is passed the host name, or an empty
|
|
58
|
+
# string if no host name is provided, and is evaluated in route block scope.
|
|
59
|
+
# When using this support, you should also call +hosts.register+
|
|
60
|
+
# to register host types that could be returned by the block. For example, to
|
|
61
|
+
# handle api subdomains differently:
|
|
62
|
+
#
|
|
63
|
+
# plugin :host_routing do |hosts|
|
|
64
|
+
# hosts.to :api, "api.example.com"
|
|
65
|
+
# hosts.register :api_sub
|
|
66
|
+
# hosts.default :www do |host|
|
|
67
|
+
# :api_sub if host.end_with?(".api.example.com")
|
|
68
|
+
# end
|
|
69
|
+
# end
|
|
70
|
+
#
|
|
71
|
+
# This plugin uses the host method on the request to get the hostname (this method
|
|
72
|
+
# is defined by Rack).
|
|
73
|
+
module HostRouting
|
|
74
|
+
# Setup the host routing support. The block yields an object used to
|
|
75
|
+
# configure the plugin. Options:
|
|
76
|
+
#
|
|
77
|
+
# :scope_predicates :: Setup predicate methods in route block scope
|
|
78
|
+
# in addition to request scope.
|
|
79
|
+
def self.configure(app, opts=OPTS, &block)
|
|
80
|
+
hosts, host_hash, default_block, default_host = DSL.new.process(&block)
|
|
81
|
+
app.opts[:host_routing_hash] = host_hash
|
|
82
|
+
app.opts[:host_routing_default_host] = default_host
|
|
83
|
+
|
|
84
|
+
app.send(:define_method, :_host_routing_default, &default_block) if default_block
|
|
85
|
+
|
|
86
|
+
app::RodaRequest.class_exec do
|
|
87
|
+
hosts.each do |host|
|
|
88
|
+
host_sym = host.to_sym
|
|
89
|
+
define_method(host_sym){|&blk| always(&blk) if _host_routing_host == host}
|
|
90
|
+
alias_method host_sym, host_sym
|
|
91
|
+
|
|
92
|
+
meth = :"#{host}?"
|
|
93
|
+
define_method(meth){_host_routing_host == host}
|
|
94
|
+
alias_method meth, meth
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
if opts[:scope_predicates]
|
|
99
|
+
app.class_exec do
|
|
100
|
+
hosts.each do |host|
|
|
101
|
+
meth = :"#{host}?"
|
|
102
|
+
define_method(meth){@_request.send(meth)}
|
|
103
|
+
alias_method meth, meth
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
class DSL
|
|
110
|
+
def initialize
|
|
111
|
+
@hosts = []
|
|
112
|
+
@host_hash = {}
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# Run the DSL for the given block.
|
|
116
|
+
def process(&block)
|
|
117
|
+
instance_exec(self, &block)
|
|
118
|
+
|
|
119
|
+
if !@default_host
|
|
120
|
+
raise RodaError, "must call default method inside host_routing plugin block to set default host"
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
@hosts.concat(@host_hash.values)
|
|
124
|
+
@hosts << @default_host
|
|
125
|
+
@hosts.uniq!
|
|
126
|
+
[@hosts.freeze, @host_hash.freeze, @default_block, @default_host].freeze
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# Register hosts that can be returned. This is only needed if
|
|
130
|
+
# calling register with a block, where the block can return
|
|
131
|
+
# a value that doesn't match a host given to +to+ or +default+.
|
|
132
|
+
def register(*hosts)
|
|
133
|
+
@hosts = hosts
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# Treat all given hostnames as routing to the give host.
|
|
137
|
+
def to(host, *hostnames)
|
|
138
|
+
hostnames.each do |hostname|
|
|
139
|
+
@host_hash[hostname] = host
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# Register the default hostname. If a block is provided, it is
|
|
144
|
+
# called with the host if there is no match for one of the hostnames
|
|
145
|
+
# provided to +to+. If the block returns nil/false, the hostname
|
|
146
|
+
# given to this method is used.
|
|
147
|
+
def default(hostname, &block)
|
|
148
|
+
@default_host = hostname
|
|
149
|
+
@default_block = block
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
private_constant :DSL
|
|
153
|
+
|
|
154
|
+
module InstanceMethods
|
|
155
|
+
# Handle case where plugin is used without providing a block to
|
|
156
|
+
# +hosts.default+. This returns nil, ensuring that the hostname
|
|
157
|
+
# provided to +hosts.default+ will be used.
|
|
158
|
+
def _host_routing_default(_)
|
|
159
|
+
nil
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
module RequestMethods
|
|
164
|
+
private
|
|
165
|
+
|
|
166
|
+
# Cache the host to use in the host routing support, so the processing
|
|
167
|
+
# is only done once per request.
|
|
168
|
+
def _host_routing_host
|
|
169
|
+
@_host_routing_host ||= _get_host_routing_host
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
# Determine the host to use for the host routing support. Tries the
|
|
173
|
+
# following, in order:
|
|
174
|
+
#
|
|
175
|
+
# * An exact match for a hostname given in +hosts.to+
|
|
176
|
+
# * The return value of the +hosts.default+ block, if given
|
|
177
|
+
# * The default value provided in the +hosts.default+ call
|
|
178
|
+
def _get_host_routing_host
|
|
179
|
+
host = self.host || ""
|
|
180
|
+
|
|
181
|
+
roda_class.opts[:host_routing_hash][host] ||
|
|
182
|
+
scope._host_routing_default(host) ||
|
|
183
|
+
roda_class.opts[:host_routing_default_host]
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
register_plugin(:host_routing, HostRouting)
|
|
189
|
+
end
|
|
190
|
+
end
|
|
@@ -53,27 +53,28 @@ class Roda
|
|
|
53
53
|
# parse the request body as JSON. Ignore an empty request body.
|
|
54
54
|
def POST
|
|
55
55
|
env = @env
|
|
56
|
-
if post_params =
|
|
57
|
-
post_params
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
56
|
+
if post_params = env["roda.json_params"]
|
|
57
|
+
return post_params
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
unless (input = env["rack.input"]) && (content_type = self.content_type) && content_type.include?('json')
|
|
61
|
+
return super
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
str = _read_json_input(input)
|
|
65
|
+
return super if str.empty?
|
|
66
|
+
begin
|
|
67
|
+
json_params = parse_json(str)
|
|
68
|
+
rescue
|
|
69
|
+
roda_class.opts[:json_parser_error_handler].call(self)
|
|
70
|
+
end
|
|
66
71
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
end
|
|
71
|
-
env["roda.json_params"] = json_params
|
|
72
|
-
env["rack.request.form_input"] = input
|
|
73
|
-
json_params
|
|
74
|
-
else
|
|
75
|
-
super
|
|
72
|
+
wrap = roda_class.opts[:json_parser_wrap]
|
|
73
|
+
if wrap == :always || (wrap == :unless_hash && !json_params.is_a?(Hash))
|
|
74
|
+
json_params = {"_json"=>json_params}
|
|
76
75
|
end
|
|
76
|
+
env["roda.json_params"] = json_params
|
|
77
|
+
json_params
|
|
77
78
|
end
|
|
78
79
|
|
|
79
80
|
private
|
data/lib/roda/plugins/render.rb
CHANGED
|
@@ -118,6 +118,45 @@ class Roda
|
|
|
118
118
|
# have either +:template+, +:inline+, +:path+, or +:content+ (for +view+) as
|
|
119
119
|
# one of the keys.
|
|
120
120
|
#
|
|
121
|
+
# = Fixed Locals in Templates
|
|
122
|
+
#
|
|
123
|
+
# By default, you can pass any local variables to any templates. A separate
|
|
124
|
+
# template method is compiled for each combination of locals. This causes
|
|
125
|
+
# multiple issues:
|
|
126
|
+
#
|
|
127
|
+
# * It is inefficient, especially for large templates that are called with
|
|
128
|
+
# many combinations of locals.
|
|
129
|
+
# * It hides issues if unused local variable names are passed to the template
|
|
130
|
+
# * It does not support default values for local variables
|
|
131
|
+
# * It does not support required local variables
|
|
132
|
+
# * It does not support cases where you want to pass values via a keyword splat
|
|
133
|
+
# * It does not support named blocks
|
|
134
|
+
#
|
|
135
|
+
# If you are using Tilt 2.6+, you can used fixed locals in templates, by
|
|
136
|
+
# passing the appropriate options in :template_opts. For example, if you
|
|
137
|
+
# are using ERB templates, the recommended way to use the render plugin is to
|
|
138
|
+
# use the +:extract_fixed_locals+ and +:default_fixed_locals+ template options:
|
|
139
|
+
#
|
|
140
|
+
# plugin :render, template_opts: {extract_fixed_locals: true, default_fixed_locals: '()'}
|
|
141
|
+
#
|
|
142
|
+
# This will default templates to not allowing any local variables to be passed.
|
|
143
|
+
# If the template requires local variables, you can specify them using a magic
|
|
144
|
+
# comment in the template, such as:
|
|
145
|
+
#
|
|
146
|
+
# <%# locals(required_local:, optional_local: nil) %>
|
|
147
|
+
#
|
|
148
|
+
# The magic comment is used as method parameters when defining the compiled template
|
|
149
|
+
# method.
|
|
150
|
+
#
|
|
151
|
+
# For better debugging of issues with invalid keywords being passed to templates that
|
|
152
|
+
# have not been updated to support fixed locals, it can be helpful to set
|
|
153
|
+
# +:default_fixed_locals+ to use a single optional keyword argument
|
|
154
|
+
# <tt>'(_no_kw: nil)'</tt>. This makes the error message show which keywords
|
|
155
|
+
# were passed, instead of showing that the takes no arguments (if you use <tt>'()'</tt>),
|
|
156
|
+
# or that no keywords are accepted (if you pass <tt>(**nil)</tt>).
|
|
157
|
+
#
|
|
158
|
+
# See Tilt's documentation for more information regarding fixed locals.
|
|
159
|
+
#
|
|
121
160
|
# = Speeding Up Template Rendering
|
|
122
161
|
#
|
|
123
162
|
# The render/view method calls are optimized for usage with a single symbol/string
|
|
@@ -131,6 +170,21 @@ class Roda
|
|
|
131
170
|
# the hash, making sure the +:cache_key+ is unique to the template you are
|
|
132
171
|
# rendering.
|
|
133
172
|
#
|
|
173
|
+
# = Recommended +template_opts+
|
|
174
|
+
#
|
|
175
|
+
# Here are the recommended values of :template_opts for new applications (a couple
|
|
176
|
+
# are Erubi-specific and can be ignored if you are using other templates engines):
|
|
177
|
+
#
|
|
178
|
+
# plugin :render, template_opts: {
|
|
179
|
+
# scope_class: self, # Always uses current class as scope class for compiled templates
|
|
180
|
+
# freeze: true, # Freeze string literals in templates
|
|
181
|
+
# extract_fixed_locals: true, # Support fixed locals in templates
|
|
182
|
+
# default_fixed_locals: '()', # Default to templates not supporting local variables
|
|
183
|
+
# escape: true, # For Erubi templates, escapes <%= by default (use <%== for unescaped
|
|
184
|
+
# chain_appends: true, # For Erubi templates, improves performance
|
|
185
|
+
# skip_compiled_encoding_detection: true, # Unless you need encodings explicitly specified
|
|
186
|
+
# }
|
|
187
|
+
#
|
|
134
188
|
# = Accepting Template Blocks in Methods
|
|
135
189
|
#
|
|
136
190
|
# If you are used to Rails, you may be surprised that this type of template code
|
|
@@ -228,6 +282,19 @@ class Roda
|
|
|
228
282
|
([1, -2].include?(((compiled_method_arity = Tilt::Template.instance_method(:compiled_method).arity) rescue false)))
|
|
229
283
|
NO_CACHE = {:cache=>false}.freeze
|
|
230
284
|
COMPILED_METHOD_SUPPORT = RUBY_VERSION >= '2.3' && tilt_compiled_method_support && ENV['RODA_RENDER_COMPILED_METHOD_SUPPORT'] != 'no'
|
|
285
|
+
FIXED_LOCALS_COMPILED_METHOD_SUPPORT = COMPILED_METHOD_SUPPORT && Tilt::Template.method_defined?(:fixed_locals?)
|
|
286
|
+
|
|
287
|
+
if FIXED_LOCALS_COMPILED_METHOD_SUPPORT
|
|
288
|
+
def self.tilt_template_fixed_locals?(template)
|
|
289
|
+
template.fixed_locals?
|
|
290
|
+
end
|
|
291
|
+
# :nocov:
|
|
292
|
+
else
|
|
293
|
+
def self.tilt_template_fixed_locals?(template)
|
|
294
|
+
false
|
|
295
|
+
end
|
|
296
|
+
end
|
|
297
|
+
# :nocov:
|
|
231
298
|
|
|
232
299
|
if compiled_method_arity == -2
|
|
233
300
|
def self.tilt_template_compiled_method(template, locals_keys, scope_class)
|
|
@@ -385,6 +452,11 @@ class Roda
|
|
|
385
452
|
end
|
|
386
453
|
|
|
387
454
|
if COMPILED_METHOD_SUPPORT
|
|
455
|
+
# Whether the underlying template uses fixed locals.
|
|
456
|
+
def fixed_locals?
|
|
457
|
+
Render.tilt_template_fixed_locals?(@template)
|
|
458
|
+
end
|
|
459
|
+
|
|
388
460
|
# Compile a method in the given module with the given name that will
|
|
389
461
|
# call the compiled template method, updating the compiled template method
|
|
390
462
|
def define_compiled_method(roda_class, method_name, locals_keys=EMPTY_ARRAY)
|
|
@@ -403,6 +475,15 @@ class Roda
|
|
|
403
475
|
method_name
|
|
404
476
|
end
|
|
405
477
|
|
|
478
|
+
# Returns an appropriate value for the template method cache.
|
|
479
|
+
def define_compiled_method_cache_value(roda_class, method_name, locals_keys=EMPTY_ARRAY)
|
|
480
|
+
if compiled_method = define_compiled_method(roda_class, method_name, locals_keys)
|
|
481
|
+
[compiled_method, false].freeze
|
|
482
|
+
else
|
|
483
|
+
compiled_method
|
|
484
|
+
end
|
|
485
|
+
end
|
|
486
|
+
|
|
406
487
|
private
|
|
407
488
|
|
|
408
489
|
# Return the compiled method for the current template object.
|
|
@@ -422,7 +503,7 @@ class Roda
|
|
|
422
503
|
mod.send(:private, method_name)
|
|
423
504
|
end
|
|
424
505
|
|
|
425
|
-
|
|
506
|
+
_call_optimized_template_method([method_name, Render.tilt_template_fixed_locals?(template)], locals, &block)
|
|
426
507
|
end
|
|
427
508
|
end
|
|
428
509
|
end
|
|
@@ -489,17 +570,17 @@ class Roda
|
|
|
489
570
|
def _freeze_layout_method
|
|
490
571
|
if render_opts[:layout]
|
|
491
572
|
instance = allocate
|
|
573
|
+
# This needs to be called even if COMPILED_METHOD_SUPPORT is not set,
|
|
574
|
+
# in order for the precompile_templates plugin to work correctly.
|
|
492
575
|
instance.send(:retrieve_template, instance.send(:view_layout_opts, OPTS))
|
|
493
576
|
|
|
494
|
-
if COMPILED_METHOD_SUPPORT
|
|
495
|
-
if (layout_template = render_opts[:optimize_layout]) && !opts[:render][:optimized_layout_method_created]
|
|
577
|
+
if COMPILED_METHOD_SUPPORT && (layout_template = render_opts[:optimize_layout]) && !opts[:render][:optimized_layout_method_created]
|
|
496
578
|
instance.send(:retrieve_template, :template=>layout_template, :cache_key=>nil, :template_method_cache_key => :_roda_layout)
|
|
497
579
|
layout_method = opts[:render][:template_method_cache][:_roda_layout]
|
|
498
580
|
define_method(:_layout_method){layout_method}
|
|
499
581
|
private :_layout_method
|
|
500
582
|
alias_method(:_layout_method, :_layout_method)
|
|
501
583
|
opts[:render] = opts[:render].merge(:optimized_layout_method_created=>true)
|
|
502
|
-
end
|
|
503
584
|
end
|
|
504
585
|
end
|
|
505
586
|
end
|
|
@@ -509,9 +590,9 @@ class Roda
|
|
|
509
590
|
# Render the given template. See Render for details.
|
|
510
591
|
def render(template, opts = (no_opts = true; optimized_template = _cached_template_method(template); OPTS), &block)
|
|
511
592
|
if optimized_template
|
|
512
|
-
|
|
593
|
+
_call_optimized_template_method(optimized_template, OPTS, &block)
|
|
513
594
|
elsif !no_opts && opts.length == 1 && (locals = opts[:locals]) && (optimized_template = _optimized_render_method_for_locals(template, locals))
|
|
514
|
-
|
|
595
|
+
_call_optimized_template_method(optimized_template, locals, &block)
|
|
515
596
|
else
|
|
516
597
|
opts = render_template_opts(template, opts)
|
|
517
598
|
retrieve_template(opts).render((opts[:scope]||self), (opts[:locals]||OPTS), &block)
|
|
@@ -534,7 +615,7 @@ class Roda
|
|
|
534
615
|
# and use it if so. This way avoids the extra conditional and local variable
|
|
535
616
|
# assignments in the next section.
|
|
536
617
|
if layout_method = _layout_method
|
|
537
|
-
return
|
|
618
|
+
return _call_optimized_template_method(layout_method, OPTS){content}
|
|
538
619
|
end
|
|
539
620
|
|
|
540
621
|
# If we have an optimized template method but no optimized layout method, create the
|
|
@@ -543,7 +624,7 @@ class Roda
|
|
|
543
624
|
if layout_template = self.class.opts[:render][:optimize_layout]
|
|
544
625
|
retrieve_template(:template=>layout_template, :cache_key=>nil, :template_method_cache_key => :_roda_layout)
|
|
545
626
|
if layout_method = _layout_method
|
|
546
|
-
return
|
|
627
|
+
return _call_optimized_template_method(layout_method, OPTS){content}
|
|
547
628
|
end
|
|
548
629
|
end
|
|
549
630
|
else
|
|
@@ -594,37 +675,43 @@ class Roda
|
|
|
594
675
|
def _optimized_render_method_for_locals(template, locals)
|
|
595
676
|
return unless method_cache = render_opts[:template_method_cache]
|
|
596
677
|
|
|
597
|
-
|
|
598
|
-
key = [:_render_locals, template, locals_keys]
|
|
599
|
-
|
|
600
|
-
optimized_template = case template
|
|
678
|
+
case template
|
|
601
679
|
when String, Symbol
|
|
602
|
-
|
|
680
|
+
key = [:_render_locals, template]
|
|
681
|
+
if optimized_template = _cached_template_method_lookup(method_cache, key)
|
|
682
|
+
# Fixed locals case
|
|
683
|
+
return optimized_template
|
|
684
|
+
end
|
|
685
|
+
|
|
686
|
+
locals_keys = locals.keys.sort
|
|
687
|
+
key << locals_keys
|
|
688
|
+
if optimized_template = _cached_template_method_lookup(method_cache, key)
|
|
689
|
+
# Regular locals case
|
|
690
|
+
return optimized_template
|
|
691
|
+
end
|
|
603
692
|
else
|
|
604
693
|
return
|
|
605
694
|
end
|
|
606
695
|
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
method_name = :"_roda_template_locals_#{self.class.object_id}_#{method_cache_key}"
|
|
696
|
+
if method_cache_key = _cached_template_method_key(key)
|
|
697
|
+
template_obj = retrieve_template(render_template_opts(template, NO_CACHE))
|
|
698
|
+
key.pop if fixed_locals = Render.tilt_template_fixed_locals?(template_obj)
|
|
699
|
+
key.freeze
|
|
700
|
+
method_cache_key.freeze
|
|
701
|
+
method_name = :"_roda_template_locals_#{self.class.object_id}_#{method_cache_key}"
|
|
614
702
|
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
703
|
+
method_cache[method_cache_key] = case template_obj
|
|
704
|
+
when Render::TemplateMtimeWrapper
|
|
705
|
+
template_obj.define_compiled_method_cache_value(self.class, method_name, locals_keys)
|
|
706
|
+
else
|
|
707
|
+
begin
|
|
708
|
+
unbound_method = Render.tilt_template_compiled_method(template_obj, locals_keys, self.class)
|
|
709
|
+
rescue ::NotImplementedError
|
|
710
|
+
false
|
|
618
711
|
else
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
false
|
|
623
|
-
else
|
|
624
|
-
self.class::RodaCompiledTemplates.send(:define_method, method_name, unbound_method)
|
|
625
|
-
self.class::RodaCompiledTemplates.send(:private, method_name)
|
|
626
|
-
method_name
|
|
627
|
-
end
|
|
712
|
+
self.class::RodaCompiledTemplates.send(:define_method, method_name, unbound_method)
|
|
713
|
+
self.class::RodaCompiledTemplates.send(:private, method_name)
|
|
714
|
+
[method_name, fixed_locals].freeze
|
|
628
715
|
end
|
|
629
716
|
end
|
|
630
717
|
end
|
|
@@ -634,11 +721,47 @@ class Roda
|
|
|
634
721
|
# a single argument is passed to view.
|
|
635
722
|
def _optimized_view_content(template)
|
|
636
723
|
if optimized_template = _cached_template_method(template)
|
|
637
|
-
|
|
724
|
+
_call_optimized_template_method(optimized_template, OPTS)
|
|
638
725
|
elsif template.is_a?(Hash) && template.length == 1
|
|
639
726
|
template[:content]
|
|
640
727
|
end
|
|
641
728
|
end
|
|
729
|
+
|
|
730
|
+
if RUBY_VERSION >= '3'
|
|
731
|
+
class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
|
|
732
|
+
def _call_optimized_template_method((meth, fixed_locals), locals, &block)
|
|
733
|
+
if fixed_locals
|
|
734
|
+
send(meth, **locals, &block)
|
|
735
|
+
else
|
|
736
|
+
send(meth, locals, &block)
|
|
737
|
+
end
|
|
738
|
+
end
|
|
739
|
+
RUBY
|
|
740
|
+
# :nocov:
|
|
741
|
+
elsif RUBY_VERSION >= '2'
|
|
742
|
+
class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
|
|
743
|
+
def _call_optimized_template_method((meth, fixed_locals), locals, &block)
|
|
744
|
+
if fixed_locals
|
|
745
|
+
if locals.empty?
|
|
746
|
+
send(meth, &block)
|
|
747
|
+
else
|
|
748
|
+
send(meth, **locals, &block)
|
|
749
|
+
end
|
|
750
|
+
else
|
|
751
|
+
send(meth, locals, &block)
|
|
752
|
+
end
|
|
753
|
+
end
|
|
754
|
+
RUBY
|
|
755
|
+
else
|
|
756
|
+
# Call the optimized template method. This is designed to be used with the
|
|
757
|
+
# method cache, which caches the method name and whether the method uses
|
|
758
|
+
# fixed locals. Methods with fixed locals need to be called with a keyword
|
|
759
|
+
# splat.
|
|
760
|
+
def _call_optimized_template_method((meth, fixed_locals), locals, &block)
|
|
761
|
+
send(meth, locals, &block)
|
|
762
|
+
end
|
|
763
|
+
end
|
|
764
|
+
# :nocov:
|
|
642
765
|
else
|
|
643
766
|
def _cached_template_method(_)
|
|
644
767
|
nil
|
|
@@ -648,10 +771,6 @@ class Roda
|
|
|
648
771
|
nil
|
|
649
772
|
end
|
|
650
773
|
|
|
651
|
-
def _layout_method
|
|
652
|
-
nil
|
|
653
|
-
end
|
|
654
|
-
|
|
655
774
|
def _optimized_render_method_for_locals(_, _)
|
|
656
775
|
nil
|
|
657
776
|
end
|
|
@@ -661,7 +780,6 @@ class Roda
|
|
|
661
780
|
end
|
|
662
781
|
end
|
|
663
782
|
|
|
664
|
-
|
|
665
783
|
# Convert template options to single hash when rendering templates using render.
|
|
666
784
|
def render_template_opts(template, opts)
|
|
667
785
|
parse_template_opts(template, opts)
|
|
@@ -772,7 +890,7 @@ class Roda
|
|
|
772
890
|
|
|
773
891
|
if define_compiled_method
|
|
774
892
|
method_name = :"_roda_template_#{self.class.object_id}_#{method_cache_key}"
|
|
775
|
-
method_cache[method_cache_key] = template.
|
|
893
|
+
method_cache[method_cache_key] = template.define_compiled_method_cache_value(self.class, method_name)
|
|
776
894
|
end
|
|
777
895
|
else
|
|
778
896
|
template = self.class.create_template(opts, template_opts)
|
|
@@ -786,7 +904,7 @@ class Roda
|
|
|
786
904
|
method_name = :"_roda_template_#{self.class.object_id}_#{method_cache_key}"
|
|
787
905
|
self.class::RodaCompiledTemplates.send(:define_method, method_name, unbound_method)
|
|
788
906
|
self.class::RodaCompiledTemplates.send(:private, method_name)
|
|
789
|
-
method_cache[method_cache_key] = method_name
|
|
907
|
+
method_cache[method_cache_key] = [method_name, Render.tilt_template_fixed_locals?(template)].freeze
|
|
790
908
|
end
|
|
791
909
|
end
|
|
792
910
|
end
|
|
@@ -50,17 +50,36 @@ class Roda
|
|
|
50
50
|
# Set a compiled path on the created template, if the path for
|
|
51
51
|
# the template is in one of the allowed_views.
|
|
52
52
|
def create_template(opts, template_opts)
|
|
53
|
-
|
|
54
|
-
return template if opts[:template_block]
|
|
53
|
+
return super if opts[:template_block]
|
|
55
54
|
|
|
56
55
|
path = File.expand_path(opts[:path])
|
|
56
|
+
compiled_path = nil
|
|
57
57
|
(self.opts[:render_coverage_strip_paths] || render_opts[:allowed_paths]).each do |dir|
|
|
58
58
|
if path.start_with?(dir + '/')
|
|
59
|
-
|
|
59
|
+
compiled_path = File.join(self.opts[:render_coverage_dir], path[dir.length+1, 10000000].gsub('/', '-'))
|
|
60
60
|
break
|
|
61
61
|
end
|
|
62
62
|
end
|
|
63
63
|
|
|
64
|
+
# For Tilt 2.6+, when using :scope_class and fixed locals, must provide
|
|
65
|
+
# compiled path as option, since compilation happens during initalization
|
|
66
|
+
# in that case. This option should be ignored if the template does not
|
|
67
|
+
# support it, but some template class may break if the option is not
|
|
68
|
+
# handled, so for compatibility, only set the method if Tilt::Template
|
|
69
|
+
# will handle it.
|
|
70
|
+
if compiled_path && Tilt::Template.method_defined?(:fixed_locals?)
|
|
71
|
+
template_opts = template_opts.dup
|
|
72
|
+
template_opts[:compiled_path] = compiled_path
|
|
73
|
+
compiled_path = nil
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
template = super
|
|
77
|
+
|
|
78
|
+
# Set compiled path for template when using older tilt versions.
|
|
79
|
+
# :nocov:
|
|
80
|
+
template.compiled_path = compiled_path if compiled_path
|
|
81
|
+
# :nocov:
|
|
82
|
+
|
|
64
83
|
template
|
|
65
84
|
end
|
|
66
85
|
end
|
data/lib/roda/response.rb
CHANGED
|
@@ -46,8 +46,6 @@ class Roda
|
|
|
46
46
|
|
|
47
47
|
# Instance methods for RodaResponse
|
|
48
48
|
module ResponseMethods
|
|
49
|
-
DEFAULT_HEADERS = {RodaResponseHeaders::CONTENT_TYPE => "text/html".freeze}.freeze
|
|
50
|
-
|
|
51
49
|
# The body for the current response.
|
|
52
50
|
attr_reader :body
|
|
53
51
|
|
|
@@ -179,11 +177,15 @@ class Roda
|
|
|
179
177
|
private
|
|
180
178
|
|
|
181
179
|
if defined?(Rack::Headers) && Rack::Headers.is_a?(Class)
|
|
180
|
+
DEFAULT_HEADERS = Rack::Headers[{RodaResponseHeaders::CONTENT_TYPE => "text/html".freeze}].freeze
|
|
181
|
+
|
|
182
182
|
# Use Rack::Headers for headers by default on Rack 3
|
|
183
183
|
def _initialize_headers
|
|
184
184
|
Rack::Headers.new
|
|
185
185
|
end
|
|
186
186
|
else
|
|
187
|
+
DEFAULT_HEADERS = {RodaResponseHeaders::CONTENT_TYPE => "text/html".freeze}.freeze
|
|
188
|
+
|
|
187
189
|
# Use plain hash for headers by default on Rack 1-2
|
|
188
190
|
def _initialize_headers
|
|
189
191
|
{}
|
data/lib/roda/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: roda
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 3.
|
|
4
|
+
version: 3.88.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jeremy Evans
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: bin
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 2025-01-14 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
12
|
- !ruby/object:Gem::Dependency
|
|
14
13
|
name: rack
|
|
@@ -150,7 +149,6 @@ dependencies:
|
|
|
150
149
|
- - ">="
|
|
151
150
|
- !ruby/object:Gem::Version
|
|
152
151
|
version: '0'
|
|
153
|
-
description:
|
|
154
152
|
email:
|
|
155
153
|
- code@jeremyevans.net
|
|
156
154
|
executables: []
|
|
@@ -225,6 +223,7 @@ files:
|
|
|
225
223
|
- lib/roda/plugins/hmac_paths.rb
|
|
226
224
|
- lib/roda/plugins/hooks.rb
|
|
227
225
|
- lib/roda/plugins/host_authorization.rb
|
|
226
|
+
- lib/roda/plugins/host_routing.rb
|
|
228
227
|
- lib/roda/plugins/hsts.rb
|
|
229
228
|
- lib/roda/plugins/indifferent_params.rb
|
|
230
229
|
- lib/roda/plugins/inject_erb.rb
|
|
@@ -312,7 +311,6 @@ metadata:
|
|
|
312
311
|
documentation_uri: https://roda.jeremyevans.net/documentation.html
|
|
313
312
|
mailing_list_uri: https://github.com/jeremyevans/roda/discussions
|
|
314
313
|
source_code_uri: https://github.com/jeremyevans/roda
|
|
315
|
-
post_install_message:
|
|
316
314
|
rdoc_options: []
|
|
317
315
|
require_paths:
|
|
318
316
|
- lib
|
|
@@ -327,8 +325,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
327
325
|
- !ruby/object:Gem::Version
|
|
328
326
|
version: '0'
|
|
329
327
|
requirements: []
|
|
330
|
-
rubygems_version: 3.
|
|
331
|
-
signing_key:
|
|
328
|
+
rubygems_version: 3.6.2
|
|
332
329
|
specification_version: 4
|
|
333
330
|
summary: Routing tree web toolkit
|
|
334
331
|
test_files: []
|