actionview 4.2.11.1 → 5.2.7.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.

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