roda 3.21.0 → 3.22.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 34a644ccaa69bc1913eb1f54315437ce4b215bd5925e82f720d0fda007895224
4
- data.tar.gz: 9a85847e46720f4fa2f7a050febd5b0f6ef7db528c8fbd09836113148944f87c
3
+ metadata.gz: c75151c856c93ac28e9089018a1d04f40f479782cc4d2e96099d4182d993d81a
4
+ data.tar.gz: 3190821d03128b656e573f8dc748fafcd753b55bfabd4d53c1c6c219ed0a340a
5
5
  SHA512:
6
- metadata.gz: 739c2a26ea80aeca2bbb4b3b51c4cabf93100309956fe594b61cc66a72f6d3e2f23ab0356c81f12e4a461bf82eb1d7f9cc2d0dc5fecbef88348bd17aca17c33a
7
- data.tar.gz: 65ef8401849ee398968ccfce12ccbf203914f7a76721c8cb0ef31b5ab9051fd7f8460361dd96abb495dfa25c5acb50141611448b7065a5be3340b8ca51cc70ff
6
+ metadata.gz: cbf0bdcd577b6766483121f654b3e5139de674b81539fda0a43d949bb73d0092d7e280b1efd772409917a0f53a6fb877897126687060b4c6a14c8c9f5d152e75
7
+ data.tar.gz: 33fa800abbe118922d05db9712bc35910e7d633fbe4f224083c79f4289e8a269dcca90f31b2ac07aa0bece05c3957e5afa3f08bf14bea005a1b93242bcb317c5
data/CHANGELOG CHANGED
@@ -1,3 +1,7 @@
1
+ = 3.22.0 (2019-07-12)
2
+
3
+ * Improve render performance up to 4x in the default case by calling compiled template methods directly (jeremyevans)
4
+
1
5
  = 3.21.0 (2019-06-14)
2
6
 
3
7
  * Cache compiled templates in development mode, until the template files are modified (jeremyevans)
@@ -0,0 +1,24 @@
1
+ = Improvements
2
+
3
+ * The render/view methods in the render plugin, when called with
4
+ a single string/symbol argument (the most common case), are now
5
+ up to 2.5x/4x faster by directly calling compiled template methods.
6
+ This works by extracting the UnboundMethod objects that Tilt
7
+ creates, and defining real methods for them, then calling those
8
+ methods using send. This avoids most of the overhead of the render
9
+ and view methods. The compiled template methods are defined inside
10
+ a module included in the Roda app's class, so this support works
11
+ even if the Roda app itself is frozen.
12
+
13
+ Some plugins, such as render_locals, do not work with this
14
+ optimization, and disable the use of it. The view_options plugin
15
+ does work with this optimization if you are using set_view_subdir or
16
+ append_view_subdir, but not if using set_view_options or
17
+ set_layout_options.
18
+
19
+ This optimization depends on Ruby 2.3+ and Tilt 1.2+, and will not
20
+ be used on earlier versions, or if an API change in Tilt is
21
+ detected.
22
+
23
+ * Session deserialization is now slightly faster in the sessions
24
+ plugin.
@@ -120,29 +120,27 @@ class Roda
120
120
  #
121
121
  # = Speeding Up Template Rendering
122
122
  #
123
- # By default, determining the cache key to use for the template can be a lot
124
- # of work. If you specify the +:cache_key+ option, you can save Roda from
125
- # having to do that work, which will make your application faster. However,
126
- # if you do this, you need to make sure you choose a correct key.
123
+ # The render/view method calls are optimized for usage with a single symbol/string
124
+ # argument specifying the template name. So for fastest rendering, pass only a
125
+ # symbol/string to render/view.
127
126
  #
128
- # If your application uses a unique template per path, in that the same
129
- # path never uses more than one template, you can use the +view_options+ plugin
130
- # and do:
131
- #
132
- # set_view_options cache_key: r.path_info
133
- #
134
- # at the top of your route block. You can even do this if you do have paths
135
- # that use more than one template, as long as you specify +:cache_key+
136
- # specifically when rendering in those paths.
137
- #
138
- # If you use a single layout in your application, you can also make layout
139
- # rendering faster by specifying +:cache_key+ inside the +:layout_opts+
140
- # plugin option.
127
+ # If you must pass a hash to render/view, either as a second argument or as the
128
+ # only argument, you can speed things up by specifying a +:cache_key+ option in
129
+ # the hash, making sure the +:cache_key+ is unique to the template you are
130
+ # rendering.
141
131
  module Render
