actionview 5.2.8.1 → 6.0.0.beta2

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.

Potentially problematic release.


This version of actionview might be problematic. Click here for more details.

Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +121 -152
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +1 -1
  5. data/lib/action_view/base.rb +107 -10
  6. data/lib/action_view/buffers.rb +15 -0
  7. data/lib/action_view/context.rb +5 -9
  8. data/lib/action_view/digestor.rb +8 -11
  9. data/lib/action_view/file_template.rb +33 -0
  10. data/lib/action_view/gem_version.rb +4 -4
  11. data/lib/action_view/helpers/asset_tag_helper.rb +7 -30
  12. data/lib/action_view/helpers/asset_url_helper.rb +4 -3
  13. data/lib/action_view/helpers/cache_helper.rb +18 -10
  14. data/lib/action_view/helpers/capture_helper.rb +4 -0
  15. data/lib/action_view/helpers/csp_helper.rb +4 -2
  16. data/lib/action_view/helpers/csrf_helper.rb +1 -1
  17. data/lib/action_view/helpers/date_helper.rb +69 -25
  18. data/lib/action_view/helpers/form_helper.rb +240 -8
  19. data/lib/action_view/helpers/form_options_helper.rb +23 -15
  20. data/lib/action_view/helpers/form_tag_helper.rb +9 -9
  21. data/lib/action_view/helpers/javascript_helper.rb +10 -11
  22. data/lib/action_view/helpers/number_helper.rb +5 -0
  23. data/lib/action_view/helpers/rendering_helper.rb +6 -4
  24. data/lib/action_view/helpers/sanitize_helper.rb +3 -3
  25. data/lib/action_view/helpers/tag_helper.rb +13 -43
  26. data/lib/action_view/helpers/tags/base.rb +9 -5
  27. data/lib/action_view/helpers/tags/color_field.rb +1 -1
  28. data/lib/action_view/helpers/tags/translator.rb +1 -6
  29. data/lib/action_view/helpers/text_helper.rb +3 -3
  30. data/lib/action_view/helpers/translation_helper.rb +12 -19
  31. data/lib/action_view/helpers/url_helper.rb +14 -14
  32. data/lib/action_view/helpers.rb +0 -2
  33. data/lib/action_view/layouts.rb +5 -5
  34. data/lib/action_view/log_subscriber.rb +6 -6
  35. data/lib/action_view/lookup_context.rb +63 -28
  36. data/lib/action_view/railtie.rb +23 -0
  37. data/lib/action_view/record_identifier.rb +2 -2
  38. data/lib/action_view/renderer/abstract_renderer.rb +56 -3
  39. data/lib/action_view/renderer/partial_renderer/collection_caching.rb +49 -10
  40. data/lib/action_view/renderer/partial_renderer.rb +66 -52
  41. data/lib/action_view/renderer/renderer.rb +16 -4
  42. data/lib/action_view/renderer/streaming_template_renderer.rb +4 -4
  43. data/lib/action_view/renderer/template_renderer.rb +18 -18
  44. data/lib/action_view/rendering.rb +49 -30
  45. data/lib/action_view/routing_url_for.rb +12 -11
  46. data/lib/action_view/template/handlers/builder.rb +2 -2
  47. data/lib/action_view/template/handlers/erb/erubi.rb +7 -3
  48. data/lib/action_view/template/handlers/erb.rb +17 -7
  49. data/lib/action_view/template/handlers/html.rb +1 -1
  50. data/lib/action_view/template/handlers/raw.rb +2 -2
  51. data/lib/action_view/template/handlers.rb +27 -1
  52. data/lib/action_view/template/html.rb +14 -5
  53. data/lib/action_view/template/inline.rb +22 -0
  54. data/lib/action_view/template/resolver.rb +70 -23
  55. data/lib/action_view/template/text.rb +5 -3
  56. data/lib/action_view/template.rb +75 -36
  57. data/lib/action_view/test_case.rb +1 -1
  58. data/lib/action_view/testing/resolvers.rb +7 -5
  59. data/lib/action_view/view_paths.rb +25 -1
  60. data/lib/action_view.rb +2 -2
  61. data/lib/assets/compiled/rails-ujs.js +39 -22
  62. metadata +19 -18
  63. data/lib/action_view/helpers/record_tag_helper.rb +0 -23
