actionview 4.2.11.1 → 6.1.5

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.

Files changed (117) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +232 -186
  3. data/MIT-LICENSE +1 -2
  4. data/README.rdoc +9 -8
  5. data/lib/action_view/base.rb +115 -39
  6. data/lib/action_view/buffers.rb +18 -1
  7. data/lib/action_view/cache_expiry.rb +52 -0
  8. data/lib/action_view/context.rb +8 -12
  9. data/lib/action_view/dependency_tracker.rb +61 -21
  10. data/lib/action_view/digestor.rb +89 -85
  11. data/lib/action_view/flows.rb +11 -12
  12. data/lib/action_view/gem_version.rb +6 -4
  13. data/lib/action_view/helpers/active_model_helper.rb +16 -11
  14. data/lib/action_view/helpers/asset_tag_helper.rb +282 -83
  15. data/lib/action_view/helpers/asset_url_helper.rb +175 -69
  16. data/lib/action_view/helpers/atom_feed_helper.rb +20 -17
  17. data/lib/action_view/helpers/cache_helper.rb +107 -43
  18. data/lib/action_view/helpers/capture_helper.rb +20 -13
  19. data/lib/action_view/helpers/controller_helper.rb +15 -4
  20. data/lib/action_view/helpers/csp_helper.rb +26 -0
  21. data/lib/action_view/helpers/csrf_helper.rb +8 -6
  22. data/lib/action_view/helpers/date_helper.rb +232 -130
  23. data/lib/action_view/helpers/debug_helper.rb +7 -6
  24. data/lib/action_view/helpers/form_helper.rb +808 -146
  25. data/lib/action_view/helpers/form_options_helper.rb +124 -78
  26. data/lib/action_view/helpers/form_tag_helper.rb +120 -74
  27. data/lib/action_view/helpers/javascript_helper.rb +33 -17
  28. data/lib/action_view/helpers/number_helper.rb +87 -62
  29. data/lib/action_view/helpers/output_safety_helper.rb +36 -4
  30. data/lib/action_view/helpers/rendering_helper.rb +21 -10
  31. data/lib/action_view/helpers/sanitize_helper.rb +30 -31
  32. data/lib/action_view/helpers/tag_helper.rb +269 -68
  33. data/lib/action_view/helpers/tags/base.rb +141 -97
  34. data/lib/action_view/helpers/tags/check_box.rb +20 -19
  35. data/lib/action_view/helpers/tags/checkable.rb +4 -2
  36. data/lib/action_view/helpers/tags/collection_check_boxes.rb +12 -34
  37. data/lib/action_view/helpers/tags/collection_helpers.rb +69 -36
  38. data/lib/action_view/helpers/tags/collection_radio_buttons.rb +6 -12
  39. data/lib/action_view/helpers/tags/collection_select.rb +4 -2
  40. data/lib/action_view/helpers/tags/color_field.rb +4 -3
  41. data/lib/action_view/helpers/tags/date_field.rb +3 -2
  42. data/lib/action_view/helpers/tags/date_select.rb +38 -37
  43. data/lib/action_view/helpers/tags/datetime_field.rb +4 -3
  44. data/lib/action_view/helpers/tags/datetime_local_field.rb +3 -2
  45. data/lib/action_view/helpers/tags/datetime_select.rb +2 -0
  46. data/lib/action_view/helpers/tags/email_field.rb +2 -0
  47. data/lib/action_view/helpers/tags/file_field.rb +2 -0
  48. data/lib/action_view/helpers/tags/grouped_collection_select.rb +4 -2
  49. data/lib/action_view/helpers/tags/hidden_field.rb +6 -0
  50. data/lib/action_view/helpers/tags/label.rb +7 -2
  51. data/lib/action_view/helpers/tags/month_field.rb +3 -2
  52. data/lib/action_view/helpers/tags/number_field.rb +2 -0
  53. data/lib/action_view/helpers/tags/password_field.rb +3 -1
  54. data/lib/action_view/helpers/tags/placeholderable.rb +3 -1
  55. data/lib/action_view/helpers/tags/radio_button.rb +7 -6
  56. data/lib/action_view/helpers/tags/range_field.rb +2 -0
  57. data/lib/action_view/helpers/tags/search_field.rb +14 -9
  58. data/lib/action_view/helpers/tags/select.rb +11 -10
  59. data/lib/action_view/helpers/tags/tel_field.rb +2 -0
  60. data/lib/action_view/helpers/tags/text_area.rb +4 -2
  61. data/lib/action_view/helpers/tags/text_field.rb +8 -8
  62. data/lib/action_view/helpers/tags/time_field.rb +3 -2
  63. data/lib/action_view/helpers/tags/time_select.rb +2 -0
  64. data/lib/action_view/helpers/tags/time_zone_select.rb +3 -1
  65. data/lib/action_view/helpers/tags/translator.rb +15 -16
  66. data/lib/action_view/helpers/tags/url_field.rb +2 -0
  67. data/lib/action_view/helpers/tags/week_field.rb +3 -2
  68. data/lib/action_view/helpers/tags.rb +3 -1
  69. data/lib/action_view/helpers/text_helper.rb +56 -38
  70. data/lib/action_view/helpers/translation_helper.rb +150 -68
  71. data/lib/action_view/helpers/url_helper.rb +284 -117
  72. data/lib/action_view/helpers.rb +5 -3
  73. data/lib/action_view/layouts.rb +68 -63
  74. data/lib/action_view/log_subscriber.rb +77 -10
  75. data/lib/action_view/lookup_context.rb +134 -91
  76. data/lib/action_view/model_naming.rb +3 -1
  77. data/lib/action_view/path_set.rb +26 -24
  78. data/lib/action_view/railtie.rb +62 -13
  79. data/lib/action_view/record_identifier.rb +53 -26
  80. data/lib/action_view/renderer/abstract_renderer.rb +151 -14
  81. data/lib/action_view/renderer/collection_renderer.rb +196 -0
  82. data/lib/action_view/renderer/object_renderer.rb +34 -0
  83. data/lib/action_view/renderer/partial_renderer/collection_caching.rb +102 -0
  84. data/lib/action_view/renderer/partial_renderer.rb +55 -303
  85. data/lib/action_view/renderer/renderer.rb +66 -9
  86. data/lib/action_view/renderer/streaming_template_renderer.rb +58 -54
  87. data/lib/action_view/renderer/template_renderer.rb +82 -73
  88. data/lib/action_view/rendering.rb +71 -45
  89. data/lib/action_view/routing_url_for.rb +34 -23
  90. data/lib/action_view/tasks/cache_digests.rake +25 -0
  91. data/lib/action_view/template/error.rb +44 -29
  92. data/lib/action_view/template/handlers/builder.rb +12 -13
  93. data/lib/action_view/template/handlers/erb/erubi.rb +89 -0
  94. data/lib/action_view/template/handlers/erb.rb +23 -89
  95. data/lib/action_view/template/handlers/html.rb +11 -0
  96. data/lib/action_view/template/handlers/raw.rb +4 -4
  97. data/lib/action_view/template/handlers.rb +12 -8
  98. data/lib/action_view/template/html.rb +10 -11
  99. data/lib/action_view/template/inline.rb +22 -0
  100. data/lib/action_view/template/raw_file.rb +25 -0
  101. data/lib/action_view/template/renderable.rb +24 -0
  102. data/lib/action_view/template/resolver.rb +263 -197
  103. data/lib/action_view/template/sources/file.rb +17 -0
  104. data/lib/action_view/template/sources.rb +13 -0
  105. data/lib/action_view/template/text.rb +8 -10
  106. data/lib/action_view/template/types.rb +18 -18
  107. data/lib/action_view/template.rb +108 -92
  108. data/lib/action_view/test_case.rb +66 -53
  109. data/lib/action_view/testing/resolvers.rb +24 -33
  110. data/lib/action_view/unbound_template.rb +31 -0
  111. data/lib/action_view/version.rb +3 -1
  112. data/lib/action_view/view_paths.rb +73 -58
  113. data/lib/action_view.rb +14 -8
  114. data/lib/assets/compiled/rails-ujs.js +746 -0
  115. metadata +42 -29
  116. data/lib/action_view/helpers/record_tag_helper.rb +0 -108
  117. data/lib/action_view/tasks/dependencies.rake +0 -23
