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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2229b19a7b47a63c8f953c9a225f094d9b6c1120893629e30fea16fe189c8462
4
- data.tar.gz: 341a5222c9bdf0cdc1aa56b654dd1accdce94257a5f9a756c8a8d37cf5bd5af8
3
+ metadata.gz: bc61123e82d5731f6459164ff0e6305115008d22f59607b471021f6d5586646d
4
+ data.tar.gz: 3bce436fb87890ef4d593ea97792044f493b3328c6c43cf7d363463f1767050c
5
5
  SHA512:
6
- metadata.gz: c996fa5a43603f429fb061c84585a84dbe97719032893f3ae1fff22dcd8056fb1c71fb8fa103ea46fca1a50cbaf6156139e9b1ee03ac1151e7a33d576d08b724
7
- data.tar.gz: 8eff9c58e5dc39d40e18445bb33a4dcba019034975c110382a9f143021ddaf6920b8e783cc031be07f431a295e2b973ed750cf9f71e064c0796822e51faa6145
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(&Proc.new) if block_given?
85
+ mod.module_eval(&block) if block
86
86
  end
87
87
 
88
88
  mod
@@ -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, OPTS))
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, OPTS)
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 |_, &block|
295
+ lambda do |locals, &block|
295
296
  if template.modified?
296
- mod.send(:define_method, method_name, template.send(:compiled_method, OPTS))
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, OPTS, &block)
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, OPTS)
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 as = opts.has_key?(:local)
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
 
@@ -4,7 +4,7 @@ class Roda
4
4
  RodaMajorVersion = 3
5
5
 
6
6
  # The minor version of Roda, updated for new feature releases of Roda.
7
- RodaMinorVersion = 23
7
+ RodaMinorVersion = 24
8
8
 
9
9
  # The patch version of Roda, updated only for bug fixes from the last
10
10
  # feature release.
@@ -1,41 +1,82 @@
1
1
  require_relative "../spec_helper"
2
2
 
3
3
  begin
4
- require 'tilt/erb'
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
- it "calls render with each argument, returning joined string with all results" do
10
- app(:bare) do
11
- plugin :render_each
12
- def render_template(t, opts)
13
- "r#{t}#{opts[:locals][:foo] if opts[:locals]}#{opts[:bar]}#{opts[:locals][:bar] if opts[:locals]} "
14
- end
15
-
16
- route do |r|
17
- r.root do
18
- render_each([1,2,3], :foo)
19
- end
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
- r.is 'a' do
22
- render_each([1,2,3], :bar, :local=>:foo, :bar=>4)
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
- r.is 'b' do
26
- render_each([1,2,3], :bar, :local=>nil)
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
- r.is 'c' do
30
- render_each([1,2,3], :bar, :locals=>{:foo=>4})
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
@@ -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.23.0
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-08-13 00:00:00.000000000 Z
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