actionview 7.1.5.2 → 8.1.2.1
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +96 -436
- data/README.rdoc +1 -1
- data/lib/action_view/base.rb +29 -11
- data/lib/action_view/buffers.rb +1 -1
- data/lib/action_view/cache_expiry.rb +9 -3
- data/lib/action_view/dependency_tracker/erb_tracker.rb +37 -28
- data/lib/action_view/dependency_tracker/ruby_tracker.rb +43 -0
- data/lib/action_view/dependency_tracker/wildcard_resolver.rb +32 -0
- data/lib/action_view/dependency_tracker.rb +8 -2
- data/lib/action_view/digestor.rb +6 -2
- data/lib/action_view/gem_version.rb +3 -3
- data/lib/action_view/helpers/asset_tag_helper.rb +39 -8
- data/lib/action_view/helpers/atom_feed_helper.rb +1 -3
- data/lib/action_view/helpers/cache_helper.rb +10 -2
- data/lib/action_view/helpers/capture_helper.rb +2 -2
- data/lib/action_view/helpers/controller_helper.rb +6 -2
- data/lib/action_view/helpers/csrf_helper.rb +1 -1
- data/lib/action_view/helpers/date_helper.rb +28 -4
- data/lib/action_view/helpers/form_helper.rb +277 -272
- data/lib/action_view/helpers/form_options_helper.rb +39 -35
- data/lib/action_view/helpers/form_tag_helper.rb +115 -72
- data/lib/action_view/helpers/javascript_helper.rb +5 -1
- data/lib/action_view/helpers/number_helper.rb +14 -0
- data/lib/action_view/helpers/output_safety_helper.rb +5 -6
- data/lib/action_view/helpers/rendering_helper.rb +160 -50
- data/lib/action_view/helpers/sanitize_helper.rb +6 -0
- data/lib/action_view/helpers/tag_helper.rb +225 -48
- data/lib/action_view/helpers/tags/base.rb +11 -9
- data/lib/action_view/helpers/tags/check_box.rb +9 -3
- data/lib/action_view/helpers/tags/collection_check_boxes.rb +4 -3
- data/lib/action_view/helpers/tags/collection_helpers.rb +2 -1
- data/lib/action_view/helpers/tags/datetime_field.rb +1 -1
- data/lib/action_view/helpers/tags/file_field.rb +7 -2
- data/lib/action_view/helpers/tags/hidden_field.rb +1 -1
- data/lib/action_view/helpers/tags/label.rb +3 -10
- data/lib/action_view/helpers/tags/radio_button.rb +1 -1
- data/lib/action_view/helpers/tags/select.rb +6 -1
- data/lib/action_view/helpers/tags/select_renderer.rb +6 -4
- data/lib/action_view/helpers/tags/text_area.rb +1 -1
- data/lib/action_view/helpers/tags/text_field.rb +1 -1
- data/lib/action_view/helpers/text_helper.rb +11 -4
- data/lib/action_view/helpers/translation_helper.rb +6 -1
- data/lib/action_view/helpers/url_helper.rb +42 -90
- data/lib/action_view/layouts.rb +9 -11
- data/lib/action_view/locale/en.yml +3 -0
- data/lib/action_view/log_subscriber.rb +9 -8
- data/lib/action_view/railtie.rb +12 -2
- data/lib/action_view/record_identifier.rb +22 -1
- data/lib/action_view/render_parser/prism_render_parser.rb +139 -0
- data/lib/action_view/{ripper_ast_parser.rb → render_parser/ripper_render_parser.rb} +162 -10
- data/lib/action_view/render_parser.rb +21 -169
- data/lib/action_view/renderer/abstract_renderer.rb +1 -1
- data/lib/action_view/renderer/partial_renderer.rb +18 -2
- data/lib/action_view/renderer/renderer.rb +32 -38
- data/lib/action_view/renderer/streaming_template_renderer.rb +8 -2
- data/lib/action_view/renderer/template_renderer.rb +3 -3
- data/lib/action_view/rendering.rb +6 -7
- data/lib/action_view/structured_event_subscriber.rb +97 -0
- data/lib/action_view/template/error.rb +18 -3
- data/lib/action_view/template/handlers/erb/erubi.rb +1 -1
- data/lib/action_view/template/handlers/erb.rb +77 -44
- data/lib/action_view/template/raw_file.rb +4 -0
- data/lib/action_view/template/renderable.rb +7 -1
- data/lib/action_view/template/resolver.rb +0 -3
- data/lib/action_view/template.rb +39 -12
- data/lib/action_view/test_case.rb +57 -62
- data/lib/action_view.rb +4 -0
- metadata +17 -14
- data/lib/action_view/dependency_tracker/ripper_tracker.rb +0 -59
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "ripper"
|
|
4
|
-
|
|
5
3
|
module ActionView
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
module RenderParser
|
|
5
|
+
class RipperRenderParser < Base # :nodoc:
|
|
8
6
|
class Node < ::Array # :nodoc:
|
|
9
7
|
attr_reader :type
|
|
10
8
|
|
|
@@ -68,7 +66,16 @@ module ActionView
|
|
|
68
66
|
|
|
69
67
|
def to_string
|
|
70
68
|
raise unless string?
|
|
71
|
-
|
|
69
|
+
|
|
70
|
+
# s(:string_literal, s(:string_content, map))
|
|
71
|
+
self[0].map do |node|
|
|
72
|
+
case node.type
|
|
73
|
+
when :@tstring_content
|
|
74
|
+
node[0]
|
|
75
|
+
when :string_embexpr
|
|
76
|
+
"*"
|
|
77
|
+
end
|
|
78
|
+
end.join("")
|
|
72
79
|
end
|
|
73
80
|
|
|
74
81
|
def hash?
|
|
@@ -183,16 +190,161 @@ module ActionView
|
|
|
183
190
|
end
|
|
184
191
|
end
|
|
185
192
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
def parse_render_nodes(code)
|
|
189
|
-
parser = RenderCallExtractor.new(code)
|
|
193
|
+
def render_calls
|
|
194
|
+
parser = RenderCallExtractor.new(@code)
|
|
190
195
|
parser.parse
|
|
191
196
|
|
|
192
197
|
parser.render_calls.group_by(&:first).to_h do |method, nodes|
|
|
193
198
|
[ method.to_sym, nodes.collect { |v| v[1] } ]
|
|
194
|
-
end
|
|
199
|
+
end.map do |method, nodes|
|
|
200
|
+
nodes.map { |n| parse_render(n) }
|
|
201
|
+
end.flatten.compact
|
|
195
202
|
end
|
|
203
|
+
|
|
204
|
+
private
|
|
205
|
+
def resolve_path_directory(path)
|
|
206
|
+
if path.include?("/")
|
|
207
|
+
path
|
|
208
|
+
else
|
|
209
|
+
"#{directory}/#{path}"
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
# Convert
|
|
214
|
+
# render("foo", ...)
|
|
215
|
+
# into either
|
|
216
|
+
# render(template: "foo", ...)
|
|
217
|
+
# or
|
|
218
|
+
# render(partial: "foo", ...)
|
|
219
|
+
def normalize_args(string, options_hash)
|
|
220
|
+
if options_hash
|
|
221
|
+
{ partial: string, locals: options_hash }
|
|
222
|
+
else
|
|
223
|
+
{ partial: string }
|
|
224
|
+
end
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
def parse_render(node)
|
|
228
|
+
node = node.argument_nodes
|
|
229
|
+
|
|
230
|
+
if (node.length == 1 || node.length == 2) && !node[0].hash?
|
|
231
|
+
if node.length == 1
|
|
232
|
+
options = normalize_args(node[0], nil)
|
|
233
|
+
elsif node.length == 2
|
|
234
|
+
options = normalize_args(node[0], node[1])
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
return nil unless options
|
|
238
|
+
|
|
239
|
+
parse_render_from_options(options)
|
|
240
|
+
elsif node.length == 1 && node[0].hash?
|
|
241
|
+
options = parse_hash_to_symbols(node[0])
|
|
242
|
+
|
|
243
|
+
return nil unless options
|
|
244
|
+
|
|
245
|
+
parse_render_from_options(options)
|
|
246
|
+
else
|
|
247
|
+
nil
|
|
248
|
+
end
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
def parse_hash(node)
|
|
252
|
+
node.hash? && node.to_hash
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
def parse_hash_to_symbols(node)
|
|
256
|
+
hash = parse_hash(node)
|
|
257
|
+
|
|
258
|
+
return unless hash
|
|
259
|
+
|
|
260
|
+
hash.transform_keys do |key_node|
|
|
261
|
+
key = parse_sym(key_node)
|
|
262
|
+
|
|
263
|
+
return unless key
|
|
264
|
+
|
|
265
|
+
key
|
|
266
|
+
end
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
def parse_render_from_options(options_hash)
|
|
270
|
+
renders = []
|
|
271
|
+
keys = options_hash.keys
|
|
272
|
+
|
|
273
|
+
if (keys & RENDER_TYPE_KEYS).size < 1
|
|
274
|
+
# Must have at least one of render keys
|
|
275
|
+
return nil
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
if (keys - ALL_KNOWN_KEYS).any?
|
|
279
|
+
# de-opt in case of unknown option
|
|
280
|
+
return nil
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
render_type = (keys & RENDER_TYPE_KEYS)[0]
|
|
284
|
+
|
|
285
|
+
node = options_hash[render_type]
|
|
286
|
+
|
|
287
|
+
if node.string?
|
|
288
|
+
template = resolve_path_directory(node.to_string)
|
|
289
|
+
else
|
|
290
|
+
if node.variable_reference?
|
|
291
|
+
dependency = node.variable_name.sub(/\A(?:\$|@{1,2})/, "")
|
|
292
|
+
elsif node.vcall?
|
|
293
|
+
dependency = node.variable_name
|
|
294
|
+
elsif node.call?
|
|
295
|
+
dependency = node.call_method_name
|
|
296
|
+
else
|
|
297
|
+
return
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
object_template = true
|
|
301
|
+
template = "#{dependency.pluralize}/#{dependency.singularize}"
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
return unless template
|
|
305
|
+
|
|
306
|
+
if spacer_template = render_template_with_spacer?(options_hash)
|
|
307
|
+
virtual_path = partial_to_virtual_path(:partial, spacer_template)
|
|
308
|
+
renders << virtual_path
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
if options_hash.key?(:object) || options_hash.key?(:collection) || object_template
|
|
312
|
+
return nil if options_hash.key?(:object) && options_hash.key?(:collection)
|
|
313
|
+
return nil unless options_hash.key?(:partial)
|
|
314
|
+
end
|
|
315
|
+
|
|
316
|
+
virtual_path = partial_to_virtual_path(render_type, template)
|
|
317
|
+
renders << virtual_path
|
|
318
|
+
|
|
319
|
+
# Support for rendering multiple templates (i.e. a partial with a layout)
|
|
320
|
+
if layout_template = render_template_with_layout?(render_type, options_hash)
|
|
321
|
+
virtual_path = partial_to_virtual_path(:layout, layout_template)
|
|
322
|
+
|
|
323
|
+
renders << virtual_path
|
|
324
|
+
end
|
|
325
|
+
|
|
326
|
+
renders
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
def parse_str(node)
|
|
330
|
+
node.string? && node.to_string
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
def parse_sym(node)
|
|
334
|
+
node.symbol? && node.to_symbol
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
def render_template_with_layout?(render_type, options_hash)
|
|
338
|
+
if render_type != :layout && options_hash.key?(:layout)
|
|
339
|
+
parse_str(options_hash[:layout])
|
|
340
|
+
end
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
def render_template_with_spacer?(options_hash)
|
|
344
|
+
if options_hash.key?(:spacer_template)
|
|
345
|
+
parse_str(options_hash[:spacer_template])
|
|
346
|
+
end
|
|
347
|
+
end
|
|
196
348
|
end
|
|
197
349
|
end
|
|
198
350
|
end
|
|
@@ -1,176 +1,19 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "action_view/ripper_ast_parser"
|
|
4
|
-
|
|
5
3
|
module ActionView
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
@code = code
|
|
10
|
-
@parser = RipperASTParser
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
def render_calls
|
|
14
|
-
render_nodes = @parser.parse_render_nodes(@code)
|
|
15
|
-
|
|
16
|
-
render_nodes.map do |method, nodes|
|
|
17
|
-
nodes.map { |n| send(:parse_render, n) }
|
|
18
|
-
end.flatten.compact
|
|
19
|
-
end
|
|
4
|
+
module RenderParser # :nodoc:
|
|
5
|
+
ALL_KNOWN_KEYS = [:partial, :template, :layout, :formats, :locals, :object, :collection, :as, :status, :content_type, :location, :spacer_template]
|
|
6
|
+
RENDER_TYPE_KEYS = [:partial, :template, :layout]
|
|
20
7
|
|
|
21
|
-
|
|
22
|
-
def
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
def resolve_path_directory(path)
|
|
27
|
-
if path.include?("/")
|
|
28
|
-
path
|
|
29
|
-
else
|
|
30
|
-
"#{directory}/#{path}"
|
|
31
|
-
end
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
# Convert
|
|
35
|
-
# render("foo", ...)
|
|
36
|
-
# into either
|
|
37
|
-
# render(template: "foo", ...)
|
|
38
|
-
# or
|
|
39
|
-
# render(partial: "foo", ...)
|
|
40
|
-
def normalize_args(string, options_hash)
|
|
41
|
-
if options_hash
|
|
42
|
-
{ partial: string, locals: options_hash }
|
|
43
|
-
else
|
|
44
|
-
{ partial: string }
|
|
45
|
-
end
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
def parse_render(node)
|
|
49
|
-
node = node.argument_nodes
|
|
50
|
-
|
|
51
|
-
if (node.length == 1 || node.length == 2) && !node[0].hash?
|
|
52
|
-
if node.length == 1
|
|
53
|
-
options = normalize_args(node[0], nil)
|
|
54
|
-
elsif node.length == 2
|
|
55
|
-
options = normalize_args(node[0], node[1])
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
return nil unless options
|
|
59
|
-
|
|
60
|
-
parse_render_from_options(options)
|
|
61
|
-
elsif node.length == 1 && node[0].hash?
|
|
62
|
-
options = parse_hash_to_symbols(node[0])
|
|
63
|
-
|
|
64
|
-
return nil unless options
|
|
65
|
-
|
|
66
|
-
parse_render_from_options(options)
|
|
67
|
-
else
|
|
68
|
-
nil
|
|
69
|
-
end
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
def parse_hash(node)
|
|
73
|
-
node.hash? && node.to_hash
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
def parse_hash_to_symbols(node)
|
|
77
|
-
hash = parse_hash(node)
|
|
78
|
-
|
|
79
|
-
return unless hash
|
|
80
|
-
|
|
81
|
-
hash.transform_keys do |key_node|
|
|
82
|
-
key = parse_sym(key_node)
|
|
83
|
-
|
|
84
|
-
return unless key
|
|
85
|
-
|
|
86
|
-
key
|
|
87
|
-
end
|
|
88
|
-
end
|
|
89
|
-
|
|
90
|
-
ALL_KNOWN_KEYS = [:partial, :template, :layout, :formats, :locals, :object, :collection, :as, :status, :content_type, :location, :spacer_template]
|
|
91
|
-
|
|
92
|
-
RENDER_TYPE_KEYS =
|
|
93
|
-
[:partial, :template, :layout]
|
|
94
|
-
|
|
95
|
-
def parse_render_from_options(options_hash)
|
|
96
|
-
renders = []
|
|
97
|
-
keys = options_hash.keys
|
|
98
|
-
|
|
99
|
-
if (keys & RENDER_TYPE_KEYS).size < 1
|
|
100
|
-
# Must have at least one of render keys
|
|
101
|
-
return nil
|
|
102
|
-
end
|
|
103
|
-
|
|
104
|
-
if (keys - ALL_KNOWN_KEYS).any?
|
|
105
|
-
# de-opt in case of unknown option
|
|
106
|
-
return nil
|
|
107
|
-
end
|
|
108
|
-
|
|
109
|
-
render_type = (keys & RENDER_TYPE_KEYS)[0]
|
|
110
|
-
|
|
111
|
-
node = options_hash[render_type]
|
|
112
|
-
|
|
113
|
-
if node.string?
|
|
114
|
-
template = resolve_path_directory(node.to_string)
|
|
115
|
-
else
|
|
116
|
-
if node.variable_reference?
|
|
117
|
-
dependency = node.variable_name.sub(/\A(?:\$|@{1,2})/, "")
|
|
118
|
-
elsif node.vcall?
|
|
119
|
-
dependency = node.variable_name
|
|
120
|
-
elsif node.call?
|
|
121
|
-
dependency = node.call_method_name
|
|
122
|
-
else
|
|
123
|
-
return
|
|
124
|
-
end
|
|
125
|
-
|
|
126
|
-
object_template = true
|
|
127
|
-
template = "#{dependency.pluralize}/#{dependency.singularize}"
|
|
128
|
-
end
|
|
129
|
-
|
|
130
|
-
return unless template
|
|
131
|
-
|
|
132
|
-
if spacer_template = render_template_with_spacer?(options_hash)
|
|
133
|
-
virtual_path = partial_to_virtual_path(:partial, spacer_template)
|
|
134
|
-
renders << virtual_path
|
|
135
|
-
end
|
|
136
|
-
|
|
137
|
-
if options_hash.key?(:object) || options_hash.key?(:collection) || object_template
|
|
138
|
-
return nil if options_hash.key?(:object) && options_hash.key?(:collection)
|
|
139
|
-
return nil unless options_hash.key?(:partial)
|
|
140
|
-
end
|
|
141
|
-
|
|
142
|
-
virtual_path = partial_to_virtual_path(render_type, template)
|
|
143
|
-
renders << virtual_path
|
|
144
|
-
|
|
145
|
-
# Support for rendering multiple templates (i.e. a partial with a layout)
|
|
146
|
-
if layout_template = render_template_with_layout?(render_type, options_hash)
|
|
147
|
-
virtual_path = partial_to_virtual_path(:layout, layout_template)
|
|
148
|
-
|
|
149
|
-
renders << virtual_path
|
|
150
|
-
end
|
|
151
|
-
|
|
152
|
-
renders
|
|
153
|
-
end
|
|
154
|
-
|
|
155
|
-
def parse_str(node)
|
|
156
|
-
node.string? && node.to_string
|
|
157
|
-
end
|
|
158
|
-
|
|
159
|
-
def parse_sym(node)
|
|
160
|
-
node.symbol? && node.to_symbol
|
|
8
|
+
class Base # :nodoc:
|
|
9
|
+
def initialize(name, code)
|
|
10
|
+
@name = name
|
|
11
|
+
@code = code
|
|
161
12
|
end
|
|
162
13
|
|
|
163
14
|
private
|
|
164
|
-
def
|
|
165
|
-
|
|
166
|
-
parse_str(options_hash[:layout])
|
|
167
|
-
end
|
|
168
|
-
end
|
|
169
|
-
|
|
170
|
-
def render_template_with_spacer?(options_hash)
|
|
171
|
-
if options_hash.key?(:spacer_template)
|
|
172
|
-
parse_str(options_hash[:spacer_template])
|
|
173
|
-
end
|
|
15
|
+
def directory
|
|
16
|
+
File.dirname(@name)
|
|
174
17
|
end
|
|
175
18
|
|
|
176
19
|
def partial_to_virtual_path(render_type, partial_path)
|
|
@@ -180,9 +23,18 @@ module ActionView
|
|
|
180
23
|
partial_path
|
|
181
24
|
end
|
|
182
25
|
end
|
|
26
|
+
end
|
|
183
27
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
28
|
+
# Check if prism is available. If it is, use it. Otherwise, use ripper.
|
|
29
|
+
begin
|
|
30
|
+
require "prism"
|
|
31
|
+
rescue LoadError
|
|
32
|
+
require "ripper"
|
|
33
|
+
require_relative "render_parser/ripper_render_parser"
|
|
34
|
+
Default = RipperRenderParser
|
|
35
|
+
else
|
|
36
|
+
require_relative "render_parser/prism_render_parser"
|
|
37
|
+
Default = PrismRenderParser
|
|
38
|
+
end
|
|
187
39
|
end
|
|
188
40
|
end
|
|
@@ -79,7 +79,7 @@ module ActionView
|
|
|
79
79
|
path = if object.respond_to?(:to_partial_path)
|
|
80
80
|
object.to_partial_path
|
|
81
81
|
else
|
|
82
|
-
raise ArgumentError.new("'#{object.inspect}' is not an ActiveModel-compatible object. It must implement
|
|
82
|
+
raise ArgumentError.new("'#{object.inspect}' is not an ActiveModel-compatible object. It must implement #to_partial_path.")
|
|
83
83
|
end
|
|
84
84
|
|
|
85
85
|
if view.prefix_partial_path_with_controller_namespace
|
|
@@ -48,6 +48,22 @@ module ActionView
|
|
|
48
48
|
#
|
|
49
49
|
# <%= render partial: "account", locals: { user: @buyer } %>
|
|
50
50
|
#
|
|
51
|
+
# == \Rendering variants of a partial
|
|
52
|
+
#
|
|
53
|
+
# The <tt>:variants</tt> option can be used to render a different template variant of a partial. For instance:
|
|
54
|
+
#
|
|
55
|
+
# <%= render partial: "account", variants: :mobile %>
|
|
56
|
+
#
|
|
57
|
+
# This will render <tt>_account.html+mobile.erb</tt>. This option also accepts multiple variants
|
|
58
|
+
# like so:
|
|
59
|
+
#
|
|
60
|
+
# <%= render partial: "account", variants: [:desktop, :mobile] %>
|
|
61
|
+
#
|
|
62
|
+
# This will look for the following templates and render the first one that exists:
|
|
63
|
+
# * <tt>_account.html+desktop.erb</tt>
|
|
64
|
+
# * <tt>_account.html+mobile.erb</tt>
|
|
65
|
+
# * <tt>_account.html.erb</tt>
|
|
66
|
+
#
|
|
51
67
|
# == \Rendering a collection of partials
|
|
52
68
|
#
|
|
53
69
|
# The example of partial use describes a familiar pattern where a template needs to iterate over an array and
|
|
@@ -94,7 +110,7 @@ module ActionView
|
|
|
94
110
|
# # <%= render partial: "accounts/account", locals: { account: @account} %>
|
|
95
111
|
# <%= render partial: @account %>
|
|
96
112
|
#
|
|
97
|
-
# # @posts is an array of Post instances, so every post record returns 'posts/post' on
|
|
113
|
+
# # @posts is an array of Post instances, so every post record returns 'posts/post' on #to_partial_path,
|
|
98
114
|
# # that's why we can replace:
|
|
99
115
|
# # <%= render partial: "posts/post", collection: @posts %>
|
|
100
116
|
# <%= render partial: @posts %>
|
|
@@ -114,7 +130,7 @@ module ActionView
|
|
|
114
130
|
# # <%= render partial: "accounts/account", locals: { account: @account} %>
|
|
115
131
|
# <%= render @account %>
|
|
116
132
|
#
|
|
117
|
-
# # @posts is an array of Post instances, so every post record returns 'posts/post' on
|
|
133
|
+
# # @posts is an array of Post instances, so every post record returns 'posts/post' on #to_partial_path,
|
|
118
134
|
# # that's why we can replace:
|
|
119
135
|
# # <%= render partial: "posts/post", collection: @posts %>
|
|
120
136
|
# <%= render @posts %>
|
|
@@ -45,12 +45,6 @@ module ActionView
|
|
|
45
45
|
end
|
|
46
46
|
end
|
|
47
47
|
|
|
48
|
-
# Direct access to template rendering.
|
|
49
|
-
def render_template(context, options) # :nodoc:
|
|
50
|
-
render_template_to_object(context, options).body
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
# Direct access to partial rendering.
|
|
54
48
|
def render_partial(context, options, &block) # :nodoc:
|
|
55
49
|
render_partial_to_object(context, options, &block).body
|
|
56
50
|
end
|
|
@@ -59,46 +53,46 @@ module ActionView
|
|
|
59
53
|
@cache_hits ||= {}
|
|
60
54
|
end
|
|
61
55
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
56
|
+
private
|
|
57
|
+
def render_template_to_object(context, options)
|
|
58
|
+
TemplateRenderer.new(@lookup_context).render(context, options)
|
|
59
|
+
end
|
|
65
60
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
61
|
+
def render_partial_to_object(context, options, &block)
|
|
62
|
+
partial = options[:partial]
|
|
63
|
+
if String === partial
|
|
64
|
+
collection = collection_from_options(options)
|
|
70
65
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
else
|
|
76
|
-
if options.key?(:object)
|
|
77
|
-
# Object + Partial
|
|
78
|
-
renderer = ObjectRenderer.new(@lookup_context, options)
|
|
79
|
-
renderer.render_object_with_partial(options[:object], partial, context, block)
|
|
66
|
+
if collection
|
|
67
|
+
# Collection + Partial
|
|
68
|
+
renderer = CollectionRenderer.new(@lookup_context, options)
|
|
69
|
+
renderer.render_collection_with_partial(collection, partial, context, block)
|
|
80
70
|
else
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
71
|
+
if options.key?(:object)
|
|
72
|
+
# Object + Partial
|
|
73
|
+
renderer = ObjectRenderer.new(@lookup_context, options)
|
|
74
|
+
renderer.render_object_with_partial(options[:object], partial, context, block)
|
|
75
|
+
else
|
|
76
|
+
# Partial
|
|
77
|
+
renderer = PartialRenderer.new(@lookup_context, options)
|
|
78
|
+
renderer.render(partial, context, block)
|
|
79
|
+
end
|
|
84
80
|
end
|
|
85
|
-
end
|
|
86
|
-
else
|
|
87
|
-
collection = collection_from_object(partial) || collection_from_options(options)
|
|
88
|
-
|
|
89
|
-
if collection
|
|
90
|
-
# Collection + Derived Partial
|
|
91
|
-
renderer = CollectionRenderer.new(@lookup_context, options)
|
|
92
|
-
renderer.render_collection_derive_partial(collection, context, block)
|
|
93
81
|
else
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
82
|
+
collection = collection_from_object(partial) || collection_from_options(options)
|
|
83
|
+
|
|
84
|
+
if collection
|
|
85
|
+
# Collection + Derived Partial
|
|
86
|
+
renderer = CollectionRenderer.new(@lookup_context, options)
|
|
87
|
+
renderer.render_collection_derive_partial(collection, context, block)
|
|
88
|
+
else
|
|
89
|
+
# Object + Derived Partial
|
|
90
|
+
renderer = ObjectRenderer.new(@lookup_context, options)
|
|
91
|
+
renderer.render_object_derive_partial(partial, context, block)
|
|
92
|
+
end
|
|
97
93
|
end
|
|
98
94
|
end
|
|
99
|
-
end
|
|
100
95
|
|
|
101
|
-
private
|
|
102
96
|
def collection_from_options(options)
|
|
103
97
|
if options.key?(:collection)
|
|
104
98
|
collection = options[:collection]
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "fiber"
|
|
4
3
|
|
|
5
4
|
module ActionView
|
|
6
5
|
# == TODO
|
|
@@ -26,6 +25,13 @@ module ActionView
|
|
|
26
25
|
self
|
|
27
26
|
end
|
|
28
27
|
|
|
28
|
+
# Returns the complete body as a string.
|
|
29
|
+
def body
|
|
30
|
+
buffer = String.new
|
|
31
|
+
each { |part| buffer << part }
|
|
32
|
+
buffer
|
|
33
|
+
end
|
|
34
|
+
|
|
29
35
|
private
|
|
30
36
|
# This is the same logging logic as in ShowExceptions middleware.
|
|
31
37
|
def log_error(exception)
|
|
@@ -43,7 +49,7 @@ module ActionView
|
|
|
43
49
|
# object that responds to each. This object is initialized with a block
|
|
44
50
|
# that knows how to render the template.
|
|
45
51
|
def render_template(view, template, layout_name = nil, locals = {}) # :nodoc:
|
|
46
|
-
return [super.body] unless
|
|
52
|
+
return [super.body] unless template.supports_streaming?
|
|
47
53
|
|
|
48
54
|
locals ||= {}
|
|
49
55
|
layout = find_layout(layout_name, locals.keys, [formats.first])
|
|
@@ -99,14 +99,14 @@ module ActionView
|
|
|
99
99
|
if layout.start_with?("/")
|
|
100
100
|
raise ArgumentError, "Rendering layouts from an absolute path is not supported."
|
|
101
101
|
else
|
|
102
|
-
@lookup_context.find_template(layout, nil, false,
|
|
102
|
+
@lookup_context.find_template(layout, nil, false, keys, details)
|
|
103
103
|
end
|
|
104
104
|
rescue ActionView::MissingTemplate
|
|
105
105
|
all_details = @details.merge(formats: @lookup_context.default_formats)
|
|
106
|
-
raise unless template_exists?(layout, nil, false,
|
|
106
|
+
raise unless template_exists?(layout, nil, false, keys, **all_details)
|
|
107
107
|
end
|
|
108
108
|
when Proc
|
|
109
|
-
resolve_layout(layout.call(@lookup_context, formats), keys, formats)
|
|
109
|
+
resolve_layout(layout.call(@lookup_context, formats, keys), keys, formats)
|
|
110
110
|
else
|
|
111
111
|
layout
|
|
112
112
|
end
|
|
@@ -27,10 +27,10 @@ module ActionView
|
|
|
27
27
|
extend ActiveSupport::Concern
|
|
28
28
|
include ActionView::ViewPaths
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
attr_internal_reader :rendered_format
|
|
31
31
|
|
|
32
32
|
def initialize
|
|
33
|
-
@
|
|
33
|
+
@_rendered_format = nil
|
|
34
34
|
super
|
|
35
35
|
end
|
|
36
36
|
|
|
@@ -118,6 +118,7 @@ module ActionView
|
|
|
118
118
|
|
|
119
119
|
def render_to_body(options = {})
|
|
120
120
|
_process_options(options)
|
|
121
|
+
_process_render_template_options(options)
|
|
121
122
|
_render_template(options)
|
|
122
123
|
end
|
|
123
124
|
|
|
@@ -136,7 +137,7 @@ module ActionView
|
|
|
136
137
|
end
|
|
137
138
|
|
|
138
139
|
rendered_format = rendered_template.format || lookup_context.formats.first
|
|
139
|
-
@
|
|
140
|
+
@_rendered_format = Template::Types[rendered_format]
|
|
140
141
|
|
|
141
142
|
rendered_template.body
|
|
142
143
|
end
|
|
@@ -173,18 +174,16 @@ module ActionView
|
|
|
173
174
|
end
|
|
174
175
|
|
|
175
176
|
# Normalize options.
|
|
176
|
-
def
|
|
177
|
-
options = super(options)
|
|
177
|
+
def _process_render_template_options(options)
|
|
178
178
|
if options[:partial] == true
|
|
179
179
|
options[:partial] = action_name
|
|
180
180
|
end
|
|
181
181
|
|
|
182
|
-
if
|
|
182
|
+
if !options.keys.intersect?([:partial, :file, :template])
|
|
183
183
|
options[:prefixes] ||= _prefixes
|
|
184
184
|
end
|
|
185
185
|
|
|
186
186
|
options[:template] ||= (options[:action] || action_name).to_s
|
|
187
|
-
options
|
|
188
187
|
end
|
|
189
188
|
end
|
|
190
189
|
end
|