132
+ # Support for using compiled methods directly requires Ruby 2.3 for the
133
+ # method binding to work, and Tilt 1.2 for Tilt::Template#compiled_method.
134
+ COMPILED_METHOD_SUPPORT = RUBY_VERSION >= '2.3' &&
135
+ defined?(Tilt::VERSION) &&
136
+ Tilt::VERSION >= '1.2' &&
137
+ (Tilt::Template.instance_method(:compiled_method).arity rescue false) == 1
138
+
142
139
  # Setup default rendering options. See Render for details.
143
140
  def self.configure(app, opts=OPTS)
144
141
  if app.opts[:render]
145
142
  orig_cache = app.opts[:render][:cache]
143
+ orig_method_cache = app.opts[:render][:template_method_cache]
146
144
  opts = app.opts[:render][:orig_opts].merge(opts)
147
145
  end
148
146
  app.opts[:render] = opts.dup
@@ -163,6 +161,20 @@ class Roda
163
161
  end
164
162
  end
165
163
 
164
+ if opts[:check_template_mtime]
165
+ opts.delete(:template_method_cache)
166
+ elsif COMPILED_METHOD_SUPPORT
167
+ opts[:template_method_cache] = orig_method_cache || (opts[:cache_class] || RodaCache).new
168
+ begin
169
+ app.const_get(:RodaCompiledTemplates, false)
170
+ rescue NameError
171
+ compiled_templates_module = Module.new
172
+ app.send(:include, compiled_templates_module)
173
+ app.const_set(:RodaCompiledTemplates, compiled_templates_module)
174
+ end
175
+ opts[:template_method_cache] = orig_method_cache || (opts[:cache_class] || RodaCache).new
176
+ end
177
+
166
178
  opts[:cache] = orig_cache || (opts[:cache_class] || RodaCache).new
167
179
 
168
180
  opts[:layout_opts] = (opts[:layout_opts] || {}).dup
@@ -182,6 +194,8 @@ class Roda
182
194
  else
183
195
  opts[:layout_opts][:template] = layout
184
196
  end
197
+
198
+ opts[:optimize_layout] = (opts[:layout_opts][:template] if opts[:layout_opts].keys.sort == [:_is_layout, :template])
185
199
  end
186
200
  opts[:layout_opts].freeze
187
201
 
@@ -249,6 +263,9 @@ class Roda
249
263
  def inherited(subclass)
250
264
  super
251
265
  opts = subclass.opts[:render] = subclass.opts[:render].dup
266
+ if COMPILED_METHOD_SUPPORT
267
+ opts[:template_method_cache] = (opts[:cache_class] || RodaCache).new
268
+ end
252
269
  opts[:cache] = opts[:cache].dup
253
270
  opts.freeze
254
271
  end
@@ -261,9 +278,13 @@ class Roda
261
278
 
262
279
  module InstanceMethods
263
280
  # Render the given template. See Render for details.
264
- def render(template, opts = OPTS, &block)
265
- opts = render_template_opts(template, opts)
266
- retrieve_template(opts).render((opts[:scope]||self), (opts[:locals]||OPTS), &block)
281
+ def render(template, opts = (optimized_template = _cached_template_method(template); OPTS), &block)
282
+ if optimized_template
283
+ send(optimized_template, OPTS, &block)
284
+ else
285
+ opts = render_template_opts(template, opts)
286
+ retrieve_template(opts).render((opts[:scope]||self), (opts[:locals]||OPTS), &block)
287
+ end
267
288
  end
268
289
 
269
290
  # Return the render options for the instance's class.
@@ -274,9 +295,26 @@ class Roda
274
295
  # Render the given template. If there is a default layout
275
296
  # for the class, take the result of the template rendering
276
297
  # and render it inside the layout. See Render for details.