@@ -6,9 +6,13 @@ require "rails"
6
6
  module ActionView
7
7
  # = Action View Railtie
8
8
  class Railtie < Rails::Engine # :nodoc:
9
+ NULL_OPTION = Object.new
10
+
9
11
  config.action_view = ActiveSupport::OrderedOptions.new
10
12
  config.action_view.embed_authenticity_token_in_remote_forms = nil
11
13
  config.action_view.debug_missing_translation = true
14
+ config.action_view.default_enforce_utf8 = nil
15
+ config.action_view.finalize_compiled_template_methods = NULL_OPTION
12
16
 
13
17
  config.eager_load_namespaces << ActionView
14
18
 
@@ -35,6 +39,25 @@ module ActionView
35
39
  end
36
40
  end
37
41
 
42
+ initializer "action_view.default_enforce_utf8" do |app|
43
+ ActiveSupport.on_load(:action_view) do
44
+ default_enforce_utf8 = app.config.action_view.delete(:default_enforce_utf8)
45
+ unless default_enforce_utf8.nil?
46
+ ActionView::Helpers::FormTagHelper.default_enforce_utf8 = default_enforce_utf8
47
+ end
48
+ end
49
+ end
50
+
51
+ initializer "action_view.finalize_compiled_template_methods" do |app|
52
+ ActiveSupport.on_load(:action_view) do
53
+ option = app.config.action_view.delete(:finalize_compiled_template_methods)
54
+
55
+ if option != NULL_OPTION
56
+ ActiveSupport::Deprecation.warn "action_view.finalize_compiled_template_methods is deprecated and has no effect"
57
+ end
58
+ end
59
+ end
60
+
38
61
  initializer "action_view.logger" do
39
62
  ActiveSupport.on_load(:action_view) { self.logger ||= Rails.logger }
40
63
  end
@@ -59,8 +59,8 @@ module ActionView
59
59
 
60
60
  include ModelNaming
61
61
 
62
- JOIN = "_".freeze
63
- NEW = "new".freeze
62
+ JOIN = "_"
63
+ NEW = "new"
64
64
 
65
65
  # The DOM class convention is to use the singular form of an object or class.
66
66
  #
@@ -17,7 +17,7 @@ module ActionView
17
17
  # that new object is called in turn. This abstracts the setup and rendering
18
18
  # into a separate classes for partials and templates.
19
19
  class AbstractRenderer #:nodoc:
20
- delegate :find_template, :find_file, :template_exists?, :any_templates?, :with_fallbacks, :with_layout_format, :formats, to: :@lookup_context
20
+ delegate :template_exists?, :any_templates?, :formats, to: :@lookup_context
21
21
 
22
22
  def initialize(lookup_context)
23
23
  @lookup_context = lookup_context
@@ -27,6 +27,53 @@ module ActionView
27
27
  raise NotImplementedError
28
28
  end
29
29
 
30
+ class RenderedCollection # :nodoc:
31
+ def self.empty(format)
32
+ EmptyCollection.new format
33
+ end
34
+
35
+ attr_reader :rendered_templates
36
+
37
+ def initialize(rendered_templates, spacer)
38
+ @rendered_templates = rendered_templates
39
+ @spacer = spacer
40
+ end
41
+
42
+ def body
43
+ @rendered_templates.map(&:body).join(@spacer.body).html_safe
44
+ end
45
+
46
+ def format
47
+ rendered_templates.first.format
48
+ end
49
+
50
+ class EmptyCollection
51
+ attr_reader :format
52
+
53
+ def initialize(format)
54
+ @format = format
55
+ end
56
+
57
+ def body; nil; end
58
+ end
59
+ end
60
+
61
+ class RenderedTemplate # :nodoc:
62
+ attr_reader :body, :layout, :template
63
+
64
+ def initialize(body, layout, template)
65
+ @body = body
66
+ @layout = layout
67
+ @template = template
68
+ end
69
+
70
+ def format
71
+ template.format
72
+ end
73
+
74
+ EMPTY_SPACER = Struct.new(:body).new
75
+ end
76
+
30
77
  private
31
78
 
32
79
  def extract_details(options) # :doc:
@@ -38,8 +85,6 @@ module ActionView
38
85
  end
39
86
 
40
87
  def instrument(name, **options) # :doc:
