actionview 5.2.8.1 → 6.0.6.1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of actionview might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +280 -94
- data/MIT-LICENSE +1 -1
- data/README.rdoc +5 -3
- data/lib/action_view/base.rb +108 -11
- data/lib/action_view/buffers.rb +15 -0
- data/lib/action_view/cache_expiry.rb +53 -0
- data/lib/action_view/context.rb +5 -9
- data/lib/action_view/digestor.rb +12 -20
- data/lib/action_view/flows.rb +0 -1
- data/lib/action_view/gem_version.rb +3 -3
- data/lib/action_view/helpers/active_model_helper.rb +0 -1
- data/lib/action_view/helpers/asset_tag_helper.rb +8 -31
- data/lib/action_view/helpers/asset_url_helper.rb +4 -3
- data/lib/action_view/helpers/cache_helper.rb +19 -12
- 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 +70 -27
- data/lib/action_view/helpers/form_helper.rb +240 -8
- data/lib/action_view/helpers/form_options_helper.rb +27 -18
- data/lib/action_view/helpers/form_tag_helper.rb +17 -15
- data/lib/action_view/helpers/javascript_helper.rb +9 -8
- data/lib/action_view/helpers/number_helper.rb +8 -2
- data/lib/action_view/helpers/output_safety_helper.rb +1 -1
- data/lib/action_view/helpers/rendering_helper.rb +6 -4
- data/lib/action_view/helpers/sanitize_helper.rb +12 -18
- data/lib/action_view/helpers/tag_helper.rb +8 -7
- data/lib/action_view/helpers/tags/base.rb +9 -6
- data/lib/action_view/helpers/tags/check_box.rb +0 -1
- data/lib/action_view/helpers/tags/collection_check_boxes.rb +0 -1
- data/lib/action_view/helpers/tags/collection_helpers.rb +0 -1
- data/lib/action_view/helpers/tags/collection_radio_buttons.rb +0 -1
- data/lib/action_view/helpers/tags/color_field.rb +1 -2
- data/lib/action_view/helpers/tags/date_field.rb +0 -1
- data/lib/action_view/helpers/tags/date_select.rb +0 -1
- data/lib/action_view/helpers/tags/datetime_field.rb +0 -1
- data/lib/action_view/helpers/tags/datetime_local_field.rb +0 -1
- data/lib/action_view/helpers/tags/label.rb +0 -1
- data/lib/action_view/helpers/tags/month_field.rb +0 -1
- data/lib/action_view/helpers/tags/radio_button.rb +0 -1
- data/lib/action_view/helpers/tags/select.rb +0 -1
- data/lib/action_view/helpers/tags/text_field.rb +0 -1
- data/lib/action_view/helpers/tags/time_field.rb +0 -1
- data/lib/action_view/helpers/tags/translator.rb +1 -6
- data/lib/action_view/helpers/tags/week_field.rb +0 -1
- data/lib/action_view/helpers/text_helper.rb +3 -4
- data/lib/action_view/helpers/translation_helper.rb +19 -17
- 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 -8
- data/lib/action_view/log_subscriber.rb +6 -7
- data/lib/action_view/lookup_context.rb +75 -32
- data/lib/action_view/path_set.rb +5 -11
- data/lib/action_view/railtie.rb +24 -1
- data/lib/action_view/record_identifier.rb +2 -3
- data/lib/action_view/renderer/abstract_renderer.rb +56 -4
- data/lib/action_view/renderer/partial_renderer/collection_caching.rb +63 -17
- data/lib/action_view/renderer/partial_renderer.rb +67 -57
- data/lib/action_view/renderer/renderer.rb +16 -4
- data/lib/action_view/renderer/streaming_template_renderer.rb +5 -7
- data/lib/action_view/renderer/template_renderer.rb +25 -20
- data/lib/action_view/rendering.rb +51 -32
- data/lib/action_view/routing_url_for.rb +12 -11
- data/lib/action_view/template/error.rb +30 -15
- 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 -8
- 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/raw_file.rb +28 -0
- data/lib/action_view/template/resolver.rb +134 -135
- data/lib/action_view/template/sources/file.rb +17 -0
- data/lib/action_view/template/sources.rb +13 -0
- data/lib/action_view/template/text.rb +5 -3
- data/lib/action_view/template.rb +102 -71
- data/lib/action_view/test_case.rb +3 -4
- data/lib/action_view/testing/resolvers.rb +33 -21
- data/lib/action_view/unbound_template.rb +31 -0
- data/lib/action_view/view_paths.rb +25 -2
- data/lib/action_view.rb +4 -2
- data/lib/assets/compiled/rails-ujs.js +30 -4
- metadata +27 -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
|
@@ -58,7 +81,7 @@ module ActionView
|
|
58
81
|
initializer "action_view.per_request_digest_cache" do |app|
|
59
82
|
ActiveSupport.on_load(:action_view) do
|
60
83
|
unless ActionView::Resolver.caching?
|
61
|
-
app.executor.to_run ActionView::
|
84
|
+
app.executor.to_run ActionView::CacheExpiry::Executor.new(watcher: app.config.file_watcher)
|
62
85
|
end
|
63
86
|
end
|
64
87
|
end
|
@@ -59,8 +59,8 @@ module ActionView
|
|
59
59
|
|
60
60
|
include ModelNaming
|
61
61
|
|
62
|
-
JOIN = "_"
|
63
|
-
NEW = "new"
|
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
|
#
|
@@ -95,7 +95,6 @@ module ActionView
|
|
95
95
|
end
|
96
96
|
|
97
97
|
private
|
98
|
-
|
99
98
|
# Returns a string representation of the key attribute(s) that is suitable for use in an HTML DOM id.
|
100
99
|
# This can be overwritten to customize the default generated string representation if desired.
|
101
100
|
# If you need to read back a key from a dom_id in order to query for the underlying database record,
|
@@ -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,8 +27,54 @@ module ActionView
|
|
27
27
|
raise NotImplementedError
|
28
28
|
end
|
29
29
|
|
30
|
-
|
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
|
31
76
|
|
77
|
+
private
|
32
78
|
def extract_details(options) # :doc:
|
33
79
|
@lookup_context.registered_details.each_with_object({}) do |key, details|
|
34
80
|
value = options[key]
|
@@ -38,8 +84,6 @@ module ActionView
|
|
38
84
|
end
|
39
85
|
|
40
86
|
def instrument(name, **options) # :doc:
|
41
|
-
options[:identifier] ||= (@template && @template.identifier) || @path
|
42
|
-
|
43
87
|
ActiveSupport::Notifications.instrument("render_#{name}.action_view", options) do |payload|
|
44
88
|
yield payload
|
45
89
|
end
|
@@ -51,5 +95,13 @@ module ActionView
|
|
51
95
|
|
52
96
|
@lookup_context.formats = formats | @lookup_context.formats
|
53
97
|
end
|
98
|
+
|
99
|
+
def build_rendered_template(content, template, layout = nil)
|
100
|
+
RenderedTemplate.new content, layout, template
|
101
|
+
end
|
102
|
+
|
103
|
+
def build_rendered_collection(templates, spacer)
|
104
|
+
RenderedCollection.new templates, spacer
|
105
|
+
end
|
54
106
|
end
|
55
107
|
end
|
@@ -11,47 +11,93 @@ module ActionView
|
|
11
11
|
end
|
12
12
|
|
13
13
|
private
|
14
|
-
def cache_collection_render(instrumentation_payload)
|
15
|
-
return yield unless @options[:cached]
|
14
|
+
def cache_collection_render(instrumentation_payload, view, template)
|
15
|
+
return yield unless @options[:cached] && view.controller.respond_to?(:perform_caching) && view.controller.perform_caching
|
16
16
|
|
17
|
-
|
18
|
-
|
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, ordered_keys = 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
|
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
|
+
keyed_partials = 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
|
48
|
+
|
49
|
+
ordered_keys.map do |key|
|
50
|
+
keyed_partials[key]
|
51
|
+
end
|
28
52
|
end
|
29
53
|
|
30
54
|
def callable_cache_key?
|
31
55
|
@options[:cached].respond_to?(:call)
|
32
56
|
end
|
33
57
|
|
34
|
-
def collection_by_cache_keys
|
58
|
+
def collection_by_cache_keys(view, template)
|
35
59
|
seed = callable_cache_key? ? @options[:cached] : ->(i) { i }
|
36
60
|
|
37
|
-
|
38
|
-
|
61
|
+
digest_path = view.digest_path_from_template(template)
|
62
|
+
|
63
|
+
@collection.each_with_object([{}, []]) do |item, (hash, ordered_keys)|
|
64
|
+
key = expanded_cache_key(seed.call(item), view, template, digest_path)
|
65
|
+
ordered_keys << key
|
66
|
+
hash[key] = item
|
39
67
|
end
|
40
68
|
end
|
41
69
|
|
42
|
-
def expanded_cache_key(key)
|
43
|
-
key =
|
70
|
+
def expanded_cache_key(key, view, template, digest_path)
|
71
|
+
key = view.combined_fragment_cache_key(view.cache_fragment_name(key, virtual_path: template.virtual_path, digest_path: digest_path))
|
44
72
|
key.frozen? ? key.dup : key # #read_multi & #write may require mutability, Dalli 2.6.0.
|
45
73
|
end
|
46
74
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
75
|
+
# `order_by` is an enumerable object containing keys of the cache,
|
76
|
+
# all keys are passed in whether found already or not.
|
77
|
+
#
|
78
|
+
# `cached_partials` is a hash. If the value exists
|
79
|
+
# it represents the rendered partial from the cache
|
80
|
+
# otherwise `Hash#fetch` will take the value of its block.
|
81
|
+
#
|
82
|
+
# This method expects a block that will return the rendered
|
83
|
+
# partial. An example is to render all results
|
84
|
+
# for each element that was not found in the cache and store it as an array.
|
85
|
+
# Order it so that the first empty cache element in `cached_partials`
|
86
|
+
# corresponds to the first element in `rendered_partials`.
|
87
|
+
#
|
88
|
+
# If the partial is not already cached it will also be
|
89
|
+
# written back to the underlying cache store.
|
90
|
+
def fetch_or_cache_partial(cached_partials, template, order_by:)
|
91
|
+
order_by.each_with_object({}) do |cache_key, hash|
|
92
|
+
hash[cache_key] =
|
93
|
+
if content = cached_partials[cache_key]
|
94
|
+
build_rendered_template(content, template)
|
95
|
+
else
|
96
|
+
yield.tap do |rendered_partial|
|
97
|
+
collection_cache.write(cache_key, rendered_partial.body)
|
98
|
+
end
|
99
|
+
end
|
53
100
|
end
|
54
|
-
end
|
55
101
|
end
|
56
102
|
end
|
57
103
|
end
|
@@ -105,9 +105,6 @@ module ActionView
|
|
105
105
|
#
|
106
106
|
# <%= render(partial: "ad", collection: @advertisements) || "There's no ad to be displayed" %>
|
107
107
|
#
|
108
|
-
# NOTE: Due to backwards compatibility concerns, the collection can't be one of hashes. Normally you'd also
|
109
|
-
# just keep domain objects, like Active Records, in there.
|
110
|
-
#
|
111
108
|
# == \Rendering shared partials
|
112
109
|
#
|
113
110
|
# Two controllers can share a set of partials and render them like this:
|
@@ -295,43 +292,59 @@ module ActionView
|
|
295
292
|
end
|
296
293
|
|
297
294
|
def render(context, options, block)
|
298
|
-
|
299
|
-
|
295
|
+
as = as_variable(options)
|
296
|
+
setup(context, options, as, block)
|
300
297
|
|
301
|
-
@
|
302
|
-
if @
|
303
|
-
@
|
298
|
+
if @path
|
299
|
+
if @has_object || @collection
|
300
|
+
@variable, @variable_counter, @variable_iteration = retrieve_variable(@path, as)
|
301
|
+
@template_keys = retrieve_template_keys(@variable)
|
304
302
|
else
|
305
|
-
|
303
|
+
@template_keys = @locals.keys
|
306
304
|
end
|
305
|
+
template = find_partial(@path, @template_keys)
|
306
|
+
@variable ||= template.variable
|
307
|
+
else
|
308
|
+
if options[:cached]
|
309
|
+
raise NotImplementedError, "render caching requires a template. Please specify a partial when rendering"
|
310
|
+
end
|
311
|
+
template = nil
|
307
312
|
end
|
308
313
|
|
309
314
|
if @collection
|
310
|
-
render_collection
|
315
|
+
render_collection(context, template)
|
311
316
|
else
|
312
|
-
render_partial
|
317
|
+
render_partial(context, template)
|
313
318
|
end
|
314
319
|
end
|
315
320
|
|
316
321
|
private
|
317
|
-
|
318
|
-
|
319
|
-
instrument(:collection, count: @collection.size) do |payload|
|
320
|
-
return
|
321
|
-
|
322
|
-
if @options.key?(:spacer_template)
|
323
|
-
|
322
|
+
def render_collection(view, template)
|
323
|
+
identifier = (template && template.identifier) || @path
|
324
|
+
instrument(:collection, identifier: identifier, count: @collection.size) do |payload|
|
325
|
+
return RenderedCollection.empty(@lookup_context.formats.first) if @collection.blank?
|
326
|
+
|
327
|
+
spacer = if @options.key?(:spacer_template)
|
328
|
+
spacer_template = find_template(@options[:spacer_template], @locals.keys)
|
329
|
+
build_rendered_template(spacer_template.render(view, @locals), spacer_template)
|
330
|
+
else
|
331
|
+
RenderedTemplate::EMPTY_SPACER
|
324
332
|
end
|
325
333
|
|
326
|
-
|
327
|
-
|
328
|
-
|
334
|
+
collection_body = if template
|
335
|
+
cache_collection_render(payload, view, template) do
|
336
|
+
collection_with_template(view, template)
|
337
|
+
end
|
338
|
+
else
|
339
|
+
collection_without_template(view)
|
340
|
+
end
|
341
|
+
build_rendered_collection(collection_body, spacer)
|
329
342
|
end
|
330
343
|
end
|
331
344
|
|
332
|
-
def render_partial
|
333
|
-
instrument(:partial) do |payload|
|
334
|
-
|
345
|
+
def render_partial(view, template)
|
346
|
+
instrument(:partial, identifier: template.identifier) do |payload|
|
347
|
+
locals, block = @locals, @block
|
335
348
|
object, as = @object, @variable
|
336
349
|
|
337
350
|
if !block && (layout = @options[:layout])
|
@@ -341,13 +354,13 @@ module ActionView
|
|
341
354
|
object = locals[as] if object.nil? # Respect object when object is false
|
342
355
|
locals[as] = object if @has_object
|
343
356
|
|
344
|
-
content =
|
357
|
+
content = template.render(view, locals) do |*name|
|
345
358
|
view._layout_for(*name, &block)
|
346
359
|
end
|
347
360
|
|
348
361
|
content = layout.render(view, locals) { content } if layout
|
349
|
-
payload[:cache_hit] = view.view_renderer.cache_hits[
|
350
|
-
content
|
362
|
+
payload[:cache_hit] = view.view_renderer.cache_hits[template.virtual_path]
|
363
|
+
build_rendered_template(content, template, layout)
|
351
364
|
end
|
352
365
|
end
|
353
366
|
|
@@ -358,16 +371,13 @@ module ActionView
|
|
358
371
|
# If +options[:partial]+ is a string, then the <tt>@path</tt> instance variable is
|
359
372
|
# set to that string. Otherwise, the +options[:partial]+ object must
|
360
373
|
# respond to +to_partial_path+ in order to setup the path.
|
361
|
-
def setup(context, options, block)
|
362
|
-
@view = context
|
374
|
+
def setup(context, options, as, block)
|
363
375
|
@options = options
|
364
376
|
@block = block
|
365
377
|
|
366
378
|
@locals = options[:locals] || {}
|
367
379
|
@details = extract_details(options)
|
368
380
|
|
369
|
-
prepend_formats(options[:formats])
|
370
|
-
|
371
381
|
partial = options[:partial]
|
372
382
|
|
373
383
|
if String === partial
|
@@ -381,26 +391,26 @@ module ActionView
|
|
381
391
|
@collection = collection_from_object || collection_from_options
|
382
392
|
|
383
393
|
if @collection
|
384
|
-
paths = @collection_data = @collection.map { |o| partial_path(o) }
|
385
|
-
|
394
|
+
paths = @collection_data = @collection.map { |o| partial_path(o, context) }
|
395
|
+
if paths.uniq.length == 1
|
396
|
+
@path = paths.first
|
397
|
+
else
|
398
|
+
paths.map! { |path| retrieve_variable(path, as).unshift(path) }
|
399
|
+
@path = nil
|
400
|
+
end
|
386
401
|
else
|
387
|
-
@path = partial_path
|
402
|
+
@path = partial_path(@object, context)
|
388
403
|
end
|
389
404
|
end
|
390
405
|
|
406
|
+
self
|
407
|
+
end
|
408
|
+
|
409
|
+
def as_variable(options)
|
391
410
|
if as = options[:as]
|
392
411
|
raise_invalid_option_as(as) unless /\A[a-z_]\w*\z/.match?(as.to_s)
|
393
|
-
as
|
412
|
+
as.to_sym
|
394
413
|
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) }
|
401
|
-
end
|
402
|
-
|
403
|
-
self
|
404
414
|
end
|
405
415
|
|
406
416
|
def collection_from_options
|
@@ -414,8 +424,8 @@ module ActionView
|
|
414
424
|
@object.to_ary if @object.respond_to?(:to_ary)
|
415
425
|
end
|
416
426
|
|
417
|
-
def find_partial
|
418
|
-
find_template(
|
427
|
+
def find_partial(path, template_keys)
|
428
|
+
find_template(path, template_keys)
|
419
429
|
end
|
420
430
|
|
421
431
|
def find_template(path, locals)
|
@@ -423,8 +433,8 @@ module ActionView
|
|
423
433
|
@lookup_context.find_template(path, prefixes, true, locals, @details)
|
424
434
|
end
|
425
435
|
|
426
|
-
def collection_with_template
|
427
|
-
|
436
|
+
def collection_with_template(view, template)
|
437
|
+
locals = @locals
|
428
438
|
as, counter, iteration = @variable, @variable_counter, @variable_iteration
|
429
439
|
|
430
440
|
if layout = @options[:layout]
|
@@ -441,12 +451,12 @@ module ActionView
|
|
441
451
|
content = template.render(view, locals)
|
442
452
|
content = layout.render(view, locals) { content } if layout
|
443
453
|
partial_iteration.iterate!
|
444
|
-
content
|
454
|
+
build_rendered_template(content, template, layout)
|
445
455
|
end
|
446
456
|
end
|
447
457
|
|
448
|
-
def collection_without_template
|
449
|
-
|
458
|
+
def collection_without_template(view)
|
459
|
+
locals, collection_data = @locals, @collection_data
|
450
460
|
cache = {}
|
451
461
|
keys = @locals.keys
|
452
462
|
|
@@ -463,7 +473,7 @@ module ActionView
|
|
463
473
|
template = (cache[path] ||= find_template(path, keys + [as, counter, iteration]))
|
464
474
|
content = template.render(view, locals)
|
465
475
|
partial_iteration.iterate!
|
466
|
-
content
|
476
|
+
build_rendered_template(content, template)
|
467
477
|
end
|
468
478
|
end
|
469
479
|
|
@@ -474,7 +484,7 @@ module ActionView
|
|
474
484
|
#
|
475
485
|
# If +prefix_partial_path_with_controller_namespace+ is true, then this
|
476
486
|
# method will prefix the partial paths with a namespace.
|
477
|
-
def partial_path(object
|
487
|
+
def partial_path(object, view)
|
478
488
|
object = object.to_model if object.respond_to?(:to_model)
|
479
489
|
|
480
490
|
path = if object.respond_to?(:to_partial_path)
|
@@ -483,7 +493,7 @@ module ActionView
|
|
483
493
|
raise ArgumentError.new("'#{object.inspect}' is not an ActiveModel-compatible object. It must implement :to_partial_path.")
|
484
494
|
end
|
485
495
|
|
486
|
-
if
|
496
|
+
if view.prefix_partial_path_with_controller_namespace
|
487
497
|
prefixed_partial_names[path] ||= merge_prefix_into_object_path(@context_prefix, path.dup)
|
488
498
|
else
|
489
499
|
path
|
@@ -511,9 +521,9 @@ module ActionView
|
|
511
521
|
end
|
512
522
|
end
|
513
523
|
|
514
|
-
def retrieve_template_keys
|
524
|
+
def retrieve_template_keys(variable)
|
515
525
|
keys = @locals.keys
|
516
|
-
keys <<
|
526
|
+
keys << variable
|
517
527
|
if @collection
|
518
528
|
keys << @variable_counter
|
519
529
|
keys << @variable_iteration
|
@@ -523,7 +533,7 @@ module ActionView
|
|
523
533
|
|
524
534
|
def retrieve_variable(path, as)
|
525
535
|
variable = as || begin
|
526
|
-
base = path[-1] == "/"
|
536
|
+
base = path[-1] == "/" ? "" : File.basename(path)
|
527
537
|
raise_invalid_identifier(path) unless base =~ /\A_?(.*?)(?:\.\w+)*\z/
|
528
538
|
$1.to_sym
|
529
539
|
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
|
@@ -27,14 +27,13 @@ module ActionView
|
|
27
27
|
end
|
28
28
|
|
29
29
|
private
|
30
|
-
|
31
30
|
# This is the same logging logic as in ShowExceptions middleware.
|
32
31
|
def log_error(exception)
|
33
32
|
logger = ActionView::Base.logger
|
34
33
|
return unless logger
|
35
34
|
|
36
|
-
message = "\n#{exception.class} (#{exception.message}):\n"
|
37
|
-
message << exception.
|
35
|
+
message = +"\n#{exception.class} (#{exception.message}):\n"
|
36
|
+
message << exception.annotated_source_code.to_s if exception.respond_to?(:annotated_source_code)
|
38
37
|
message << " " << exception.backtrace.join("\n ")
|
39
38
|
logger.fatal("#{message}\n\n")
|
40
39
|
end
|
@@ -43,19 +42,18 @@ module ActionView
|
|
43
42
|
# For streaming, instead of rendering a given a template, we return a Body
|
44
43
|
# object that responds to each. This object is initialized with a block
|
45
44
|
# 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?
|
45
|
+
def render_template(view, template, layout_name = nil, locals = {}) #:nodoc:
|
46
|
+
return [super.body] unless layout_name && template.supports_streaming?
|
48
47
|
|
49
48
|
locals ||= {}
|
50
49
|
layout = layout_name && find_layout(layout_name, locals.keys, [formats.first])
|
51
50
|
|
52
51
|
Body.new do |buffer|
|
53
|
-
delayed_render(buffer, template, layout,
|
52
|
+
delayed_render(buffer, template, layout, view, locals)
|
54
53
|
end
|
55
54
|
end
|
56
55
|
|
57
56
|
private
|
58
|
-
|
59
57
|
def delayed_render(buffer, template, layout, view, locals)
|
60
58
|
# Wrap the given buffer in the StreamingBuffer and pass it to the
|
61
59
|
# underlying template handler. Now, every time something is concatenated
|