277
- def view(template, opts=OPTS)
278
- opts = parse_template_opts(template, opts)
279
- content = opts[:content] || render_template(opts)
298
+ def view(template, opts = (optimized_template = _cached_template_method(template); OPTS))
299
+ if optimized_template
300
+ content = send(optimized_template, OPTS)
301
+
302
+ render_opts = self.class.opts[:render]
303
+ if layout_template = render_opts[:optimize_layout]
304
+ method_cache = render_opts[:template_method_cache]
305
+ unless layout_method = method_cache[:_roda_layout]
306
+ retrieve_template(:template=>layout_template, :cache_key=>nil, :template_method_cache_key => :_roda_layout)
307
+ layout_method = method_cache[:_roda_layout]
308
+ end
309
+
310
+ if layout_method
311
+ return send(layout_method, OPTS){content}
312
+ end
313
+ end
314
+ else
315
+ opts = parse_template_opts(template, opts)
316
+ content = opts[:content] || render_template(opts)
317
+ end
280
318
 
281
319
  if layout_opts = view_layout_opts(opts)
282
320
  content = render_template(layout_opts){content}
@@ -287,6 +325,41 @@ class Roda
287
325
 
288
326
  private
289
327
 
328
+ if COMPILED_METHOD_SUPPORT
329
+ # If there is an instance method for the template, return the instance
330
+ # method symbol. This optimization is only used for render/view calls
331
+ # with a single string or symbol argument.
332
+ def _cached_template_method(template)
333
+ case template
334
+ when String, Symbol
335
+ if (method_cache = render_opts[:template_method_cache])
336
+ _cached_template_method_lookup(method_cache, template)
337
+ end
338
+ end
339
+ end
340
+
341
+ # The key to use in the template method cache for the given template.
342
+ def _cached_template_method_key(template)
343
+ template
344
+ end
345
+
346
+ # Return the instance method symbol for the template in the method cache.
347
+ def _cached_template_method_lookup(method_cache, template)
348
+ method_cache[template]
349
+ end
350
+ else
351
+ # :nocov:
352
+ def _cached_template_method(template)
353
+ nil
354
+ end
355
+
356
+ def _cached_template_method_key(template)
357
+ nil
358
+ end
359
+ # :nocov:
360
+ end
361
+
362
+
290
363
  # Convert template options to single hash when rendering templates using render.
291
364
  def render_template_opts(template, opts)
292
365
  parse_template_opts(template, opts)
@@ -313,7 +386,7 @@ class Roda
313
386
  # Given the template name and options, set the template class, template path/content,
314
387
  # template block, and locals to use for the render in the passed options.
315
388
  def find_template(opts)
316
- render_opts = render_opts()
389
+ render_opts = self.class.opts[:render]
317
390
  engine_override = opts[:engine]
318
391
  engine = opts[:engine] ||= render_opts[:engine]
319
392
  if content = opts[:inline]
@@ -332,13 +405,15 @@ class Roda
332
405
  end
333
406
 
334
407
  if cache
335
- template_block = opts[:template_block] unless content
336
- template_opts = opts[:template_opts]
337
-
338
- opts[:cache_key] ||= if template_class || engine_override || template_opts || template_block
339
- [path, template_class, engine_override, template_opts, template_block]
340
- else
341
- path
408
+ unless opts.has_key?(:cache_key)
409
+ template_block = opts[:template_block] unless content
410
+ template_opts = opts[:template_opts]
411
+
412
+ opts[:cache_key] = if template_class || engine_override || template_opts || template_block
413
+ [path, template_class, engine_override, template_opts, template_block]
414
+ else
415
+ path
416
+ end
342
417
  end
343
418
  else
344
419
  opts.delete(:cache_key)
@@ -353,6 +428,9 @@ class Roda
353
428
  if template.is_a?(Hash)
354
429
  opts.merge!(template)
355
430
  else
431
+ if opts.empty? && (key = _cached_template_method_key(template))
432
+ opts[:template_method_cache_key] = key
433
+ end
356
434
  opts[:template] = template
357
435
  opts
358
436
  end
@@ -372,6 +450,7 @@ class Roda
372
450
  end
373
451
  cached_template(opts) do
374
452
  opts = found_template_opts || find_template(opts)
453
+ render_opts = self.class.opts[:render]
375
454
  template_opts = render_opts[:template_opts]