41
- options[:identifier] ||= (@template && @template.identifier) || @path
42
-
43
88
  ActiveSupport::Notifications.instrument("render_#{name}.action_view", options) do |payload|
44
89
  yield payload
45
90
  end
@@ -51,5 +96,13 @@ module ActionView
51
96
 
52
97
  @lookup_context.formats = formats | @lookup_context.formats
53
98
  end
99
+
100
+ def build_rendered_template(content, template, layout = nil)
101
+ RenderedTemplate.new content, layout, template
102
+ end
103
+
104
+ def build_rendered_collection(templates, spacer)
105
+ RenderedCollection.new templates, spacer
106
+ end
54
107
  end
55
108
  end
@@ -11,18 +11,38 @@ module ActionView
11
11
  end
12
12
 
13
13
  private
14
- def cache_collection_render(instrumentation_payload)
14
+ def cache_collection_render(instrumentation_payload, view, template)
15
15
  return yield unless @options[:cached]
16
16
 
17
- keyed_collection = collection_by_cache_keys
17
+ # Result is a hash with the key represents the
18
+ # key used for cache lookup and the value is the item
19
+ # on which the partial is being rendered
20
+ keyed_collection = collection_by_cache_keys(view, template)
21
+
22
+ # Pull all partials from cache
23
+ # Result is a hash, key matches the entry in
24
+ # `keyed_collection` where the cache was retrieved and the
25
+ # value is the value that was present in the cache
18
26
  cached_partials = collection_cache.read_multi(*keyed_collection.keys)
19
27
  instrumentation_payload[:cache_hits] = cached_partials.size
20
28
 
29
+ # Extract the items for the keys that are not found
30
+ # Set the uncached values to instance variable @collection
31
+ # which is used by the caller
21
32
  @collection = keyed_collection.reject { |key, _| cached_partials.key?(key) }.values
33
+
34
+ # If all elements are already in cache then
35
+ # rendered partials will be an empty array
36
+ #
37
+ # If the cache is missing elements then
38
+ # the block will be called against the remaining items
39
+ # in the @collection.
22
40
  rendered_partials = @collection.empty? ? [] : yield
23
41
 
24
42
  index = 0
25
- fetch_or_cache_partial(cached_partials, order_by: keyed_collection.each_key) do
43
+ fetch_or_cache_partial(cached_partials, template, order_by: keyed_collection.each_key) do
44
+ # This block is called once
45
+ # for every cache miss while preserving order.
26
46
  rendered_partials[index].tap { index += 1 }
27
47
  end
28
48
  end
@@ -31,24 +51,43 @@ module ActionView
31
51
  @options[:cached].respond_to?(:call)
32
52
  end
33
53
 
34
- def collection_by_cache_keys
54
+ def collection_by_cache_keys(view, template)
35
55
  seed = callable_cache_key? ? @options[:cached] : ->(i) { i }
36
56
 
57
+ digest_path = view.digest_path_from_template(template)
58
+
37
59
  @collection.each_with_object({}) do |item, hash|
38
- hash[expanded_cache_key(seed.call(item))] = item
60
+ hash[expanded_cache_key(seed.call(item), view, template, digest_path)] = item
39
61
  end
40
62
  end
41
63
 
42
- def expanded_cache_key(key)
43
- key = @view.combined_fragment_cache_key(@view.cache_fragment_name(key, virtual_path: @template.virtual_path))
64
+ def expanded_cache_key(key, view, template, digest_path)
65
+ key = view.combined_fragment_cache_key(view.cache_fragment_name(key, virtual_path: template.virtual_path, digest_path: digest_path))
44
66
  key.frozen? ? key.dup : key # #read_multi & #write may require mutability, Dalli 2.6.0.
45
67
  end
46
68
 
47
- def fetch_or_cache_partial(cached_partials, order_by:)
69
+ # `order_by` is an enumerable object containing keys of the cache,
70
+ # all keys are passed in whether found already or not.
71
+ #
72
+ # `cached_partials` is a hash. If the value exists
73
+ # it represents the rendered partial from the cache
74
+ # otherwise `Hash#fetch` will take the value of its block.
75
+ #
76
+ # This method expects a block that will return the rendered
77
+ # partial. An example is to render all results
78
+ # for each element that was not found in the cache and store it as an array.
79
+ # Order it so that the first empty cache element in `cached_partials`
80
+ # corresponds to the first element in `rendered_partials`.
81
+ #
82
+ # If the partial is not already cached it will also be
83
+ # written back to the underlying cache store.
84
+ def fetch_or_cache_partial(cached_partials, template, order_by:)
48
85
  order_by.map do |cache_key|
