roda 3.23.0 → 3.24.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 +6 -0
- data/doc/release_notes/3.24.0.txt +14 -0
- data/lib/roda/plugins/module_include.rb +2 -2
- data/lib/roda/plugins/render.rb +12 -11
- data/lib/roda/plugins/render_each.rb +119 -2
- data/lib/roda/version.rb +1 -1
- data/spec/plugin/render_each_spec.rb +64 -23
- data/spec/views/each.str +1 -0
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bc61123e82d5731f6459164ff0e6305115008d22f59607b471021f6d5586646d
|
4
|
+
data.tar.gz: 3bce436fb87890ef4d593ea97792044f493b3328c6c43cf7d363463f1767050c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 11141195b070ccfda0ddafb7b54a81402cddf003b1256ae95cf1c3578f5afdf50afb65ac4449336f9925280901d9e40ac13329fb29435e12bd75d49b66f4130e
|
7
|
+
data.tar.gz: c561ade16056cd4a6b004b58397fd8ed0c38d7b65b3be116f40553dd8b14af04fbbec7f41dd341c63cbdf48a897075ca97557c2f46710d7c4d09ac3ce9b2e12a
|
data/CHANGELOG
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
= 3.24.0 (2019-09-13)
|
2
|
+
|
3
|
+
* Fix Proc.new warning in module_include plugin on Ruby 2.7+ (jeremyevans)
|
4
|
+
|
5
|
+
* Improve render_each performance by calling compiled template methods directly (jeremyevans)
|
6
|
+
|
1
7
|
= 3.23.0 (2019-08-13)
|
2
8
|
|
3
9
|
* Make roda/session_middleware work if type_routing plugin is loaded into Roda itself (jeremyevans) (#169)
|
@@ -0,0 +1,14 @@
|
|
1
|
+
= Improvements
|
2
|
+
|
3
|
+
* The performance of the render_each plugin has been dramatically
|
4
|
+
improved by calling compiled template methods directly. For a simple
|
5
|
+
template, render_each performance with a single object can be about
|
6
|
+
2x faster, and render_each performance for 100 objects can be 3x
|
7
|
+
(cache: false) to 9x (cache: true) faster.
|
8
|
+
|
9
|
+
This optimization can be used if no options are provided to
|
10
|
+
render_each, or if :local and/or :locals options are provided. Use
|
11
|
+
of other options will disable this optimization.
|
12
|
+
|
13
|
+
* The module_include plugin no longer calls Proc.new without a
|
14
|
+
block, fixing a warning on Ruby 2.7.
|
@@ -62,7 +62,7 @@ class Roda
|
|
62
62
|
private
|
63
63
|
|
64
64
|
# Backbone of the request_module and response_module methods.
|
65
|
-
def module_include(type, mod)
|
65
|
+
def module_include(type, mod, &block)
|
66
66
|
if type == :response
|
67
67
|
klass = self::RodaResponse
|
68
68
|
iv = :@response_module
|
@@ -82,7 +82,7 @@ class Roda
|
|
82
82
|
klass.send(:include, mod)
|
83
83
|
end
|
84
84
|
|
85
|
-
mod.module_eval(&
|
85
|
+
mod.module_eval(&block) if block
|
86
86
|
end
|
87
87
|
|
88
88
|
mod
|
data/lib/roda/plugins/render.rb
CHANGED
@@ -224,7 +224,8 @@ class Roda
|
|
224
224
|
|
225
225
|
# Wrapper object for the Tilt template, that checks the modified
|
226
226
|
# time of the template file, and rebuilds the template if the
|
227
|
-
# template file has been modified.
|
227
|
+
# template file has been modified. This is an internal class and
|
228
|
+
# the API is subject to change at any time.
|
228
229
|
class TemplateMtimeWrapper
|
229
230
|
def initialize(template_class, path, *template_args)
|
230
231
|
@template_class = template_class
|
@@ -263,17 +264,17 @@ class Roda
|
|
263
264
|
if COMPILED_METHOD_SUPPORT
|
264
265
|
# Compile a method in the given module with the given name that will
|
265
266
|
# call the compiled template method, updating the compiled template method
|
266
|
-
def define_compiled_method(roda_class, method_name)
|
267
|
+
def define_compiled_method(roda_class, method_name, locals_keys=EMPTY_ARRAY)
|
267
268
|
mod = roda_class::RodaCompiledTemplates
|
268
269
|
internal_method_name = :"_#{method_name}"
|
269
270
|
begin
|
270
|
-
mod.send(:define_method, internal_method_name, send(:compiled_method,
|
271
|
+
mod.send(:define_method, internal_method_name, send(:compiled_method, locals_keys))
|
271
272
|
rescue ::NotImplementedError
|
272
273
|
return false
|
273
274
|
end
|
274
275
|
|
275
276
|
mod.send(:private, internal_method_name)
|
276
|
-
mod.send(:define_method, method_name, &compiled_method_lambda(self, roda_class, internal_method_name))
|
277
|
+
mod.send(:define_method, method_name, &compiled_method_lambda(self, roda_class, internal_method_name, locals_keys))
|
277
278
|
mod.send(:private, method_name)
|
278
279
|
|
279
280
|
method_name
|
@@ -282,22 +283,22 @@ class Roda
|
|
282
283
|
private
|
283
284
|
|
284
285
|
# Return the compiled method for the current template object.
|
285
|
-
def compiled_method(
|
286
|
-
@template.send(:compiled_method,
|
286
|
+
def compiled_method(locals_keys=EMPTY_ARRAY)
|
287
|
+
@template.send(:compiled_method, locals_keys)
|
287
288
|
end
|
288
289
|
|
289
290
|
# Return the lambda used to define the compiled template method. This
|
290
291
|
# is separated into its own method so the lambda does not capture any
|
291
292
|
# unnecessary local variables
|
292
|
-
def compiled_method_lambda(template, roda_class, method_name)
|
293
|
+
def compiled_method_lambda(template, roda_class, method_name, locals_keys=EMPTY_ARRAY)
|
293
294
|
mod = roda_class::RodaCompiledTemplates
|
294
|
-
lambda do |
|
295
|
+
lambda do |locals, &block|
|
295
296
|
if template.modified?
|
296
|
-
mod.send(:define_method, method_name, template.send(:compiled_method,
|
297
|
+
mod.send(:define_method, method_name, template.send(:compiled_method, locals_keys))
|
297
298
|
mod.send(:private, method_name)
|
298
299
|
end
|
299
300
|
|
300
|
-
send(method_name,
|
301
|
+
send(method_name, locals, &block)
|
301
302
|
end
|
302
303
|
end
|
303
304
|
end
|
@@ -524,7 +525,7 @@ class Roda
|
|
524
525
|
|
525
526
|
if define_compiled_method && cache != false
|
526
527
|
begin
|
527
|
-
unbound_method = template.send(:compiled_method,
|
528
|
+
unbound_method = template.send(:compiled_method, EMPTY_ARRAY)
|
528
529
|
rescue ::NotImplementedError
|
529
530
|
method_cache[method_cache_key] = false
|
530
531
|
else
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen-string-literal: true
|
2
2
|
|
3
|
+
require_relative 'render'
|
4
|
+
|
3
5
|
#
|
4
6
|
class Roda
|
5
7
|
module RodaPlugins
|
@@ -32,6 +34,10 @@ class Roda
|
|
32
34
|
app.plugin :render
|
33
35
|
end
|
34
36
|
|
37
|
+
COMPILED_METHOD_SUPPORT = Render::COMPILED_METHOD_SUPPORT
|
38
|
+
NO_CACHE = {:cache=>false}.freeze
|
39
|
+
ALLOWED_KEYS = [:locals, :local].freeze
|
40
|
+
|
35
41
|
module InstanceMethods
|
36
42
|
# For each value in enum, render the given template using the
|
37
43
|
# given opts. The template and options hash are passed to +render+.
|
@@ -39,11 +45,43 @@ class Roda
|
|
39
45
|
# :local :: The local variable to use for the current enum value
|
40
46
|
# inside the template. An explicit +nil+ value does not
|
41
47
|
# set a local variable. If not set, uses the template name.
|
42
|
-
def render_each(enum, template, opts=OPTS)
|
43
|
-
if
|
48
|
+
def render_each(enum, template, opts=(no_opts = true; optimized_template = _cached_render_each_template_method(template); OPTS))
|
49
|
+
if optimized_template
|
50
|
+
as = template.to_s.to_sym
|
51
|
+
return enum.map{|v| send(optimized_template, as=>v)}.join
|
52
|
+
elsif opts.has_key?(:local)
|
44
53
|
as = opts[:local]
|
45
54
|
else
|
46
55
|
as = template.to_s.to_sym
|
56
|
+
|
57
|
+
if COMPILED_METHOD_SUPPORT &&
|
58
|
+
no_opts &&
|
59
|
+
optimized_template.nil? &&
|
60
|
+
(method_cache = render_opts[:template_method_cache]) &&
|
61
|
+
(method_cache_key = _cached_template_method_key([:_render_each, template]))
|
62
|
+
|
63
|
+
template_obj = retrieve_template(render_template_opts(template, NO_CACHE))
|
64
|
+
method_name = :"_roda_render_each_#{self.class.object_id}_#{method_cache_key}"
|
65
|
+
|
66
|
+
case template_obj
|
67
|
+
when Render::TemplateMtimeWrapper
|
68
|
+
optimized_template = method_cache[method_cache_key] = template_obj.define_compiled_method(self.class, method_name, [as])
|
69
|
+
else
|
70
|
+
begin
|
71
|
+
unbound_method = template_obj.send(:compiled_method, [as])
|
72
|
+
rescue ::NotImplementedError
|
73
|
+
method_cache[method_cache_key] = false
|
74
|
+
else
|
75
|
+
self.class::RodaCompiledTemplates.send(:define_method, method_name, unbound_method)
|
76
|
+
self.class::RodaCompiledTemplates.send(:private, method_name)
|
77
|
+
optimized_template = method_cache[method_cache_key] = method_name
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
if optimized_template
|
82
|
+
return enum.map{|v| send(optimized_template, as=>v)}.join
|
83
|
+
end
|
84
|
+
end
|
47
85
|
end
|
48
86
|
|
49
87
|
if as
|
@@ -55,11 +93,90 @@ class Roda
|
|
55
93
|
end
|
56
94
|
end
|
57
95
|
|
96
|
+
if COMPILED_METHOD_SUPPORT &&
|
97
|
+
!no_opts &&
|
98
|
+
as &&
|
99
|
+
(opts.keys - ALLOWED_KEYS).empty? &&
|
100
|
+
(method_cache = render_opts[:template_method_cache])
|
101
|
+
|
102
|
+
locals_keys = (locals.keys << as).sort
|
103
|
+
key = [:_render_each, template, locals_keys]
|
104
|
+
|
105
|
+
optimized_template = case template
|
106
|
+
when String, Symbol
|
107
|
+
_cached_template_method_lookup(method_cache, key)
|
108
|
+
else
|
109
|
+
false
|
110
|
+
end
|
111
|
+
|
112
|
+
case optimized_template
|
113
|
+
when Symbol
|
114
|
+
return enum.map do |v|
|
115
|
+
locals[as] = v
|
116
|
+
send(optimized_template, locals)
|
117
|
+
end.join
|
118
|
+
when false
|
119
|
+
# nothing
|
120
|
+
else
|
121
|
+
if method_cache_key = _cached_template_method_key(key)
|
122
|
+
template_obj = retrieve_template(render_template_opts(template, NO_CACHE))
|
123
|
+
method_name = :"_roda_render_each_#{self.class.object_id}_#{method_cache_key}"
|
124
|
+
|
125
|
+
case template_obj
|
126
|
+
when Render::TemplateMtimeWrapper
|
127
|
+
optimized_template = method_cache[method_cache_key] = template_obj.define_compiled_method(self.class, method_name, locals_keys)
|
128
|
+
else
|
129
|
+
begin
|
130
|
+
unbound_method = template_obj.send(:compiled_method, locals_keys)
|
131
|
+
rescue ::NotImplementedError
|
132
|
+
method_cache[method_cache_key] = false
|
133
|
+
else
|
134
|
+
self.class::RodaCompiledTemplates.send(:define_method, method_name, unbound_method)
|
135
|
+
self.class::RodaCompiledTemplates.send(:private, method_name)
|
136
|
+
optimized_template = method_cache[method_cache_key] = method_name
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
if optimized_template
|
141
|
+
return enum.map do |v|
|
142
|
+
locals[as] = v
|
143
|
+
send(optimized_template, locals)
|
144
|
+
end.join
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
58
150
|
enum.map do |v|
|
59
151
|
locals[as] = v if as
|
60
152
|
render_template(template, opts)
|
61
153
|
end.join
|
62
154
|
end
|
155
|
+
|
156
|
+
private
|
157
|
+
|
158
|
+
if COMPILED_METHOD_SUPPORT
|
159
|
+
# If compiled method support is enabled in the render plugin, return the
|
160
|
+
# method name to call to render the template. Return false if not given
|
161
|
+
# a string or symbol, or if compiled method support for this template has
|
162
|
+
# been explicitly disabled. Otherwise return nil.
|
163
|
+
def _cached_render_each_template_method(template)
|
164
|
+
case template
|
165
|
+
when String, Symbol
|
166
|
+
if (method_cache = render_opts[:template_method_cache])
|
167
|
+
_cached_template_method_lookup(method_cache, [:_render_each, template])
|
168
|
+
end
|
169
|
+
else
|
170
|
+
false
|
171
|
+
end
|
172
|
+
end
|
173
|
+
else
|
174
|
+
# :nocov:
|
175
|
+
def _cached_render_each_template_method(template)
|
176
|
+
nil
|
177
|
+
end
|
178
|
+
# :nocov:
|
179
|
+
end
|
63
180
|
end
|
64
181
|
end
|
65
182
|
|
data/lib/roda/version.rb
CHANGED
@@ -1,41 +1,82 @@
|
|
1
1
|
require_relative "../spec_helper"
|
2
2
|
|
3
3
|
begin
|
4
|
-
require 'tilt
|
4
|
+
require 'tilt'
|
5
|
+
require 'tilt/string'
|
6
|
+
require 'tilt/rdoc'
|
7
|
+
require_relative '../../lib/roda/plugins/render'
|
5
8
|
rescue LoadError
|
6
9
|
warn "tilt not installed, skipping render_each plugin test"
|
7
10
|
else
|
8
11
|
describe "render_each plugin" do
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
12
|
+
[true, false].each do |cache|
|
13
|
+
it "calls render with each argument, returning joined string with all results in cache: #{cache} mode" do
|
14
|
+
app(:bare) do
|
15
|
+
plugin :render, :views=>'spec/views', :engine=>'str', :cache=>cache
|
16
|
+
plugin :render_each
|
17
|
+
|
18
|
+
o = Object.new
|
19
|
+
def o.to_s; 'each' end
|
20
|
+
|
21
|
+
route do |r|
|
22
|
+
r.root do
|
23
|
+
render_each([1,2,3], :each)
|
24
|
+
end
|
25
|
+
|
26
|
+
r.is 'a' do
|
27
|
+
render_each([1,2,3], :each, :local=>:foo, :bar=>4)
|
28
|
+
end
|
29
|
+
|
30
|
+
r.is 'b' do
|
31
|
+
render_each([1,2,3], :each, :local=>nil)
|
32
|
+
end
|
33
|
+
|
34
|
+
r.is 'c' do
|
35
|
+
render_each([1,2,3], :each, :locals=>{:foo=>4})
|
36
|
+
end
|
37
|
+
|
38
|
+
r.is 'd' do
|
39
|
+
render_each([1,2,3], {:template=>:each}, :local=>:each)
|
40
|
+
end
|
20
41
|
|
21
|
-
|
22
|
-
|
42
|
+
r.is 'e' do
|
43
|
+
render_each([1,2,3], o)
|
44
|
+
end
|
23
45
|
end
|
46
|
+
end
|
47
|
+
|
48
|
+
3.times do
|
49
|
+
body.must_equal "r-1-\nr-2-\nr-3-\n"
|
50
|
+
body("/a").must_equal "r--1\nr--2\nr--3\n"
|
51
|
+
body("/b").must_equal "r--\nr--\nr--\n"
|
52
|
+
body("/c").must_equal "r-1-4\nr-2-4\nr-3-4\n"
|
53
|
+
body("/d").must_equal "r-1-\nr-2-\nr-3-\n"
|
54
|
+
body("/e").must_equal "r-1-\nr-2-\nr-3-\n"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
if Roda::RodaPlugins::Render::COMPILED_METHOD_SUPPORT
|
59
|
+
it "calls render with each argument, handling template engines that don't support compilation in cache: #{cache} mode" do
|
60
|
+
app(:bare) do
|
61
|
+
plugin :render, :views=>'spec/views', :engine=>'rdoc', :cache=>cache
|
62
|
+
plugin :render_each
|
24
63
|
|
25
|
-
|
26
|
-
|
64
|
+
route do |r|
|
65
|
+
r.root do
|
66
|
+
render_each([1], :a)
|
67
|
+
end
|
68
|
+
r.is 'a' do
|
69
|
+
render_each([1], :a, :local=>:b)
|
70
|
+
end
|
71
|
+
end
|
27
72
|
end
|
28
73
|
|
29
|
-
|
30
|
-
|
74
|
+
3.times do
|
75
|
+
body.strip.must_equal "<p># a # * b</p>"
|
76
|
+
body('/a').strip.must_equal "<p># a # * b</p>"
|
31
77
|
end
|
32
78
|
end
|
33
79
|
end
|
34
|
-
|
35
|
-
body.must_equal 'rfoo1 rfoo2 rfoo3 '
|
36
|
-
body("/a").must_equal 'rbar14 rbar24 rbar34 '
|
37
|
-
body("/b").must_equal 'rbar rbar rbar '
|
38
|
-
body("/c").must_equal 'rbar41 rbar42 rbar43 '
|
39
80
|
end
|
40
81
|
end
|
41
82
|
end
|
data/spec/views/each.str
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
r-#{each if local_variables.include?(:each)}-#{foo if local_variables.include?(:foo)}
|
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.24.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: 2019-
|
11
|
+
date: 2019-09-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rack
|
@@ -220,6 +220,7 @@ extra_rdoc_files:
|
|
220
220
|
- doc/release_notes/3.21.0.txt
|
221
221
|
- doc/release_notes/3.22.0.txt
|
222
222
|
- doc/release_notes/3.23.0.txt
|
223
|
+
- doc/release_notes/3.24.0.txt
|
223
224
|
files:
|
224
225
|
- CHANGELOG
|
225
226
|
- MIT-LICENSE
|
@@ -279,6 +280,7 @@ files:
|
|
279
280
|
- doc/release_notes/3.21.0.txt
|
280
281
|
- doc/release_notes/3.22.0.txt
|
281
282
|
- doc/release_notes/3.23.0.txt
|
283
|
+
- doc/release_notes/3.24.0.txt
|
282
284
|
- doc/release_notes/3.3.0.txt
|
283
285
|
- doc/release_notes/3.4.0.txt
|
284
286
|
- doc/release_notes/3.5.0.txt
|
@@ -516,6 +518,7 @@ files:
|
|
516
518
|
- spec/views/comp_layout.erb
|
517
519
|
- spec/views/comp_test.erb
|
518
520
|
- spec/views/content-yield.erb
|
521
|
+
- spec/views/each.str
|
519
522
|
- spec/views/home.erb
|
520
523
|
- spec/views/home.str
|
521
524
|
- spec/views/iv.erb
|