@@ -1,33 +1,8 @@
1
- require 'thread_safe'
1
+ # frozen_string_literal: true
2
2
 
3
- module ActionView
4
- class PartialIteration
5
- # The number of iterations that will be done by the partial.
6
- attr_reader :size
7
-
8
- # The current iteration of the partial.
9
- attr_reader :index
10
-
11
- def initialize(size)
12
- @size = size
13
- @index = 0
14
- end
15
-
16
- # Check if this is the first iteration of the partial.
17
- def first?
18
- index == 0
19
- end
20
-
21
- # Check if this is the last iteration of the partial.
22
- def last?
23
- index == size - 1
24
- end
25
-
26
- def iterate! # :nodoc:
27
- @index += 1
28
- end
29
- end
3
+ require "action_view/renderer/partial_renderer/collection_caching"
30
4
 
5
+ module ActionView
31
6
  # = Action View Partials
32
7
  #
33
8
  # There's also a convenience method for rendering sub templates within the current controller that depends on a
@@ -49,12 +24,12 @@ module ActionView
49
24
  # <%= render partial: "ad", locals: { ad: ad } %>
50
25
  # <% end %>
51
26
  #
52
- # This would first render "advertiser/_account.html.erb" with @buyer passed in as the local variable +account+, then
53
- # render "advertiser/_ad.html.erb" and pass the local variable +ad+ to the template for display.
27
+ # This would first render <tt>advertiser/_account.html.erb</tt> with <tt>@buyer</tt> passed in as the local variable +account+, then
28
+ # render <tt>advertiser/_ad.html.erb</tt> and pass the local variable +ad+ to the template for display.
54
29
  #