49
- cached_partials.fetch(cache_key) do
86
+ if content = cached_partials[cache_key]
87
+ build_rendered_template(content, template)
88
+ else
50
89
  yield.tap do |rendered_partial|
51
- collection_cache.write(cache_key, rendered_partial)
90
+ collection_cache.write(cache_key, rendered_partial.body)
52
91
  end
53
92
  end
54
93
  end
@@ -295,43 +295,60 @@ module ActionView
295
295
  end
296
296
 
297
297
  def render(context, options, block)
298
- setup(context, options, block)
299
- @template = find_partial
298
+ as = as_variable(options)
299
+ setup(context, options, as, block)
300
300
 
301
- @lookup_context.rendered_format ||= begin
302
- if @template && @template.formats.present?
303
- @template.formats.first
301
+ if @path
302
+ if @has_object || @collection
303
+ @variable, @variable_counter, @variable_iteration = retrieve_variable(@path, as)
304
+ @template_keys = retrieve_template_keys(@variable)
304
305
  else
305
- formats.first
306
+ @template_keys = @locals.keys
307
+ end
308
+ template = find_partial(@path, @template_keys)
309
+ @variable ||= template.variable
310
+ else
311
+ if options[:cached]
312
+ raise NotImplementedError, "render caching requires a template. Please specify a partial when rendering"
306
313
  end
314
+ template = nil
307
315
  end
308
316
 
309
317
  if @collection
310
- render_collection
318
+ render_collection(context, template)
311
319
  else
312
- render_partial
320
+ render_partial(context, template)
313
321
  end
314
322
  end
315
323
 
316
324
  private
317
325
 
318
- def render_collection
319
- instrument(:collection, count: @collection.size) do |payload|
320
- return nil if @collection.blank?
326
+ def render_collection(view, template)
327
+ identifier = (template && template.identifier) || @path
328
+ instrument(:collection, identifier: identifier, count: @collection.size) do |payload|
329
+ return RenderedCollection.empty(@lookup_context.formats.first) if @collection.blank?
321
330
 
322
- if @options.key?(:spacer_template)
323
- spacer = find_template(@options[:spacer_template], @locals.keys).render(@view, @locals)
331
+ spacer = if @options.key?(:spacer_template)
332
+ spacer_template = find_template(@options[:spacer_template], @locals.keys)
333
+ build_rendered_template(spacer_template.render(view, @locals), spacer_template)
334
+ else
335
+ RenderedTemplate::EMPTY_SPACER
324
336
  end
325
337
 
326
- cache_collection_render(payload) do
327
- @template ? collection_with_template : collection_without_template
328
- end.join(spacer).html_safe
338
+ collection_body = if template
339
+ cache_collection_render(payload, view, template) do
340
+ collection_with_template(view, template)
341
+ end
342
+ else
343
+ collection_without_template(view)
344
+ end
345
+ build_rendered_collection(collection_body, spacer)
329
346
  end
330
347
  end
331
348
 
332
- def render_partial
333
- instrument(:partial) do |payload|
334
- view, locals, block = @view, @locals, @block
349
+ def render_partial(view, template)
350
+ instrument(:partial, identifier: template.identifier) do |payload|
351
+ locals, block = @locals, @block
335
352
  object, as = @object, @variable
336
353
 
337
354
  if !block && (layout = @options[:layout])
@@ -341,13 +358,13 @@ module ActionView
341
358
  object = locals[as] if object.nil? # Respect object when object is false
342
359
  locals[as] = object if @has_object
343
360
 
344
- content = @template.render(view, locals) do |*name|
361
+ content = template.render(view, locals) do |*name|
345
362
  view._layout_for(*name, &block)
346
363
  end
347
364
 
348
365
  content = layout.render(view, locals) { content } if layout
349
- payload[:cache_hit] = view.view_renderer.cache_hits[@template.virtual_path]
350
- content
366
+ payload[:cache_hit] = view.view_renderer.cache_hits[template.virtual_path]
367
+ build_rendered_template(content, template, layout)
351
368
  end
352
369
  end