376
455
  if engine_opts = render_opts[:engine_opts][opts[:engine]]
377
456
  template_opts = template_opts.merge(engine_opts)
@@ -383,7 +462,28 @@ class Roda
383
462
  if render_opts[:check_template_mtime] && !opts[:template_block] && !cache
384
463
  TemplateMtimeWrapper.new(opts[:template_class], opts[:path], 1, template_opts)
385
464
  else
386
- opts[:template_class].new(opts[:path], 1, template_opts, &opts[:template_block])
465
+ template = opts[:template_class].new(opts[:path], 1, template_opts, &opts[:template_block])
466
+
467
+ if COMPILED_METHOD_SUPPORT &&
468
+ (method_cache_key = opts[:template_method_cache_key]) &&
469
+ (method_cache = render_opts[:template_method_cache]) &&
470
+ (method_cache[method_cache_key] != false) &&
471
+ !opts[:inline] &&
472
+ cache != false
473
+
474
+ begin
475
+ unbound_method = template.send(:compiled_method, OPTS)
476
+ rescue ::NotImplementedError
477
+ method_cache[method_cache_key] = false
478
+ else
479
+ method_name = :"_roda_template_#{self.class.object_id}_#{method_cache_key}"
480
+ self.class::RodaCompiledTemplates.send(:define_method, method_name, unbound_method)
481
+ self.class::RodaCompiledTemplates.send(:private, method_name)
482
+ method_cache[method_cache_key] = method_name
483
+ end
484
+ end
485
+
486
+ template
387
487
  end
388
488
  end
389
489
  end
@@ -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
@@ -41,6 +43,14 @@ class Roda
41
43
  module InstanceMethods
42
44
  private
43
45
 
46
+ if Render::COMPILED_METHOD_SUPPORT
47
+ # Disable use of cached templates, since it assumes a render/view call with no
48
+ # options will have no locals.
49
+ def _cached_template_method(template)
50
+ nil
51
+ end
52
+ end
53
+
44
54
  def render_locals
45
55
  opts[:render_locals]
46
56
  end
@@ -394,10 +394,11 @@ class Roda
394
394
 
395
395
  bitmap, created_at, updated_at = data.unpack('vVV')
396
396
  padding_bytes = bitmap & PADDING_MASK
397
- if (max = opts[:max_seconds]) && Time.now.to_i > created_at + max
397
+ now = Time.now.to_i
398
+ if (max = opts[:max_seconds]) && now > created_at + max
398
399
  return _session_serialization_error("Not returning session: maximum session time expired")
399
400
  end
400
- if (max = opts[:max_idle_seconds]) && Time.now.to_i > updated_at + max
401
+ if (max = opts[:max_idle_seconds]) && now > updated_at + max
401
402
  return _session_serialization_error("Not returning session: maximum session idle time expired")
402
403
  end
403
404
 
@@ -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
@@ -124,6 +126,32 @@ class Roda
124
126
 
125
127
  private
126
128
 
129
+ if Render::COMPILED_METHOD_SUPPORT
130
+ # Return nil if using custom view or layout options.
131
+ # If using a view subdir, prefix the template key with the subdir.
132
+ def _cached_template_method_key(template)
133
+ return if @_view_options || @_layout_options
134
+
135
+ if subdir = @_view_subdir
136
+ template = [subdir, template]
137
+ end
138
+
139
+ super
140
+ end
141
+
142
+ # Return nil if using custom view or layout options.
143
+ # If using a view subdir, prefix the template key with the subdir.
144
+ def _cached_template_method_lookup(method_cache, template)
145
+ return if @_view_options || @_layout_options
146
+
147
+ if subdir = @_view_subdir
148
+ template = [subdir, template]
149
+ end
150
+
151
+ super
152
+ end
153
+ end
154
+
127
155
  # If view options or locals have been set and this
128
156
  # template isn't a layout template, merge the options
129
157
  # and locals into the returned hash.
@@ -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 = 21
7
+ RodaMinorVersion = 22
8
8
 
9
9
  # The patch version of Roda, updated only for bug fixes from the last
10
10
  # feature release.
@@ -1,8 +1,10 @@
1
1
  require_relative "../spec_helper"
2
2
 
3
3
  begin
4
+ require 'tilt'
4
5
  require 'tilt/erb'