55
30
  # == The :as and :object options
56
31
  #
57
- # By default <tt>ActionView::PartialRenderer</tt> doesn't have any local variables.
32
+ # By default ActionView::PartialRenderer doesn't have any local variables.
58
33
  # The <tt>:object</tt> option can be used to pass an object to the partial. For instance:
59
34
  #
60
35
  # <%= render partial: "account", object: @buyer %>
@@ -73,7 +48,7 @@ module ActionView
73
48
  #
74
49
  # <%= render partial: "account", locals: { user: @buyer } %>
75
50
  #
76
- # == Rendering a collection of partials
51
+ # == \Rendering a collection of partials
77
52
  #
78
53
  # The example of partial use describes a familiar pattern where a template needs to iterate over an array and
79
54
  # render a sub template for each of the elements. This pattern has been implemented as a single method that
@@ -82,7 +57,7 @@ module ActionView
82
57
  #
83
58
  # <%= render partial: "ad", collection: @advertisements %>
84
59
  #
85
- # This will render "advertiser/_ad.html.erb" and pass the local variable +ad+ to the template for display. An
60
+ # This will render <tt>advertiser/_ad.html.erb</tt> and pass the local variable +ad+ to the template for display. An
86
61
  # iteration object will automatically be made available to the template with a name of the form
87
62
  # +partial_name_iteration+. The iteration object has knowledge about which index the current object has in
88
63
  # the collection and the total size of the collection. The iteration object also has two convenience methods,
@@ -97,37 +72,34 @@ module ActionView
97
72
  #
98
73
  # <%= render partial: "ad", collection: @advertisements, spacer_template: "ad_divider" %>
99
74
  #
100
- # If the given <tt>:collection</tt> is nil or empty, <tt>render</tt> will return nil. This will allow you
101
- # to specify a text which will displayed instead by using this form:
75
+ # If the given <tt>:collection</tt> is +nil+ or empty, <tt>render</tt> will return +nil+. This will allow you
76
+ # to specify a text which will be displayed instead by using this form:
102
77
  #
103
78
  # <%= render(partial: "ad", collection: @advertisements) || "There's no ad to be displayed" %>
104
79
  #
105
- # NOTE: Due to backwards compatibility concerns, the collection can't be one of hashes. Normally you'd also
106
- # just keep domain objects, like Active Records, in there.
107
- #
108
- # == Rendering shared partials
80
+ # == \Rendering shared partials
109
81
  #
110
82
  # Two controllers can share a set of partials and render them like this:
111
83
  #
112
84
  # <%= render partial: "advertisement/ad", locals: { ad: @advertisement } %>
113
85
  #
114
- # This will render the partial "advertisement/_ad.html.erb" regardless of which controller this is being called from.
86
+ # This will render the partial <tt>advertisement/_ad.html.erb</tt> regardless of which controller this is being called from.
115
87
  #