353
370
 
@@ -358,16 +375,13 @@ module ActionView
358
375
  # If +options[:partial]+ is a string, then the <tt>@path</tt> instance variable is
359
376
  # set to that string. Otherwise, the +options[:partial]+ object must
360
377
  # respond to +to_partial_path+ in order to setup the path.
361
- def setup(context, options, block)
362
- @view = context
378
+ def setup(context, options, as, block)
363
379
  @options = options
364
380
  @block = block
365
381
 
366
382
  @locals = options[:locals] || {}
367
383
  @details = extract_details(options)
368
384
 
369
- prepend_formats(options[:formats])
370
-
371
385
  partial = options[:partial]
372
386
 
373
387
  if String === partial
@@ -381,26 +395,26 @@ module ActionView
381
395
  @collection = collection_from_object || collection_from_options
382
396
 
383
397
  if @collection
384
- paths = @collection_data = @collection.map { |o| partial_path(o) }
385
- @path = paths.uniq.one? ? paths.first : nil
398
+ paths = @collection_data = @collection.map { |o| partial_path(o, context) }
399
+ if paths.uniq.length == 1
400
+ @path = paths.first
401
+ else
402
+ paths.map! { |path| retrieve_variable(path, as).unshift(path) }
403
+ @path = nil
404
+ end
386
405
  else
387
- @path = partial_path
406
+ @path = partial_path(@object, context)
388
407
  end
389
408
  end
390
409
 
410
+ self
411
+ end
412
+
413
+ def as_variable(options)
391
414
  if as = options[:as]
392
415
  raise_invalid_option_as(as) unless /\A[a-z_]\w*\z/.match?(as.to_s)
393
- as = as.to_sym
394
- end
395
-
396
- if @path
397
- @variable, @variable_counter, @variable_iteration = retrieve_variable(@path, as)
398
- @template_keys = retrieve_template_keys
399
- else
400
- paths.map! { |path| retrieve_variable(path, as).unshift(path) }
416
+ as.to_sym
401
417
  end
402
-
403
- self
404
418
  end
405
419
 
406
420
  def collection_from_options
@@ -414,8 +428,8 @@ module ActionView
414
428
  @object.to_ary if @object.respond_to?(:to_ary)
415
429
  end
416
430
 
417
- def find_partial
418
- find_template(@path, @template_keys) if @path
431
+ def find_partial(path, template_keys)
432
+ find_template(path, template_keys)
419
433
  end
420
434
 
421
435
  def find_template(path, locals)
@@ -423,8 +437,8 @@ module ActionView
423
437
  @lookup_context.find_template(path, prefixes, true, locals, @details)
424
438
  end
425
439
 
426
- def collection_with_template
427
- view, locals, template = @view, @locals, @template
440
+ def collection_with_template(view, template)
441
+ locals = @locals
428
442
  as, counter, iteration = @variable, @variable_counter, @variable_iteration
429
443
 
430
444
  if layout = @options[:layout]
@@ -441,12 +455,12 @@ module ActionView
441
455
  content = template.render(view, locals)
442
456
  content = layout.render(view, locals) { content } if layout
443
457
  partial_iteration.iterate!
444
- content
458
+ build_rendered_template(content, template, layout)
445
459
  end
446
460
  end
447
461
 
448
- def collection_without_template
449
- view, locals, collection_data = @view, @locals, @collection_data
462
+ def collection_without_template(view)
463
+ locals, collection_data = @locals, @collection_data
450
464
  cache = {}
451
465
  keys = @locals.keys
452
466
 
@@ -463,7 +477,7 @@ module ActionView
463
477
  template = (cache[path] ||= find_template(path, keys + [as, counter, iteration]))
464
478
  content = template.render(view, locals)
465
479
  partial_iteration.iterate!
466
- content
480
+ build_rendered_template(content, template)
467
481
  end
468
482
  end
469
483
 
@@ -474,7 +488,7 @@ module ActionView
474
488
  #
475
489
  # If +prefix_partial_path_with_controller_namespace+ is true, then this
476
490
  # method will prefix the partial paths with a namespace.
477
- def partial_path(object = @object)
491
+ def partial_path(object, view)
478
492
  object = object.to_model if object.respond_to?(:to_model)
479
493
 
480
494
  path = if object.respond_to?(:to_partial_path)