5
6
  require 'tilt/string'
7
+ require_relative '../../lib/roda/plugins/render'
6
8
  rescue LoadError
7
9
  warn "tilt not installed, skipping render plugin test"
8
10
  else
@@ -258,6 +260,162 @@ describe "render plugin" do
258
260
  app.render_opts[:views].must_equal File.join(Dir.pwd, 'bar')
259
261
  end
260
262
 
263
+ if Roda::RodaPlugins::Render::COMPILED_METHOD_SUPPORT
264
+ it "does not cache template renders when using a template library that doesn't support it" do
265
+ begin
266
+ require 'tilt/rdoc'
267
+ rescue
268
+ next
269
+ end
270
+
271
+ app(:bare) do
272
+ plugin :render, :views=>'spec/views', :engine=>'rdoc'
273
+ route do
274
+ render('a')
275
+ end
276
+ end
277
+
278
+ app.render_opts[:template_method_cache]['a'].must_be_nil
279
+ body.strip.must_equal "<p># a # * b</p>"
280
+ app.render_opts[:template_method_cache]['a'].must_equal false
281
+ body.strip.must_equal "<p># a # * b</p>"
282
+ app.render_opts[:template_method_cache]['a'].must_equal false
283
+ body.strip.must_equal "<p># a # * b</p>"
284
+ app.render_opts[:template_method_cache]['a'].must_equal false
285
+ app::RodaCompiledTemplates.private_instance_methods.length.must_equal 0
286
+ end
287
+
288
+ ['comp_test', :comp_test].each do |template|
289
+ it "does not cache template renders when given a hash" do
290
+ app(:bare) do
291
+ plugin :render, :views=>'spec/views'
292
+ route do
293
+ render(:template=>template)
294
+ end
295
+ end
296
+
297
+ app.render_opts[:template_method_cache][template].must_be_nil
298
+ body.strip.must_equal "ct"
299
+ app.render_opts[:template_method_cache][template].must_be_nil
300
+ body.strip.must_equal "ct"
301
+ app.render_opts[:template_method_cache][template].must_be_nil
302
+ body.strip.must_equal "ct"
303
+ app.render_opts[:template_method_cache][template].must_be_nil
304
+ app::RodaCompiledTemplates.private_instance_methods.length.must_equal 0
305
+ end
306
+
307
+ it "caches template renders when given a #{template.class}" do
308
+ app(:bare) do
309
+ plugin :render, :views=>'spec/views'
310
+ route do
311
+ render(template)
312
+ end
313
+ end
314
+
315
+ app.render_opts[:template_method_cache][template].must_be_nil
316
+ body.strip.must_equal "ct"
317
+ app.render_opts[:template_method_cache][template].must_be_kind_of(Symbol)
318
+ body.strip.must_equal "ct"
319
+ app.render_opts[:template_method_cache][template].must_be_kind_of(Symbol)
320
+ body.strip.must_equal "ct"
321
+ app.render_opts[:template_method_cache][template].must_be_kind_of(Symbol)
322
+ app::RodaCompiledTemplates.private_instance_methods.length.must_equal 1
323
+ end
324
+
325
+ it "does not cache template views or layout when given a hash" do
326
+ app(:bare) do
327
+ layout = template.to_s.sub('test', 'layout')
328
+ layout = layout.to_sym if template.is_a?(Symbol)
329
+ plugin :render, :views=>'spec/views', :layout=>layout
330
+ route do
331
+ view(:template=>template)
332
+ end
333
+ end
334
+
335
+ app.render_opts[:template_method_cache][template].must_be_nil
336
+ app.render_opts[:template_method_cache][:_roda_layout].must_be_nil
337
+ body.strip.must_equal "act\nb"
338
+ app.render_opts[:template_method_cache][template].must_be_nil
339
+ app.render_opts[:template_method_cache][:_roda_layout].must_be_nil
340
+ body.strip.must_equal "act\nb"
341
+ app.render_opts[:template_method_cache][template].must_be_nil
342
+ app.render_opts[:template_method_cache][:_roda_layout].must_be_nil
343
+ body.strip.must_equal "act\nb"
344
+ app.render_opts[:template_method_cache][template].must_be_nil
345
+ app.render_opts[:template_method_cache][:_roda_layout].must_be_nil
346
+ app::RodaCompiledTemplates.private_instance_methods.length.must_equal 0
347
+ end
348
+
349
+ it "caches template views with layout when given a #{template.class}" do
350
+ app(:bare) do
351
+ layout = template.to_s.sub('test', 'layout')
352
+ layout = layout.to_sym if template.is_a?(Symbol)
353
+ plugin :render, :views=>'spec/views', :layout=>layout
354
+ route do
355
+ view(template)
356
+ end
357
+ end
358
+
359
+ app.render_opts[:template_method_cache][template].must_be_nil
360
+ app.render_opts[:template_method_cache][:_roda_layout].must_be_nil
361
+ body.strip.must_equal "act\nb"
362
+ app.render_opts[:template_method_cache][template].must_be_kind_of(Symbol)
363
+ app.render_opts[:template_method_cache][:_roda_layout].must_be_nil
364
+ body.strip.must_equal "act\nb"
365
+ app.render_opts[:template_method_cache][template].must_be_kind_of(Symbol)
366
+ app.render_opts[:template_method_cache][:_roda_layout].must_be_kind_of(Symbol)
367
+ body.strip.must_equal "act\nb"
368
+ app.render_opts[:template_method_cache][template].must_be_kind_of(Symbol)
369
+ app.render_opts[:template_method_cache][:_roda_layout].must_be_kind_of(Symbol)
370
+ app::RodaCompiledTemplates.private_instance_methods.length.must_equal 2
371
+ end
372
+
373
+ it "caches template views without layout when additional layout options given when given a #{template.class}" do
374
+ app(:bare) do
375
+ plugin :render, :views=>'spec/views', :layout=>nil
376
+ route do
377
+ view(template)
378
+ end
379
+ end
380
+
381
+ app.render_opts[:template_method_cache][template].must_be_nil
382
+ app.render_opts[:template_method_cache][:_roda_layout].must_be_nil
383
+ body.strip.must_equal "ct"
384
+ app.render_opts[:template_method_cache][template].must_be_kind_of(Symbol)
385
+ app.render_opts[:template_method_cache][:_roda_layout].must_be_nil
386
+ body.strip.must_equal "ct"
387
+ app.render_opts[:template_method_cache][template].must_be_kind_of(Symbol)
388
+ app.render_opts[:template_method_cache][:_roda_layout].must_be_nil
389
+ body.strip.must_equal "ct"
390
+ app.render_opts[:template_method_cache][template].must_be_kind_of(Symbol)
391
+ app.render_opts[:template_method_cache][:_roda_layout].must_be_nil
392
+ app::RodaCompiledTemplates.private_instance_methods.length.must_equal 1
393
+ end
394
+
395
+ it "caches template views without layout when additional layout options given when given a #{template.class}" do
396
+ app(:bare) do
397
+ plugin :render, :views=>'spec/views', :layout_opts=>{:locals=>{:title=>"Home"}}
398
+ route do
399
+ view(template)
400
+ end
401
+ end
402
+
403
+ app.render_opts[:template_method_cache][template].must_be_nil
404
+ app.render_opts[:template_method_cache][:_roda_layout].must_be_nil
405
+ body.strip.must_equal "<title>Roda: Home</title>\nct"
406
+ app.render_opts[:template_method_cache][template].must_be_kind_of(Symbol)
407
+ app.render_opts[:template_method_cache][:_roda_layout].must_be_nil
408
+ body.strip.must_equal "<title>Roda: Home</title>\nct"
409
+ app.render_opts[:template_method_cache][template].must_be_kind_of(Symbol)
410
+ app.render_opts[:template_method_cache][:_roda_layout].must_be_nil
411
+ body.strip.must_equal "<title>Roda: Home</title>\nct"
412
+ app.render_opts[:template_method_cache][template].must_be_kind_of(Symbol)
413
+ app.render_opts[:template_method_cache][:_roda_layout].must_be_nil
414
+ app::RodaCompiledTemplates.private_instance_methods.length.must_equal 1
415
+ end
416
+ end
417
+ end
418
+
261
419
  it "inline layouts and inline views" do