116
- # == Rendering objects that respond to `to_partial_path`
88
+ # == \Rendering objects that respond to +to_partial_path+
117
89
  #
118
90
  # Instead of explicitly naming the location of a partial, you can also let PartialRenderer do the work
119
- # and pick the proper path by checking `to_partial_path` method.
91
+ # and pick the proper path by checking +to_partial_path+ method.
120
92
  #
121
93
  # # @account.to_partial_path returns 'accounts/account', so it can be used to replace:
122
94
  # # <%= render partial: "accounts/account", locals: { account: @account} %>
123
95
  # <%= render partial: @account %>
124
96
  #
125
- # # @posts is an array of Post instances, so every post record returns 'posts/post' on `to_partial_path`,
97
+ # # @posts is an array of Post instances, so every post record returns 'posts/post' on +to_partial_path+,
126
98
  # # that's why we can replace:
127
99
  # # <%= render partial: "posts/post", collection: @posts %>
128
100
  # <%= render partial: @posts %>
129
101
  #
130
- # == Rendering the default case
102
+ # == \Rendering the default case
131
103
  #
132
104
  # If you're not going to be using any of the options like collections or layouts, you can also use the short-hand
133
105
  # defaults of render to render partials. Examples:
@@ -142,34 +114,34 @@ module ActionView
142
114
  # # <%= render partial: "accounts/account", locals: { account: @account} %>
143
115
  # <%= render @account %>
144
116
  #
145
- # # @posts is an array of Post instances, so every post record returns 'posts/post' on `to_partial_path`,
117
+ # # @posts is an array of Post instances, so every post record returns 'posts/post' on +to_partial_path+,
146
118
  # # that's why we can replace:
147
119
  # # <%= render partial: "posts/post", collection: @posts %>
148
120
  # <%= render @posts %>
149
121
  #
150
- # == Rendering partials with layouts
122
+ # == \Rendering partials with layouts
151
123
  #
152
124
  # Partials can have their own layouts applied to them. These layouts are different than the ones that are
153
125
  # specified globally for the entire action, but they work in a similar fashion. Imagine a list with two types
154
126
  # of users:
155
127
  #
156
- # <%# app/views/users/index.html.erb &>
128
+ # <%# app/views/users/index.html.erb %>
157
129
  # Here's the administrator:
158
130
  # <%= render partial: "user", layout: "administrator", locals: { user: administrator } %>
159
131
  #
160
132
  # Here's the editor:
161
133
  # <%= render partial: "user", layout: "editor", locals: { user: editor } %>
162
134
  #
163
- # <%# app/views/users/_user.html.erb &>
135
+ # <%# app/views/users/_user.html.erb %>
164
136
  # Name: <%= user.name %>
165
137
  #
166
- # <%# app/views/users/_administrator.html.erb &>
138
+ # <%# app/views/users/_administrator.html.erb %>
167
139
  # <div id="administrator">
168
140
  # Budget: $<%= user.budget %>
169
141
  # <%= yield %>
170
142
  # </div>
171
143
  #
172
- # <%# app/views/users/_editor.html.erb &>
144
+ # <%# app/views/users/_editor.html.erb %>
173
145
  # <div id="editor">
174
146
  # Deadline: <%= user.deadline %>
175
147
  # <%= yield %>
@@ -232,7 +204,7 @@ module ActionView
232
204
  #
233
205
  # You can also apply a layout to a block within any template:
234
206
  #
235
- # <%# app/views/users/_chief.html.erb &>
207
+ # <%# app/views/users/_chief.html.erb %>
236
208
  # <%= render(layout: "administrator", locals: { user: chief }) do %>
237
209
  # Title: <%= chief.title %>
238
210
  # <% end %>
@@ -249,13 +221,13 @@ module ActionView
249
221
  # If you pass arguments to "yield" then this will be passed to the block. One way to use this is to pass
250
222
  # an array to layout and treat it as an enumerable.
251
223
  #
252
- # <%# app/views/users/_user.html.erb &>
224
+ # <%# app/views/users/_user.html.erb %>
253
225
  # <div class="user">
254
226
  # Budget: $<%= user.budget %>
255
227
  # <%= yield user %>
256
228
  # </div>
257
229
  #
