actionview 6.0.6.1 → 6.1.7.2
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 +226 -220
- data/MIT-LICENSE +1 -2
- data/lib/action_view/base.rb +18 -49
- data/lib/action_view/cache_expiry.rb +1 -2
- data/lib/action_view/dependency_tracker.rb +10 -4
- data/lib/action_view/digestor.rb +3 -2
- data/lib/action_view/gem_version.rb +3 -3
- data/lib/action_view/helpers/asset_tag_helper.rb +57 -17
- data/lib/action_view/helpers/asset_url_helper.rb +6 -4
- data/lib/action_view/helpers/atom_feed_helper.rb +2 -1
- data/lib/action_view/helpers/cache_helper.rb +10 -16
- data/lib/action_view/helpers/date_helper.rb +6 -5
- data/lib/action_view/helpers/form_helper.rb +66 -30
- data/lib/action_view/helpers/form_options_helper.rb +7 -16
- data/lib/action_view/helpers/form_tag_helper.rb +4 -3
- data/lib/action_view/helpers/javascript_helper.rb +3 -3
- data/lib/action_view/helpers/number_helper.rb +6 -6
- data/lib/action_view/helpers/rendering_helper.rb +11 -3
- data/lib/action_view/helpers/tag_helper.rb +98 -22
- data/lib/action_view/helpers/tags/base.rb +10 -6
- data/lib/action_view/helpers/tags/check_box.rb +1 -1
- data/lib/action_view/helpers/tags/date_field.rb +1 -1
- data/lib/action_view/helpers/tags/date_select.rb +2 -2
- data/lib/action_view/helpers/tags/datetime_local_field.rb +1 -1
- data/lib/action_view/helpers/tags/hidden_field.rb +4 -0
- data/lib/action_view/helpers/tags/label.rb +4 -0
- data/lib/action_view/helpers/tags/month_field.rb +1 -1
- data/lib/action_view/helpers/tags/select.rb +1 -1
- data/lib/action_view/helpers/tags/time_field.rb +1 -1
- data/lib/action_view/helpers/tags/week_field.rb +1 -1
- data/lib/action_view/helpers/text_helper.rb +2 -2
- data/lib/action_view/helpers/translation_helper.rb +88 -50
- data/lib/action_view/helpers/url_helper.rb +136 -24
- data/lib/action_view/layouts.rb +3 -2
- data/lib/action_view/log_subscriber.rb +26 -10
- data/lib/action_view/lookup_context.rb +3 -18
- data/lib/action_view/path_set.rb +0 -3
- data/lib/action_view/railtie.rb +39 -46
- data/lib/action_view/renderer/abstract_renderer.rb +93 -14
- data/lib/action_view/renderer/collection_renderer.rb +196 -0
- data/lib/action_view/renderer/object_renderer.rb +34 -0
- data/lib/action_view/renderer/partial_renderer/collection_caching.rb +25 -26
- data/lib/action_view/renderer/partial_renderer.rb +20 -282
- data/lib/action_view/renderer/renderer.rb +44 -1
- data/lib/action_view/renderer/streaming_template_renderer.rb +5 -1
- data/lib/action_view/renderer/template_renderer.rb +15 -12
- data/lib/action_view/rendering.rb +3 -1
- data/lib/action_view/routing_url_for.rb +1 -1
- data/lib/action_view/template/handlers/erb/erubi.rb +9 -7
- data/lib/action_view/template/handlers/erb.rb +10 -14
- data/lib/action_view/template/handlers.rb +0 -26
- data/lib/action_view/template/html.rb +1 -11
- data/lib/action_view/template/raw_file.rb +0 -3
- data/lib/action_view/template/renderable.rb +24 -0
- data/lib/action_view/template/resolver.rb +82 -40
- data/lib/action_view/template/text.rb +0 -3
- data/lib/action_view/template.rb +9 -49
- data/lib/action_view/test_case.rb +18 -25
- data/lib/action_view/testing/resolvers.rb +10 -31
- data/lib/action_view/unbound_template.rb +3 -3
- data/lib/action_view/view_paths.rb +34 -36
- data/lib/action_view.rb +4 -1
- data/lib/assets/compiled/rails-ujs.js +2 -2
- metadata +14 -11
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "active_support/core_ext/enumerable"
|
4
|
+
|
3
5
|
module ActionView
|
4
6
|
module CollectionCaching # :nodoc:
|
5
7
|
extend ActiveSupport::Concern
|
@@ -11,13 +13,19 @@ module ActionView
|
|
11
13
|
end
|
12
14
|
|
13
15
|
private
|
14
|
-
def
|
15
|
-
|
16
|
+
def will_cache?(options, view)
|
17
|
+
options[:cached] && view.controller.respond_to?(:perform_caching) && view.controller.perform_caching
|
18
|
+
end
|
19
|
+
|
20
|
+
def cache_collection_render(instrumentation_payload, view, template, collection)
|
21
|
+
return yield(collection) unless will_cache?(@options, view)
|
22
|
+
|
23
|
+
collection_iterator = collection
|
16
24
|
|
17
25
|
# Result is a hash with the key represents the
|
18
26
|
# key used for cache lookup and the value is the item
|
19
27
|
# on which the partial is being rendered
|
20
|
-
keyed_collection, ordered_keys = collection_by_cache_keys(view, template)
|
28
|
+
keyed_collection, ordered_keys = collection_by_cache_keys(view, template, collection)
|
21
29
|
|
22
30
|
# Pull all partials from cache
|
23
31
|
# Result is a hash, key matches the entry in
|
@@ -27,17 +35,9 @@ module ActionView
|
|
27
35
|
instrumentation_payload[:cache_hits] = cached_partials.size
|
28
36
|
|
29
37
|
# Extract the items for the keys that are not found
|
30
|
-
|
31
|
-
|
32
|
-
|
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.
|
40
|
-
rendered_partials = @collection.empty? ? [] : yield
|
38
|
+
collection = keyed_collection.reject { |key, _| cached_partials.key?(key) }.values
|
39
|
+
|
40
|
+
rendered_partials = collection.empty? ? [] : yield(collection_iterator.from_collection(collection))
|
41
41
|
|
42
42
|
index = 0
|
43
43
|
keyed_partials = fetch_or_cache_partial(cached_partials, template, order_by: keyed_collection.each_key) do
|
@@ -55,12 +55,12 @@ module ActionView
|
|
55
55
|
@options[:cached].respond_to?(:call)
|
56
56
|
end
|
57
57
|
|
58
|
-
def collection_by_cache_keys(view, template)
|
58
|
+
def collection_by_cache_keys(view, template, collection)
|
59
59
|
seed = callable_cache_key? ? @options[:cached] : ->(i) { i }
|
60
60
|
|
61
61
|
digest_path = view.digest_path_from_template(template)
|
62
62
|
|
63
|
-
|
63
|
+
collection.each_with_object([{}, []]) do |item, (hash, ordered_keys)|
|
64
64
|
key = expanded_cache_key(seed.call(item), view, template, digest_path)
|
65
65
|
ordered_keys << key
|
66
66
|
hash[key] = item
|
@@ -68,7 +68,7 @@ module ActionView
|
|
68
68
|
end
|
69
69
|
|
70
70
|
def expanded_cache_key(key, view, template, digest_path)
|
71
|
-
key = view.combined_fragment_cache_key(view.cache_fragment_name(key,
|
71
|
+
key = view.combined_fragment_cache_key(view.cache_fragment_name(key, digest_path: digest_path))
|
72
72
|
key.frozen? ? key.dup : key # #read_multi & #write may require mutability, Dalli 2.6.0.
|
73
73
|
end
|
74
74
|
|
@@ -88,16 +88,15 @@ module ActionView
|
|
88
88
|
# If the partial is not already cached it will also be
|
89
89
|
# written back to the underlying cache store.
|
90
90
|
def fetch_or_cache_partial(cached_partials, template, order_by:)
|
91
|
-
order_by.
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
end
|
99
|
-
end
|
91
|
+
order_by.index_with do |cache_key|
|
92
|
+
if content = cached_partials[cache_key]
|
93
|
+
build_rendered_template(content, template)
|
94
|
+
else
|
95
|
+
yield.tap do |rendered_partial|
|
96
|
+
collection_cache.write(cache_key, rendered_partial.body)
|
97
|
+
end
|
100
98
|
end
|
99
|
+
end
|
101
100
|
end
|
102
101
|
end
|
103
102
|
end
|
@@ -1,36 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "concurrent/map"
|
4
3
|
require "action_view/renderer/partial_renderer/collection_caching"
|
5
4
|
|
6
5
|
module ActionView
|
7
|
-
class PartialIteration
|
8
|
-
# The number of iterations that will be done by the partial.
|
9
|
-
attr_reader :size
|
10
|
-
|
11
|
-
# The current iteration of the partial.
|
12
|
-
attr_reader :index
|
13
|
-
|
14
|
-
def initialize(size)
|
15
|
-
@size = size
|
16
|
-
@index = 0
|
17
|
-
end
|
18
|
-
|
19
|
-
# Check if this is the first iteration of the partial.
|
20
|
-
def first?
|
21
|
-
index == 0
|
22
|
-
end
|
23
|
-
|
24
|
-
# Check if this is the last iteration of the partial.
|
25
|
-
def last?
|
26
|
-
index == size - 1
|
27
|
-
end
|
28
|
-
|
29
|
-
def iterate! # :nodoc:
|
30
|
-
@index += 1
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
6
|
# = Action View Partials
|
35
7
|
#
|
36
8
|
# There's also a convenience method for rendering sub templates within the current controller that depends on a
|
@@ -282,281 +254,47 @@ module ActionView
|
|
282
254
|
class PartialRenderer < AbstractRenderer
|
283
255
|
include CollectionCaching
|
284
256
|
|
285
|
-
|
286
|
-
|
257
|
+
def initialize(lookup_context, options)
|
258
|
+
super(lookup_context)
|
259
|
+
@options = options
|
260
|
+
@locals = @options[:locals] || {}
|
261
|
+
@details = extract_details(@options)
|
287
262
|
end
|
288
263
|
|
289
|
-
def
|
290
|
-
|
291
|
-
@context_prefix = @lookup_context.prefixes.first
|
292
|
-
end
|
264
|
+
def render(partial, context, block)
|
265
|
+
template = find_template(partial, template_keys(partial))
|
293
266
|
|
294
|
-
|
295
|
-
|
296
|
-
setup(context, options, as, block)
|
297
|
-
|
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)
|
302
|
-
else
|
303
|
-
@template_keys = @locals.keys
|
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
|
267
|
+
if !block && (layout = @options[:layout])
|
268
|
+
layout = find_template(layout.to_s, template_keys(partial))
|
312
269
|
end
|
313
270
|
|
314
|
-
|
315
|
-
render_collection(context, template)
|
316
|
-
else
|
317
|
-
render_partial(context, template)
|
318
|
-
end
|
271
|
+
render_partial_template(context, @locals, template, layout, block)
|
319
272
|
end
|
320
273
|
|
321
274
|
private
|
322
|
-
def
|
323
|
-
|
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
|
332
|
-
end
|
333
|
-
|
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)
|
342
|
-
end
|
275
|
+
def template_keys(_)
|
276
|
+
@locals.keys
|
343
277
|
end
|
344
278
|
|
345
|
-
def
|
346
|
-
instrument(
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
end
|
353
|
-
|
354
|
-
object = locals[as] if object.nil? # Respect object when object is false
|
355
|
-
locals[as] = object if @has_object
|
356
|
-
|
357
|
-
content = template.render(view, locals) do |*name|
|
279
|
+
def render_partial_template(view, locals, template, layout, block)
|
280
|
+
ActiveSupport::Notifications.instrument(
|
281
|
+
"render_partial.action_view",
|
282
|
+
identifier: template.identifier,
|
283
|
+
layout: layout && layout.virtual_path
|
284
|
+
) do |payload|
|
285
|
+
content = template.render(view, locals, add_to_stack: !block) do |*name|
|
358
286
|
view._layout_for(*name, &block)
|
359
287
|
end
|
360
288
|
|
361
289
|
content = layout.render(view, locals) { content } if layout
|
362
290
|
payload[:cache_hit] = view.view_renderer.cache_hits[template.virtual_path]
|
363
|
-
build_rendered_template(content, template
|
364
|
-
end
|
365
|
-
end
|
366
|
-
|
367
|
-
# Sets up instance variables needed for rendering a partial. This method
|
368
|
-
# finds the options and details and extracts them. The method also contains
|
369
|
-
# logic that handles the type of object passed in as the partial.
|
370
|
-
#
|
371
|
-
# If +options[:partial]+ is a string, then the <tt>@path</tt> instance variable is
|
372
|
-
# set to that string. Otherwise, the +options[:partial]+ object must
|
373
|
-
# respond to +to_partial_path+ in order to setup the path.
|
374
|
-
def setup(context, options, as, block)
|
375
|
-
@options = options
|
376
|
-
@block = block
|
377
|
-
|
378
|
-
@locals = options[:locals] || {}
|
379
|
-
@details = extract_details(options)
|
380
|
-
|
381
|
-
partial = options[:partial]
|
382
|
-
|
383
|
-
if String === partial
|
384
|
-
@has_object = options.key?(:object)
|
385
|
-
@object = options[:object]
|
386
|
-
@collection = collection_from_options
|
387
|
-
@path = partial
|
388
|
-
else
|
389
|
-
@has_object = true
|
390
|
-
@object = partial
|
391
|
-
@collection = collection_from_object || collection_from_options
|
392
|
-
|
393
|
-
if @collection
|
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
|
401
|
-
else
|
402
|
-
@path = partial_path(@object, context)
|
403
|
-
end
|
404
|
-
end
|
405
|
-
|
406
|
-
self
|
407
|
-
end
|
408
|
-
|
409
|
-
def as_variable(options)
|
410
|
-
if as = options[:as]
|
411
|
-
raise_invalid_option_as(as) unless /\A[a-z_]\w*\z/.match?(as.to_s)
|
412
|
-
as.to_sym
|
413
|
-
end
|
414
|
-
end
|
415
|
-
|
416
|
-
def collection_from_options
|
417
|
-
if @options.key?(:collection)
|
418
|
-
collection = @options[:collection]
|
419
|
-
collection ? collection.to_a : []
|
291
|
+
build_rendered_template(content, template)
|
420
292
|
end
|
421
293
|
end
|
422
294
|
|
423
|
-
def collection_from_object
|
424
|
-
@object.to_ary if @object.respond_to?(:to_ary)
|
425
|
-
end
|
426
|
-
|
427
|
-
def find_partial(path, template_keys)
|
428
|
-
find_template(path, template_keys)
|
429
|
-
end
|
430
|
-
|
431
295
|
def find_template(path, locals)
|
432
296
|
prefixes = path.include?(?/) ? [] : @lookup_context.prefixes
|
433
297
|
@lookup_context.find_template(path, prefixes, true, locals, @details)
|
434
298
|
end
|
435
|
-
|
436
|
-
def collection_with_template(view, template)
|
437
|
-
locals = @locals
|
438
|
-
as, counter, iteration = @variable, @variable_counter, @variable_iteration
|
439
|
-
|
440
|
-
if layout = @options[:layout]
|
441
|
-
layout = find_template(layout, @template_keys)
|
442
|
-
end
|
443
|
-
|
444
|
-
partial_iteration = PartialIteration.new(@collection.size)
|
445
|
-
locals[iteration] = partial_iteration
|
446
|
-
|
447
|
-
@collection.map do |object|
|
448
|
-
locals[as] = object
|
449
|
-
locals[counter] = partial_iteration.index
|
450
|
-
|
451
|
-
content = template.render(view, locals)
|
452
|
-
content = layout.render(view, locals) { content } if layout
|
453
|
-
partial_iteration.iterate!
|
454
|
-
build_rendered_template(content, template, layout)
|
455
|
-
end
|
456
|
-
end
|
457
|
-
|
458
|
-
def collection_without_template(view)
|
459
|
-
locals, collection_data = @locals, @collection_data
|
460
|
-
cache = {}
|
461
|
-
keys = @locals.keys
|
462
|
-
|
463
|
-
partial_iteration = PartialIteration.new(@collection.size)
|
464
|
-
|
465
|
-
@collection.map do |object|
|
466
|
-
index = partial_iteration.index
|
467
|
-
path, as, counter, iteration = collection_data[index]
|
468
|
-
|
469
|
-
locals[as] = object
|
470
|
-
locals[counter] = index
|
471
|
-
locals[iteration] = partial_iteration
|
472
|
-
|
473
|
-
template = (cache[path] ||= find_template(path, keys + [as, counter, iteration]))
|
474
|
-
content = template.render(view, locals)
|
475
|
-
partial_iteration.iterate!
|
476
|
-
build_rendered_template(content, template)
|
477
|
-
end
|
478
|
-
end
|
479
|
-
|
480
|
-
# Obtains the path to where the object's partial is located. If the object
|
481
|
-
# responds to +to_partial_path+, then +to_partial_path+ will be called and
|
482
|
-
# will provide the path. If the object does not respond to +to_partial_path+,
|
483
|
-
# then an +ArgumentError+ is raised.
|
484
|
-
#
|
485
|
-
# If +prefix_partial_path_with_controller_namespace+ is true, then this
|
486
|
-
# method will prefix the partial paths with a namespace.
|
487
|
-
def partial_path(object, view)
|
488
|
-
object = object.to_model if object.respond_to?(:to_model)
|
489
|
-
|
490
|
-
path = if object.respond_to?(:to_partial_path)
|
491
|
-
object.to_partial_path
|
492
|
-
else
|
493
|
-
raise ArgumentError.new("'#{object.inspect}' is not an ActiveModel-compatible object. It must implement :to_partial_path.")
|
494
|
-
end
|
495
|
-
|
496
|
-
if view.prefix_partial_path_with_controller_namespace
|
497
|
-
prefixed_partial_names[path] ||= merge_prefix_into_object_path(@context_prefix, path.dup)
|
498
|
-
else
|
499
|
-
path
|
500
|
-
end
|
501
|
-
end
|
502
|
-
|
503
|
-
def prefixed_partial_names
|
504
|
-
@prefixed_partial_names ||= PREFIXED_PARTIAL_NAMES[@context_prefix]
|
505
|
-
end
|
506
|
-
|
507
|
-
def merge_prefix_into_object_path(prefix, object_path)
|
508
|
-
if prefix.include?(?/) && object_path.include?(?/)
|
509
|
-
prefixes = []
|
510
|
-
prefix_array = File.dirname(prefix).split("/")
|
511
|
-
object_path_array = object_path.split("/")[0..-3] # skip model dir & partial
|
512
|
-
|
513
|
-
prefix_array.each_with_index do |dir, index|
|
514
|
-
break if dir == object_path_array[index]
|
515
|
-
prefixes << dir
|
516
|
-
end
|
517
|
-
|
518
|
-
(prefixes << object_path).join("/")
|
519
|
-
else
|
520
|
-
object_path
|
521
|
-
end
|
522
|
-
end
|
523
|
-
|
524
|
-
def retrieve_template_keys(variable)
|
525
|
-
keys = @locals.keys
|
526
|
-
keys << variable
|
527
|
-
if @collection
|
528
|
-
keys << @variable_counter
|
529
|
-
keys << @variable_iteration
|
530
|
-
end
|
531
|
-
keys
|
532
|
-
end
|
533
|
-
|
534
|
-
def retrieve_variable(path, as)
|
535
|
-
variable = as || begin
|
536
|
-
base = path[-1] == "/" ? "" : File.basename(path)
|
537
|
-
raise_invalid_identifier(path) unless base =~ /\A_?(.*?)(?:\.\w+)*\z/
|
538
|
-
$1.to_sym
|
539
|
-
end
|
540
|
-
if @collection
|
541
|
-
variable_counter = :"#{variable}_counter"
|
542
|
-
variable_iteration = :"#{variable}_iteration"
|
543
|
-
end
|
544
|
-
[variable, variable_counter, variable_iteration]
|
545
|
-
end
|
546
|
-
|
547
|
-
IDENTIFIER_ERROR_MESSAGE = "The partial name (%s) is not a valid Ruby identifier; " \
|
548
|
-
"make sure your partial name starts with underscore."
|
549
|
-
|
550
|
-
OPTION_AS_ERROR_MESSAGE = "The value (%s) of the option `as` is not a valid Ruby identifier; " \
|
551
|
-
"make sure it starts with lowercase letter, " \
|
552
|
-
"and is followed by any combination of letters, numbers and underscores."
|
553
|
-
|
554
|
-
def raise_invalid_identifier(path)
|
555
|
-
raise ArgumentError.new(IDENTIFIER_ERROR_MESSAGE % (path))
|
556
|
-
end
|
557
|
-
|
558
|
-
def raise_invalid_option_as(as)
|
559
|
-
raise ArgumentError.new(OPTION_AS_ERROR_MESSAGE % (as))
|
560
|
-
end
|
561
299
|
end
|
562
300
|
end
|
@@ -62,7 +62,50 @@ module ActionView
|
|
62
62
|
end
|
63
63
|
|
64
64
|
def render_partial_to_object(context, options, &block) #:nodoc:
|
65
|
-
|
65
|
+
partial = options[:partial]
|
66
|
+
if String === partial
|
67
|
+
collection = collection_from_options(options)
|
68
|
+
|
69
|
+
if collection
|
70
|
+
# Collection + Partial
|
71
|
+
renderer = CollectionRenderer.new(@lookup_context, options)
|
72
|
+
renderer.render_collection_with_partial(collection, partial, context, block)
|
73
|
+
else
|
74
|
+
if options.key?(:object)
|
75
|
+
# Object + Partial
|
76
|
+
renderer = ObjectRenderer.new(@lookup_context, options)
|
77
|
+
renderer.render_object_with_partial(options[:object], partial, context, block)
|
78
|
+
else
|
79
|
+
# Partial
|
80
|
+
renderer = PartialRenderer.new(@lookup_context, options)
|
81
|
+
renderer.render(partial, context, block)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
else
|
85
|
+
collection = collection_from_object(partial) || collection_from_options(options)
|
86
|
+
|
87
|
+
if collection
|
88
|
+
# Collection + Derived Partial
|
89
|
+
renderer = CollectionRenderer.new(@lookup_context, options)
|
90
|
+
renderer.render_collection_derive_partial(collection, context, block)
|
91
|
+
else
|
92
|
+
# Object + Derived Partial
|
93
|
+
renderer = ObjectRenderer.new(@lookup_context, options)
|
94
|
+
renderer.render_object_derive_partial(partial, context, block)
|
95
|
+
end
|
96
|
+
end
|
66
97
|
end
|
98
|
+
|
99
|
+
private
|
100
|
+
def collection_from_options(options)
|
101
|
+
if options.key?(:collection)
|
102
|
+
collection = options[:collection]
|
103
|
+
collection || []
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def collection_from_object(object)
|
108
|
+
object if object.respond_to?(:to_ary)
|
109
|
+
end
|
67
110
|
end
|
68
111
|
end
|
@@ -62,7 +62,11 @@ module ActionView
|
|
62
62
|
output = ActionView::StreamingBuffer.new(buffer)
|
63
63
|
yielder = lambda { |*name| view._layout_for(*name) }
|
64
64
|
|
65
|
-
instrument(
|
65
|
+
ActiveSupport::Notifications.instrument(
|
66
|
+
"render_template.action_view",
|
67
|
+
identifier: template.identifier,
|
68
|
+
layout: layout && layout.virtual_path
|
69
|
+
) do
|
66
70
|
outer_config = I18n.config
|
67
71
|
fiber = Fiber.new do
|
68
72
|
I18n.config = outer_config
|
@@ -1,7 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "active_support/core_ext/object/try"
|
4
|
-
|
5
3
|
module ActionView
|
6
4
|
class TemplateRenderer < AbstractRenderer #:nodoc:
|
7
5
|
def render(context, options)
|
@@ -28,8 +26,7 @@ module ActionView
|
|
28
26
|
if File.exist?(options[:file])
|
29
27
|
Template::RawFile.new(options[:file])
|
30
28
|
else
|
31
|
-
|
32
|
-
@lookup_context.with_fallbacks.find_template(options[:file], nil, false, keys, @details)
|
29
|
+
raise ArgumentError, "`render file:` should be given the absolute path to a file. '#{options[:file]}' was given instead"
|
33
30
|
end
|
34
31
|
elsif options.key?(:inline)
|
35
32
|
handler = Template.handler_for_extension(options[:type] || "erb")
|
@@ -39,6 +36,8 @@ module ActionView
|
|
39
36
|
@lookup_context.formats.first
|
40
37
|
end
|
41
38
|
Template::Inline.new(options[:inline], "inline template", handler, locals: keys, format: format)
|
39
|
+
elsif options.key?(:renderable)
|
40
|
+
Template::Renderable.new(options[:renderable])
|
42
41
|
elsif options.key?(:template)
|
43
42
|
if options[:template].respond_to?(:render)
|
44
43
|
options[:template]
|
@@ -54,7 +53,11 @@ module ActionView
|
|
54
53
|
# supplied as well.
|
55
54
|
def render_template(view, template, layout_name, locals)
|
56
55
|
render_with_layout(view, template, layout_name, locals) do |layout|
|
57
|
-
instrument(
|
56
|
+
ActiveSupport::Notifications.instrument(
|
57
|
+
"render_template.action_view",
|
58
|
+
identifier: template.identifier,
|
59
|
+
layout: layout && layout.virtual_path
|
60
|
+
) do
|
58
61
|
template.render(view, locals) { |*name| view._layout_for(*name) }
|
59
62
|
end
|
60
63
|
end
|
@@ -62,15 +65,16 @@ module ActionView
|
|
62
65
|
|
63
66
|
def render_with_layout(view, template, path, locals)
|
64
67
|
layout = path && find_layout(path, locals.keys, [formats.first])
|
65
|
-
content = yield(layout)
|
66
68
|
|
67
69
|
body = if layout
|
68
|
-
|
69
|
-
|
70
|
+
ActiveSupport::Notifications.instrument("render_layout.action_view", identifier: layout.identifier) do
|
71
|
+
view.view_flow.set(:layout, yield(layout))
|
72
|
+
layout.render(view, locals) { |*name| view._layout_for(*name) }
|
73
|
+
end
|
70
74
|
else
|
71
|
-
|
75
|
+
yield
|
72
76
|
end
|
73
|
-
build_rendered_template(body, template
|
77
|
+
build_rendered_template(body, template)
|
74
78
|
end
|
75
79
|
|
76
80
|
# This is the method which actually finds the layout using details in the lookup
|
@@ -88,8 +92,7 @@ module ActionView
|
|
88
92
|
when String
|
89
93
|
begin
|
90
94
|
if layout.start_with?("/")
|
91
|
-
|
92
|
-
@lookup_context.with_fallbacks.find_template(layout, nil, false, [], details)
|
95
|
+
raise ArgumentError, "Rendering layouts from an absolute path is not supported."
|
93
96
|
else
|
94
97
|
@lookup_context.find_template(layout, nil, false, [], details)
|
95
98
|
end
|
@@ -33,7 +33,7 @@ module ActionView
|
|
33
33
|
super
|
34
34
|
end
|
35
35
|
|
36
|
-
# Overwrite process to
|
36
|
+
# Overwrite process to set up I18n proxy.
|
37
37
|
def process(*) #:nodoc:
|
38
38
|
old_config, I18n.config = I18n.config, I18nProxy.new(I18n.config, lookup_context)
|
39
39
|
super
|
@@ -144,6 +144,8 @@ module ActionView
|
|
144
144
|
else
|
145
145
|
if action.respond_to?(:permitted?) && action.permitted?
|
146
146
|
options = action
|
147
|
+
elsif action.respond_to?(:render_in)
|
148
|
+
options[:renderable] = action
|
147
149
|
else
|
148
150
|
options[:partial] = action
|
149
151
|
end
|
@@ -105,7 +105,7 @@ module ActionView
|
|
105
105
|
end
|
106
106
|
else
|
107
107
|
method = _generate_paths_by_default ? :path : :url
|
108
|
-
builder = ActionDispatch::Routing::PolymorphicRoutes::HelperMethodBuilder.
|
108
|
+
builder = ActionDispatch::Routing::PolymorphicRoutes::HelperMethodBuilder.public_send(method)
|
109
109
|
|
110
110
|
case options
|
111
111
|
when Symbol
|
@@ -13,9 +13,11 @@ module ActionView
|
|
13
13
|
|
14
14
|
# Dup properties so that we don't modify argument
|
15
15
|
properties = Hash[properties]
|
16
|
-
|
17
|
-
properties[:
|
18
|
-
properties[:
|
16
|
+
|
17
|
+
properties[:bufvar] ||= "@output_buffer"
|
18
|
+
properties[:preamble] ||= ""
|
19
|
+
properties[:postamble] ||= "#{properties[:bufvar]}.to_s"
|
20
|
+
|
19
21
|
properties[:escapefunc] = ""
|
20
22
|
|
21
23
|
super
|
@@ -37,7 +39,7 @@ module ActionView
|
|
37
39
|
if text == "\n"
|
38
40
|
@newline_pending += 1
|
39
41
|
else
|
40
|
-
src << "
|
42
|
+
src << bufvar << ".safe_append='"
|
41
43
|
src << "\n" * @newline_pending if @newline_pending > 0
|
42
44
|
src << text.gsub(/['\\]/, '\\\\\&')
|
43
45
|
src << "'.freeze;"
|
@@ -52,9 +54,9 @@ module ActionView
|
|
52
54
|
flush_newline_if_pending(src)
|
53
55
|
|
54
56
|
if (indicator == "==") || @escape
|
55
|
-
src << "
|
57
|
+
src << bufvar << ".safe_expr_append="
|
56
58
|
else
|
57
|
-
src << "
|
59
|
+
src << bufvar << ".append="
|
58
60
|
end
|
59
61
|
|
60
62
|
if BLOCK_EXPR.match?(code)
|
@@ -76,7 +78,7 @@ module ActionView
|
|
76
78
|
|
77
79
|
def flush_newline_if_pending(src)
|
78
80
|
if @newline_pending > 0
|
79
|
-
src << "
|
81
|
+
src << bufvar << ".safe_append='#{"\n" * @newline_pending}'.freeze;"
|
80
82
|
@newline_pending = 0
|
81
83
|
end
|
82
84
|
end
|