262
420
  app(:render) do
263
421
  view({:inline=>'bar'}, :layout=>{:inline=>'Foo: <%= yield %>'})
@@ -21,12 +21,23 @@ describe "view_options plugin view subdirs" do
21
21
 
22
22
  r.on "about" do
23
23
  append_view_subdir 'views'
24
+ r.on 'test' do
25
+ append_view_subdir 'about'
26
+ r.is('view'){view("comp_test")}
27
+ r.is{render("comp_test")}
28
+ end
24
29
  render("about", :locals=>{:title => "About Roda"})
25
30
  end
26
31
 
27
32
  r.on "path" do
28
33
  render('spec/views/about', :locals=>{:title => "Path"}, :layout_opts=>{:locals=>{:title=>"Home"}})
29
34
  end
35
+
36
+ r.on 'test' do
37
+ set_view_subdir 'spec/views'
38
+ r.is('view'){view("comp_test")}
39
+ r.is{render("comp_test")}
40
+ end
30
41
  end
31
42
  end
32
43
  end
@@ -42,6 +53,22 @@ describe "view_options plugin view subdirs" do
42
53
  it "should not change behavior when subdir is not set" do
43
54
  body("/path").strip.must_equal "<h1>Path</h1>"
44
55
  end
56
+
57
+ it "should handle template compilation correctly" do
58
+ @app.plugin :render, :layout=>'./spec/views/comp_layout'
59
+ 3.times do
60
+ body("/test").strip.must_equal "ct"
61
+ body("/about/test").strip.must_equal "about-ct"
62
+ body("/test/view").strip.must_equal "act\nb"
63
+ body("/about/test/view").strip.must_equal "aabout-ct\nb"
64
+ end
65
+ if Roda::RodaPlugins::Render::COMPILED_METHOD_SUPPORT
66
+ method_cache = @app.opts[:render][:template_method_cache]
67
+ method_cache[['spec/views', 'comp_test']].must_be_kind_of(Symbol)
68
+ method_cache[['spec/views/about', 'comp_test']].must_be_kind_of(Symbol)
69
+ method_cache[:_roda_layout].must_be_kind_of(Symbol)
70
+ end
71
+ end
45
72
  end