258
- # <%# app/views/users/index.html.erb &>
230
+ # <%# app/views/users/index.html.erb %>
259
231
  # <%= render layout: @users do |user| %>
260
232
  # Title: <%= user.title %>
261
233
  # <% end %>
@@ -264,14 +236,14 @@ module ActionView
264
236
  #
265
237
  # You can also yield multiple times in one layout and use block arguments to differentiate the sections.
266
238
  #
267
- # <%# app/views/users/_user.html.erb &>
239
+ # <%# app/views/users/_user.html.erb %>
268
240
  # <div class="user">
269
241
  # <%= yield user, :header %>
270
242
  # Budget: $<%= user.budget %>
271
243
  # <%= yield user, :footer %>
272
244
  # </div>
273
245
  #
274
- # <%# app/views/users/index.html.erb &>
246
+ # <%# app/views/users/index.html.erb %>
275
247
  # <%= render layout: @users do |user, section| %>
276
248
  # <%- case section when :header -%>
277
249
  # Title: <%= user.title %>
@@ -280,269 +252,49 @@ module ActionView
280
252
  # <%- end -%>
281
253
  # <% end %>
282
254
  class PartialRenderer < AbstractRenderer
283
- PREFIXED_PARTIAL_NAMES = ThreadSafe::Cache.new do |h, k|
284
- h[k] = ThreadSafe::Cache.new
285
- end
286
-
287
- def initialize(*)
288
- super
289
- @context_prefix = @lookup_context.prefixes.first
290
- end
291
-
292
- def render(context, options, block)
293
- setup(context, options, block)
294
- identifier = (@template = find_partial) ? @template.identifier : @path
295
-
296
- @lookup_context.rendered_format ||= begin
297
- if @template && @template.formats.present?
298
- @template.formats.first
299
- else
300
- formats.first
301
- end
302
- end
255
+ include CollectionCaching
303
256
 
304
- if @collection
305
- instrument(:collection, :identifier => identifier || "collection", :count => @collection.size) do
306
- render_collection
307
- end
308
- else
309
- instrument(:partial, :identifier => identifier) do
310
- render_partial
311
- end
312
- end
313
- end
314
-
315
- private
316
-
317
- def render_collection
318
- return nil if @collection.blank?
319
-
320
- if @options.key?(:spacer_template)
321
- spacer = find_template(@options[:spacer_template], @locals.keys).render(@view, @locals)
322
- end
323
-
324
- result = @template ? collection_with_template : collection_without_template
325
- result.join(spacer).html_safe
257
+ def initialize(lookup_context, options)
258
+ super(lookup_context)
259
+ @options = options
260
+ @locals = @options[:locals] || {}
261
+ @details = extract_details(@options)
326
262
  end
327
263
 
328
- def render_partial
329
- view, locals, block = @view, @locals, @block
330
- object, as = @object, @variable
264
+ def render(partial, context, block)
265
+ template = find_template(partial, template_keys(partial))
331
266
 
332
267
  if !block && (layout = @options[:layout])
333
- layout = find_template(layout.to_s, @template_keys)
268
+ layout = find_template(layout.to_s, template_keys(partial))
334
269
  end
335
270
 
336
- object = locals[as] if object.nil? # Respect object when object is false
337
- locals[as] = object
338
-
339
- content = @template.render(view, locals) do |*name|
340
- view._layout_for(*name, &block)
341
- end
342
-
343
- content = layout.render(view, locals){ content } if layout
344
- content
271
+ render_partial_template(context, @locals, template, layout, block)
345
272
  end
346
273
 
347
274
  private
348
-
349
- # Sets up instance variables needed for rendering a partial. This method
350
- # finds the options and details and extracts them. The method also contains
351
- # logic that handles the type of object passed in as the partial.
352
- #
353
- # If +options[:partial]+ is a string, then the +@path+ instance variable is
354
- # set to that string. Otherwise, the +options[:partial]+ object must
355
- # respond to +to_partial_path+ in order to setup the path.
356
- def setup(context, options, block)
357
- @view = context
358
- @options = options
359
- @block = block
360
-
361
- @locals = options[:locals] || {}
362
- @details = extract_details(options)
363
-
364
- prepend_formats(options[:formats])
365
-
366
- partial = options[:partial]
367
-
368
- if String === partial
369
- @has_object = options.key?(:object)
370
- @object = options[:object]
371
- @collection = collection_from_options
372
- @path = partial
373
- else
374
- @has_object = true
375
- @object = partial
376
- @collection = collection_from_object || collection_from_options
377
-
378
- if @collection
379
- paths = @collection_data = @collection.map { |o| partial_path(o) }
380
- @path = paths.uniq.one? ? paths.first : nil
381
- else
382
- @path = partial_path
383
- end
384
- end
385
-
386
- if as = options[:as]
387
- raise_invalid_option_as(as) unless as.to_s =~ /\A[a-z_]\w*\z/
388
- as = as.to_sym
275
+ def template_keys(_)
276
+ @locals.keys
389
277
  end