@@ -483,7 +497,7 @@ module ActionView
483
497
  raise ArgumentError.new("'#{object.inspect}' is not an ActiveModel-compatible object. It must implement :to_partial_path.")
484
498
  end
485
499
 
486
- if @view.prefix_partial_path_with_controller_namespace
500
+ if view.prefix_partial_path_with_controller_namespace
487
501
  prefixed_partial_names[path] ||= merge_prefix_into_object_path(@context_prefix, path.dup)
488
502
  else
489
503
  path
@@ -511,9 +525,9 @@ module ActionView
511
525
  end
512
526
  end
513
527
 
514
- def retrieve_template_keys
528
+ def retrieve_template_keys(variable)
515
529
  keys = @locals.keys
516
- keys << @variable if @has_object || @collection
530
+ keys << variable
517
531
  if @collection
518
532
  keys << @variable_counter
519
533
  keys << @variable_iteration
@@ -523,7 +537,7 @@ module ActionView
523
537
 
524
538
  def retrieve_variable(path, as)
525
539
  variable = as || begin
526
- base = path[-1] == "/".freeze ? "".freeze : File.basename(path)
540
+ base = path[-1] == "/" ? "" : File.basename(path)
527
541
  raise_invalid_identifier(path) unless base =~ /\A_?(.*?)(?:\.\w+)*\z/
528
542
  $1.to_sym
529
543
  end
@@ -19,10 +19,14 @@ module ActionView
19
19
 
20
20
  # Main render entry point shared by Action View and Action Controller.
21
21
  def render(context, options)
22
+ render_to_object(context, options).body
23
+ end
24
+
25
+ def render_to_object(context, options) # :nodoc:
22
26
  if options.key?(:partial)
23
- render_partial(context, options)
27
+ render_partial_to_object(context, options)
24
28
  else
25
- render_template(context, options)
29
+ render_template_to_object(context, options)
26
30
  end
27
31
  end
28
32
 
@@ -41,16 +45,24 @@ module ActionView
41
45
 
42
46
  # Direct access to template rendering.
43
47
  def render_template(context, options) #:nodoc:
44
- TemplateRenderer.new(@lookup_context).render(context, options)
48
+ render_template_to_object(context, options).body
45
49
  end
46
50
 
47
51
  # Direct access to partial rendering.
48
52
  def render_partial(context, options, &block) #:nodoc:
49
- PartialRenderer.new(@lookup_context).render(context, options, block)
53
+ render_partial_to_object(context, options, &block).body
50
54
  end
51
55
 
52
56
  def cache_hits # :nodoc:
53
57
  @cache_hits ||= {}
54
58
  end
59
+
60
+ def render_template_to_object(context, options) #:nodoc:
61
+ TemplateRenderer.new(@lookup_context).render(context, options)
62
+ end
63
+
64
+ def render_partial_to_object(context, options, &block) #:nodoc:
65
+ PartialRenderer.new(@lookup_context).render(context, options, block)
66
+ end
55
67
  end
56
68
  end
@@ -33,7 +33,7 @@ module ActionView
33
33
  logger = ActionView::Base.logger
34
34
  return unless logger
35
35
 
36
- message = "\n#{exception.class} (#{exception.message}):\n".dup
36
+ message = +"\n#{exception.class} (#{exception.message}):\n"
37
37
  message << exception.annoted_source_code.to_s if exception.respond_to?(:annoted_source_code)
38
38
  message << " " << exception.backtrace.join("\n ")
39
39
  logger.fatal("#{message}\n\n")
@@ -43,14 +43,14 @@ module ActionView
43
43
  # For streaming, instead of rendering a given a template, we return a Body
44
44
  # object that responds to each. This object is initialized with a block
45
45
  # that knows how to render the template.
46
- def render_template(template, layout_name = nil, locals = {}) #:nodoc:
47
- return [super] unless layout_name && template.supports_streaming?
46
+ def render_template(view, template, layout_name = nil, locals = {}) #:nodoc:
47
+ return [super.body] unless layout_name && template.supports_streaming?
48
48
 
49
49
  locals ||= {}
50
50
  layout = layout_name && find_layout(layout_name, locals.keys, [formats.first])
51
51
 
52
52
  Body.new do |buffer|
53
- delayed_render(buffer, template, layout, @view, locals)
53
+ delayed_render(buffer, template, layout, view, locals)
54
54
  end
