actionview 4.2.11.1 → 5.2.7
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 +108 -240
- data/MIT-LICENSE +1 -1
- data/README.rdoc +5 -6
- data/lib/action_view/base.rb +38 -28
- data/lib/action_view/buffers.rb +3 -1
- data/lib/action_view/context.rb +3 -3
- data/lib/action_view/dependency_tracker.rb +54 -20
- data/lib/action_view/digestor.rb +94 -83
- data/lib/action_view/flows.rb +11 -11
- data/lib/action_view/gem_version.rb +5 -3
- data/lib/action_view/helpers/active_model_helper.rb +17 -11
- data/lib/action_view/helpers/asset_tag_helper.rb +244 -62
- data/lib/action_view/helpers/asset_url_helper.rb +170 -67
- data/lib/action_view/helpers/atom_feed_helper.rb +19 -17
- data/lib/action_view/helpers/cache_helper.rb +105 -42
- data/lib/action_view/helpers/capture_helper.rb +16 -13
- data/lib/action_view/helpers/controller_helper.rb +15 -4
- data/lib/action_view/helpers/csp_helper.rb +24 -0
- data/lib/action_view/helpers/csrf_helper.rb +7 -5
- data/lib/action_view/helpers/date_helper.rb +170 -112
- data/lib/action_view/helpers/debug_helper.rb +7 -6
- data/lib/action_view/helpers/form_helper.rb +521 -127
- data/lib/action_view/helpers/form_options_helper.rb +109 -63
- data/lib/action_view/helpers/form_tag_helper.rb +110 -67
- data/lib/action_view/helpers/javascript_helper.rb +27 -12
- data/lib/action_view/helpers/number_helper.rb +77 -58
- data/lib/action_view/helpers/output_safety_helper.rb +36 -4
- data/lib/action_view/helpers/record_tag_helper.rb +14 -99
- data/lib/action_view/helpers/rendering_helper.rb +6 -5
- data/lib/action_view/helpers/sanitize_helper.rb +20 -15
- data/lib/action_view/helpers/tag_helper.rb +198 -73
- data/lib/action_view/helpers/tags/base.rb +134 -97
- data/lib/action_view/helpers/tags/check_box.rb +20 -18
- data/lib/action_view/helpers/tags/checkable.rb +4 -2
- data/lib/action_view/helpers/tags/collection_check_boxes.rb +12 -33
- data/lib/action_view/helpers/tags/collection_helpers.rb +70 -36
- data/lib/action_view/helpers/tags/collection_radio_buttons.rb +6 -11
- data/lib/action_view/helpers/tags/collection_select.rb +4 -2
- data/lib/action_view/helpers/tags/color_field.rb +3 -1
- data/lib/action_view/helpers/tags/date_field.rb +2 -0
- data/lib/action_view/helpers/tags/date_select.rb +38 -36
- data/lib/action_view/helpers/tags/datetime_field.rb +4 -2
- data/lib/action_view/helpers/tags/datetime_local_field.rb +2 -0
- data/lib/action_view/helpers/tags/datetime_select.rb +2 -0
- data/lib/action_view/helpers/tags/email_field.rb +2 -0
- data/lib/action_view/helpers/tags/file_field.rb +2 -0
- data/lib/action_view/helpers/tags/grouped_collection_select.rb +4 -2
- data/lib/action_view/helpers/tags/hidden_field.rb +2 -0
- data/lib/action_view/helpers/tags/label.rb +3 -1
- data/lib/action_view/helpers/tags/month_field.rb +2 -0
- data/lib/action_view/helpers/tags/number_field.rb +2 -0
- data/lib/action_view/helpers/tags/password_field.rb +3 -1
- data/lib/action_view/helpers/tags/placeholderable.rb +3 -1
- data/lib/action_view/helpers/tags/radio_button.rb +7 -5
- data/lib/action_view/helpers/tags/range_field.rb +2 -0
- data/lib/action_view/helpers/tags/search_field.rb +14 -9
- data/lib/action_view/helpers/tags/select.rb +11 -9
- data/lib/action_view/helpers/tags/tel_field.rb +2 -0
- data/lib/action_view/helpers/tags/text_area.rb +4 -2
- data/lib/action_view/helpers/tags/text_field.rb +8 -7
- data/lib/action_view/helpers/tags/time_field.rb +2 -0
- data/lib/action_view/helpers/tags/time_select.rb +2 -0
- data/lib/action_view/helpers/tags/time_zone_select.rb +3 -1
- data/lib/action_view/helpers/tags/translator.rb +17 -13
- data/lib/action_view/helpers/tags/url_field.rb +2 -0
- data/lib/action_view/helpers/tags/week_field.rb +2 -0
- data/lib/action_view/helpers/tags.rb +3 -1
- data/lib/action_view/helpers/text_helper.rb +55 -36
- data/lib/action_view/helpers/translation_helper.rb +74 -32
- data/lib/action_view/helpers/url_helper.rb +159 -104
- data/lib/action_view/helpers.rb +5 -1
- data/lib/action_view/layouts.rb +65 -58
- data/lib/action_view/log_subscriber.rb +60 -8
- data/lib/action_view/lookup_context.rb +80 -65
- data/lib/action_view/model_naming.rb +3 -1
- data/lib/action_view/path_set.rb +30 -19
- data/lib/action_view/railtie.rb +39 -6
- data/lib/action_view/record_identifier.rb +53 -25
- data/lib/action_view/renderer/abstract_renderer.rb +21 -15
- data/lib/action_view/renderer/partial_renderer/collection_caching.rb +57 -0
- data/lib/action_view/renderer/partial_renderer.rb +218 -214
- data/lib/action_view/renderer/renderer.rb +8 -6
- data/lib/action_view/renderer/streaming_template_renderer.rb +50 -48
- data/lib/action_view/renderer/template_renderer.rb +67 -66
- data/lib/action_view/rendering.rb +19 -14
- data/lib/action_view/routing_url_for.rb +27 -17
- data/lib/action_view/tasks/cache_digests.rake +25 -0
- data/lib/action_view/template/error.rb +16 -16
- data/lib/action_view/template/handlers/builder.rb +10 -11
- data/lib/action_view/template/handlers/erb/erubi.rb +83 -0
- data/lib/action_view/template/handlers/erb.rb +9 -80
- data/lib/action_view/template/handlers/html.rb +11 -0
- data/lib/action_view/template/handlers/raw.rb +3 -3
- data/lib/action_view/template/handlers.rb +11 -7
- data/lib/action_view/template/html.rb +5 -5
- data/lib/action_view/template/resolver.rb +140 -115
- data/lib/action_view/template/text.rb +8 -9
- data/lib/action_view/template/types.rb +18 -18
- data/lib/action_view/template.rb +56 -31
- data/lib/action_view/test_case.rb +50 -29
- data/lib/action_view/testing/resolvers.rb +31 -31
- data/lib/action_view/version.rb +3 -1
- data/lib/action_view/view_paths.rb +28 -34
- data/lib/action_view.rb +8 -7
- data/lib/assets/compiled/rails-ujs.js +720 -0
- metadata +25 -24
- data/lib/action_view/tasks/dependencies.rake +0 -23
@@ -1,4 +1,7 @@
|
|
1
|
-
|
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
|
53
|
-
# render
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
-
|
284
|
-
|
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
|
-
|
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
|
-
|
306
|
-
render_collection
|
307
|
-
end
|
310
|
+
render_collection
|
308
311
|
else
|
309
|
-
|
310
|
-
render_partial
|
311
|
-
end
|
312
|
+
render_partial
|
312
313
|
end
|
313
314
|
end
|
314
315
|
|
315
316
|
private
|
316
317
|
|
317
|
-
|
318
|
-
|
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
|
-
|
321
|
-
|
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
|
-
|
325
|
-
|
326
|
-
|
332
|
+
def render_partial
|
333
|
+
instrument(:partial) do |payload|
|
334
|
+
view, locals, block = @view, @locals, @block
|
335
|
+
object, as = @object, @variable
|
327
336
|
|
328
|
-
|
329
|
-
|
330
|
-
|
337
|
+
if !block && (layout = @options[:layout])
|
338
|
+
layout = find_template(layout.to_s, @template_keys)
|
339
|
+
end
|
331
340
|
|
332
|
-
|
333
|
-
|
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
|
-
|
337
|
-
|
344
|
+
content = @template.render(view, locals) do |*name|
|
345
|
+
view._layout_for(*name, &block)
|
346
|
+
end
|
338
347
|
|
339
|
-
|
340
|
-
|
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
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
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
|
-
|
350
|
-
|
351
|
-
|
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 @
|
379
|
-
|
380
|
-
@
|
396
|
+
if @path
|
397
|
+
@variable, @variable_counter, @variable_iteration = retrieve_variable(@path, as)
|
398
|
+
@template_keys = retrieve_template_keys
|
381
399
|
else
|
382
|
-
|
400
|
+
paths.map! { |path| retrieve_variable(path, as).unshift(path) }
|
383
401
|
end
|
384
|
-
end
|
385
402
|
|
386
|
-
|
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
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
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
|
-
|
399
|
-
|
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
|
-
|
413
|
-
|
414
|
-
|
417
|
+
def find_partial
|
418
|
+
find_template(@path, @template_keys) if @path
|
419
|
+
end
|
415
420
|
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
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
|
-
|
422
|
-
|
423
|
-
|
426
|
+
def collection_with_template
|
427
|
+
view, locals, template = @view, @locals, @template
|
428
|
+
as, counter, iteration = @variable, @variable_counter, @variable_iteration
|
424
429
|
|
425
|
-
|
426
|
-
|
427
|
-
|
430
|
+
if layout = @options[:layout]
|
431
|
+
layout = find_template(layout, @template_keys)
|
432
|
+
end
|
428
433
|
|
429
|
-
|
430
|
-
|
434
|
+
partial_iteration = PartialIteration.new(@collection.size)
|
435
|
+
locals[iteration] = partial_iteration
|
431
436
|
|
432
|
-
|
433
|
-
|
434
|
-
|
437
|
+
@collection.map do |object|
|
438
|
+
locals[as] = object
|
439
|
+
locals[counter] = partial_iteration.index
|
435
440
|
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
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
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
448
|
+
def collection_without_template
|
449
|
+
view, locals, collection_data = @view, @locals, @collection_data
|
450
|
+
cache = {}
|
451
|
+
keys = @locals.keys
|
447
452
|
|
448
|
-
|
453
|
+
partial_iteration = PartialIteration.new(@collection.size)
|
449
454
|
|
450
|
-
|
451
|
-
|
452
|
-
|
455
|
+
@collection.map do |object|
|
456
|
+
index = partial_iteration.index
|
457
|
+
path, as, counter, iteration = collection_data[index]
|
453
458
|
|
454
|
-
|
455
|
-
|
456
|
-
|
459
|
+
locals[as] = object
|
460
|
+
locals[counter] = index
|
461
|
+
locals[iteration] = partial_iteration
|
457
462
|
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
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
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
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
|
-
|
482
|
-
prefixed_partial_names
|
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
|
-
|
489
|
-
|
490
|
-
|
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
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
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
|
-
|
499
|
-
|
500
|
-
|
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
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
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
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
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
|
-
|
533
|
-
|
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
|
-
|
537
|
-
|
538
|
-
|
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
|
-
|
541
|
-
|
542
|
-
|
544
|
+
def raise_invalid_identifier(path)
|
545
|
+
raise ArgumentError.new(IDENTIFIER_ERROR_MESSAGE % (path))
|
546
|
+
end
|
543
547
|
|
544
|
-
|
545
|
-
|
546
|
-
|
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
|
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
|
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
|