390
278
 
391
- if @path
392
- @variable, @variable_counter, @variable_iteration = retrieve_variable(@path, as)
393
- @template_keys = retrieve_template_keys
394
- else
395
- paths.map! { |path| retrieve_variable(path, as).unshift(path) }
396
- end
397
-
398
- self
399
- end
400
-
401
- def collection_from_options
402
- if @options.key?(:collection)
403
- collection = @options[:collection]
404
- collection.respond_to?(:to_ary) ? collection.to_ary : []
405
- end
406
- end
407
-
408
- def collection_from_object
409
- @object.to_ary if @object.respond_to?(:to_ary)
410
- end
411
-
412
- def find_partial
413
- find_template(@path, @template_keys) if @path
414
- end
415
-
416
- def find_template(path, locals)
417
- prefixes = path.include?(?/) ? [] : @lookup_context.prefixes
418
- @lookup_context.find_template(path, prefixes, true, locals, @details)
419
- end
420
-
421
- def collection_with_template
422
- view, locals, template = @view, @locals, @template
423
- as, counter, iteration = @variable, @variable_counter, @variable_iteration
424
-
425
- if layout = @options[:layout]
426
- layout = find_template(layout, @template_keys)
427
- end
428
-
429
- partial_iteration = PartialIteration.new(@collection.size)
430
- locals[iteration] = partial_iteration
431
-
432
- @collection.map do |object|
433
- locals[as] = object
434
- locals[counter] = partial_iteration.index
435
-
436
- content = template.render(view, locals)
437
- content = layout.render(view, locals) { content } if layout
438
- partial_iteration.iterate!
439
- content
440
- end
441
- end
442
-
443
- def collection_without_template
444
- view, locals, collection_data = @view, @locals, @collection_data
445
- cache = {}
446
- keys = @locals.keys
447
-
448
- partial_iteration = PartialIteration.new(@collection.size)
449
-
450
- @collection.map do |object|
451
- index = partial_iteration.index
452
- path, as, counter, iteration = collection_data[index]
453
-
454
- locals[as] = object
455
- locals[counter] = index
456
- locals[iteration] = partial_iteration
457
-
458
- template = (cache[path] ||= find_template(path, keys + [as, counter]))
459
- content = template.render(view, locals)
460
- partial_iteration.iterate!
461
- content
462
- end
463
- end
464
-
465
- # Obtains the path to where the object's partial is located. If the object
466
- # responds to +to_partial_path+, then +to_partial_path+ will be called and
467
- # will provide the path. If the object does not respond to +to_partial_path+,
468
- # then an +ArgumentError+ is raised.
469
- #
470
- # If +prefix_partial_path_with_controller_namespace+ is true, then this
471
- # method will prefix the partial paths with a namespace.
472
- def partial_path(object = @object)
473
- object = object.to_model if object.respond_to?(:to_model)
474
-
475
- path = if object.respond_to?(:to_partial_path)
476
- object.to_partial_path
477
- else
478
- raise ArgumentError.new("'#{object.inspect}' is not an ActiveModel-compatible object. It must implement :to_partial_path.")
479
- end
480
-
481
- if @view.prefix_partial_path_with_controller_namespace
482
- prefixed_partial_names[path] ||= merge_prefix_into_object_path(@context_prefix, path.dup)
483
- else
484
- path
485
- end
486
- end
487
-
488
- def prefixed_partial_names
489
- @prefixed_partial_names ||= PREFIXED_PARTIAL_NAMES[@context_prefix]
490
- end
491
-
492
- def merge_prefix_into_object_path(prefix, object_path)
493
- if prefix.include?(?/) && object_path.include?(?/)
494
- prefixes = []
495
- prefix_array = File.dirname(prefix).split('/')
496
- object_path_array = object_path.split('/')[0..-3] # skip model dir & partial
497
-
498
- prefix_array.each_with_index do |dir, index|
499
- break if dir == object_path_array[index]
500
- prefixes << dir
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|
286
+ view._layout_for(*name, &block)
287
+ end
288
+
289
+ content = layout.render(view, locals) { content } if layout
290
+ payload[:cache_hit] = view.view_renderer.cache_hits[template.virtual_path]
291
+ build_rendered_template(content, template)
501
292
  end