46
73
 
47
74
  describe "view_options plugin" do
@@ -0,0 +1,2 @@
1
+ # a
2
+ # * b
@@ -0,0 +1 @@
1
+ about-ct
@@ -0,0 +1 @@
1
+ a<%= yield %>b
@@ -0,0 +1 @@
1
+ ct
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.21.0
4
+ version: 3.22.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-06-14 00:00:00.000000000 Z
11
+ date: 2019-07-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack
@@ -218,6 +218,7 @@ extra_rdoc_files:
218
218
  - doc/release_notes/3.19.0.txt
219
219
  - doc/release_notes/3.20.0.txt
220
220
  - doc/release_notes/3.21.0.txt
221
+ - doc/release_notes/3.22.0.txt
221
222
  files:
222
223
  - CHANGELOG
223
224
  - MIT-LICENSE
@@ -275,6 +276,7 @@ files:
275
276
  - doc/release_notes/3.2.0.txt
276
277
  - doc/release_notes/3.20.0.txt
277
278
  - doc/release_notes/3.21.0.txt
279
+ - doc/release_notes/3.22.0.txt
278
280
  - doc/release_notes/3.3.0.txt
279
281
  - doc/release_notes/3.4.0.txt
280
282
  - doc/release_notes/3.5.0.txt
@@ -500,13 +502,17 @@ files:
500
502
  - spec/version_spec.rb
501
503
  - spec/views/_test.erb
502
504
  - spec/views/a.erb
505
+ - spec/views/a.rdoc
503
506
  - spec/views/about.erb
504
507
  - spec/views/about.str
505
508
  - spec/views/about/_test.css.gz
506
509
  - spec/views/about/_test.erb
507
510
  - spec/views/about/_test.erb.gz
511
+ - spec/views/about/comp_test.erb
508
512
  - spec/views/b.erb
509
513
  - spec/views/c.erb
514
+ - spec/views/comp_layout.erb
515
+ - spec/views/comp_test.erb
510
516
  - spec/views/content-yield.erb
511
517
  - spec/views/home.erb
512
518
  - spec/views/home.str