55
55
  end
56
56
 
@@ -5,15 +5,12 @@ require "active_support/core_ext/object/try"
5
5
  module ActionView
6
6
  class TemplateRenderer < AbstractRenderer #:nodoc:
7
7
  def render(context, options)
8
- @view = context
9
8
  @details = extract_details(options)
10
9
  template = determine_template(options)
11
10
 
12
- prepend_formats(template.formats)
11
+ prepend_formats(template.format)
13
12
 
14
- @lookup_context.rendered_format ||= (template.formats.first || formats.first)
15
-
16
- render_template(template, options[:layout], options[:locals])
13
+ render_template(context, template, options[:layout], options[:locals] || {})
17
14
  end
18
15
 
19
16
  private
@@ -29,15 +26,20 @@ module ActionView
29
26
  elsif options.key?(:html)
30
27
  Template::HTML.new(options[:html], formats.first)
31
28
  elsif options.key?(:file)
32
- with_fallbacks { find_file(options[:file], nil, false, keys, @details) }
29
+ @lookup_context.with_fallbacks.find_file(options[:file], nil, false, keys, @details)
33
30
  elsif options.key?(:inline)
34
31
  handler = Template.handler_for_extension(options[:type] || "erb")
35
- Template.new(options[:inline], "inline template", handler, locals: keys)
32
+ format = if handler.respond_to?(:default_format)
33
+ handler.default_format
34
+ else
35
+ @lookup_context.formats.first
36
+ end
37
+ Template::Inline.new(options[:inline], "inline template", handler, locals: keys, format: format)
36
38
  elsif options.key?(:template)
37
39
  if options[:template].respond_to?(:render)
38
40
  options[:template]
39
41
  else
40
- find_template(options[:template], options[:prefixes], false, keys, @details)
42
+ @lookup_context.find_template(options[:template], options[:prefixes], false, keys, @details)
41
43
  end
42
44
  else
43
45
  raise ArgumentError, "You invoked render but did not give any of :partial, :template, :inline, :file, :plain, :html or :body option."
@@ -46,27 +48,25 @@ module ActionView
46
48
 
47
49
  # Renders the given template. A string representing the layout can be
48
50
  # supplied as well.
49
- def render_template(template, layout_name = nil, locals = nil)
50
- view, locals = @view, locals || {}
51
-
52
- render_with_layout(layout_name, locals) do |layout|
51
+ def render_template(view, template, layout_name, locals)
52
+ render_with_layout(view, layout_name, template, locals) do |layout|
53
53
  instrument(:template, identifier: template.identifier, layout: layout.try(:virtual_path)) do
54
54
  template.render(view, locals) { |*name| view._layout_for(*name) }
55
55
  end
56
56
  end
57
57
  end
58
58
 
59
- def render_with_layout(path, locals)
59
+ def render_with_layout(view, path, template, locals)
60
60
  layout = path && find_layout(path, locals.keys, [formats.first])
61
61
  content = yield(layout)
62
62
 
63
- if layout
64
- view = @view
63
+ body = if layout
65
64
  view.view_flow.set(:layout, content)
66
65
  layout.render(view, locals) { |*name| view._layout_for(*name) }
67
66
  else
68
67
  content
69
68
  end
69
+ build_rendered_template(body, template, layout)
70
70
  end
71
71
 
72
72
  # This is the method which actually finds the layout using details in the lookup
@@ -84,16 +84,16 @@ module ActionView
84
84
  when String
85
85
  begin
86
86
  if layout.start_with?("/")
87
- with_fallbacks { find_template(layout, nil, false, [], details) }
87
+ @lookup_context.with_fallbacks.find_template(layout, nil, false, [], details)
88
88
  else
89
- find_template(layout, nil, false, [], details)
89
+ @lookup_context.find_template(layout, nil, false, [], details)
90
90
  end
91
91
  rescue ActionView::MissingTemplate
92
92
  all_details = @details.merge(formats: @lookup_context.default_formats)
93
93
  raise unless template_exists?(layout, nil, false, [], all_details)
94
94
  end
95
95
  when Proc
96
- resolve_layout(layout.call(formats), keys, formats)
96
+ resolve_layout(layout.call(@lookup_context, formats), keys, formats)
97
97
  else
98
98
  layout
99
99
  end