502
-
503
- (prefixes << object_path).join("/")
504
- else
505
- object_path
506
293
  end
507
- end
508
-
509
- def retrieve_template_keys
510
- keys = @locals.keys
511
- keys << @variable if @has_object || @collection
512
- if @collection
513
- keys << @variable_counter
514
- keys << @variable_iteration
515
- end
516
- keys
517
- end
518
294
 
519
- def retrieve_variable(path, as)
520
- variable = as || begin
521
- base = path[-1] == "/" ? "" : File.basename(path)
522
- raise_invalid_identifier(path) unless base =~ /\A_?([a-z]\w*)(\.\w+)*\z/
523
- $1.to_sym
524
- end
525
- if @collection
526
- variable_counter = :"#{variable}_counter"
527
- variable_iteration = :"#{variable}_iteration"
295
+ def find_template(path, locals)
296
+ prefixes = path.include?(?/) ? [] : @lookup_context.prefixes
297
+ @lookup_context.find_template(path, prefixes, true, locals, @details)
528
298
  end
529
- [variable, variable_counter, variable_iteration]
530
- end
531
-
532
- IDENTIFIER_ERROR_MESSAGE = "The partial name (%s) is not a valid Ruby identifier; " +
533
- "make sure your partial name starts with underscore, " +
534
- "and is followed by any combination of letters, numbers and underscores."
535
-
536
- OPTION_AS_ERROR_MESSAGE = "The value (%s) of the option `as` is not a valid Ruby identifier; " +
537
- "make sure it starts with lowercase letter, " +
538
- "and is followed by any combination of letters, numbers and underscores."
539
-
540
- def raise_invalid_identifier(path)
541
- raise ArgumentError.new(IDENTIFIER_ERROR_MESSAGE % (path))
542
- end
543
-
544
- def raise_invalid_option_as(as)
545
- raise ArgumentError.new(OPTION_AS_ERROR_MESSAGE % (as))
546
- end
547
299
  end
548
300
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActionView
2
4
  # This is the main entry point for rendering. It basically delegates
3
5
  # to other objects like TemplateRenderer and PartialRenderer which
@@ -15,16 +17,16 @@ module ActionView
15
17
  @lookup_context = lookup_context
16
18
  end
17
19
 
18
- # Main render entry point shared by AV and AC.
20
+ # Main render entry point shared by Action View and Action Controller.
19
21
  def render(context, options)
20
- if options.respond_to?(:permitted?) && !options.permitted?
21
- raise ArgumentError, "render parameters are not permitted"
22
- end
22
+ render_to_object(context, options).body
23
+ end
23
24
 
25
+ def render_to_object(context, options) # :nodoc:
24
26
  if options.key?(:partial)
25
- render_partial(context, options)
27
+ render_partial_to_object(context, options)
26
28
  else
27
- render_template(context, options)
29
+ render_template_to_object(context, options)
28
30
  end
29
31
  end
30
32
 
@@ -41,14 +43,69 @@ module ActionView
41
43
  end
42
44
  end
43
45
 
44
- # Direct accessor to template rendering.
46
+ # Direct access to template rendering.
45
47
  def render_template(context, options) #:nodoc:
46
- TemplateRenderer.new(@lookup_context).render(context, options)
48
+ render_template_to_object(context, options).body
47
49
  end
48
50
 
49
51
  # Direct access to partial rendering.
50
52
  def render_partial(context, options, &block) #:nodoc:
51
- PartialRenderer.new(@lookup_context).render(context, options, block)
53
+ render_partial_to_object(context, options, &block).body
54
+ end
55
+
56
+ def cache_hits # :nodoc:
57
+ @cache_hits ||= {}
52
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
+ 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
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
53
110
  end
54
111
  end