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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +121 -152
- data/MIT-LICENSE +1 -1
- data/README.rdoc +1 -1
- data/lib/action_view/base.rb +107 -10
- data/lib/action_view/buffers.rb +15 -0
- data/lib/action_view/context.rb +5 -9
- data/lib/action_view/digestor.rb +8 -11
- data/lib/action_view/file_template.rb +33 -0
- data/lib/action_view/gem_version.rb +4 -4
- data/lib/action_view/helpers/asset_tag_helper.rb +7 -30
- data/lib/action_view/helpers/asset_url_helper.rb +4 -3
- data/lib/action_view/helpers/cache_helper.rb +18 -10
- data/lib/action_view/helpers/capture_helper.rb +4 -0
- data/lib/action_view/helpers/csp_helper.rb +4 -2
- data/lib/action_view/helpers/csrf_helper.rb +1 -1
- data/lib/action_view/helpers/date_helper.rb +69 -25
- data/lib/action_view/helpers/form_helper.rb +240 -8
- data/lib/action_view/helpers/form_options_helper.rb +23 -15
- data/lib/action_view/helpers/form_tag_helper.rb +9 -9
- data/lib/action_view/helpers/javascript_helper.rb +10 -11
- data/lib/action_view/helpers/number_helper.rb +5 -0
- data/lib/action_view/helpers/rendering_helper.rb +6 -4
- data/lib/action_view/helpers/sanitize_helper.rb +3 -3
- data/lib/action_view/helpers/tag_helper.rb +13 -43
- data/lib/action_view/helpers/tags/base.rb +9 -5
- data/lib/action_view/helpers/tags/color_field.rb +1 -1
- data/lib/action_view/helpers/tags/translator.rb +1 -6
- data/lib/action_view/helpers/text_helper.rb +3 -3
- data/lib/action_view/helpers/translation_helper.rb +12 -19
- data/lib/action_view/helpers/url_helper.rb +14 -14
- data/lib/action_view/helpers.rb +0 -2
- data/lib/action_view/layouts.rb +5 -5
- data/lib/action_view/log_subscriber.rb +6 -6
- data/lib/action_view/lookup_context.rb +63 -28
- data/lib/action_view/railtie.rb +23 -0
- data/lib/action_view/record_identifier.rb +2 -2
- data/lib/action_view/renderer/abstract_renderer.rb +56 -3
- data/lib/action_view/renderer/partial_renderer/collection_caching.rb +49 -10
- data/lib/action_view/renderer/partial_renderer.rb +66 -52
- data/lib/action_view/renderer/renderer.rb +16 -4
- data/lib/action_view/renderer/streaming_template_renderer.rb +4 -4
- data/lib/action_view/renderer/template_renderer.rb +18 -18
- data/lib/action_view/rendering.rb +49 -30
- data/lib/action_view/routing_url_for.rb +12 -11
- data/lib/action_view/template/handlers/builder.rb +2 -2
- data/lib/action_view/template/handlers/erb/erubi.rb +7 -3
- data/lib/action_view/template/handlers/erb.rb +17 -7
- data/lib/action_view/template/handlers/html.rb +1 -1
- data/lib/action_view/template/handlers/raw.rb +2 -2
- data/lib/action_view/template/handlers.rb +27 -1
- data/lib/action_view/template/html.rb +14 -5
- data/lib/action_view/template/inline.rb +22 -0
- data/lib/action_view/template/resolver.rb +70 -23
- data/lib/action_view/template/text.rb +5 -3
- data/lib/action_view/template.rb +75 -36
- data/lib/action_view/test_case.rb +1 -1
- data/lib/action_view/testing/resolvers.rb +7 -5
- data/lib/action_view/view_paths.rb +25 -1
- data/lib/action_view.rb +2 -2
- data/lib/assets/compiled/rails-ujs.js +39 -22
- metadata +19 -18
- data/lib/action_view/helpers/record_tag_helper.rb +0 -23
data/lib/action_view/railtie.rb
CHANGED
@@ -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
|
@@ -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 :
|
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
|
-
|
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 =
|
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
|
-
|
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
|
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
|
-
|
299
|
-
|
298
|
+
as = as_variable(options)
|
299
|
+
setup(context, options, as, block)
|
300
300
|
|
301
|
-
@
|
302
|
-
if @
|
303
|
-
@
|
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
|
-
|
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
|
-
|
320
|
-
|
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
|
-
|
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
|
-
|
327
|
-
|
328
|
-
|
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
|
-
|
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 =
|
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[
|
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
|
-
|
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
|
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(
|
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
|
-
|
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
|
-
|
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
|
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
|
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 <<
|
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] == "/"
|
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
|
-
|
27
|
+
render_partial_to_object(context, options)
|
24
28
|
else
|
25
|
-
|
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
|
-
|
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
|
-
|
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"
|
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,
|
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.
|
11
|
+
prepend_formats(template.format)
|
13
12
|
|
14
|
-
|
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
|
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
|
-
|
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
|
50
|
-
view,
|
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
|
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
|