actionview 7.0.8 → 7.2.0
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 +54 -439
- data/MIT-LICENSE +1 -1
- data/README.rdoc +1 -1
- data/app/assets/javascripts/rails-ujs.esm.js +686 -0
- data/app/assets/javascripts/rails-ujs.js +630 -0
- data/lib/action_view/base.rb +52 -14
- data/lib/action_view/buffers.rb +106 -8
- data/lib/action_view/cache_expiry.rb +44 -41
- data/lib/action_view/context.rb +1 -1
- data/lib/action_view/dependency_tracker/{ripper_tracker.rb → ruby_tracker.rb} +4 -3
- data/lib/action_view/dependency_tracker.rb +1 -1
- data/lib/action_view/deprecator.rb +7 -0
- data/lib/action_view/digestor.rb +1 -1
- data/lib/action_view/gem_version.rb +3 -3
- data/lib/action_view/helpers/active_model_helper.rb +1 -1
- data/lib/action_view/helpers/asset_tag_helper.rb +151 -55
- data/lib/action_view/helpers/asset_url_helper.rb +6 -5
- data/lib/action_view/helpers/atom_feed_helper.rb +5 -5
- data/lib/action_view/helpers/cache_helper.rb +7 -13
- data/lib/action_view/helpers/capture_helper.rb +30 -10
- data/lib/action_view/helpers/content_exfiltration_prevention_helper.rb +70 -0
- data/lib/action_view/helpers/controller_helper.rb +6 -0
- data/lib/action_view/helpers/csp_helper.rb +2 -2
- data/lib/action_view/helpers/csrf_helper.rb +3 -3
- data/lib/action_view/helpers/date_helper.rb +17 -19
- data/lib/action_view/helpers/debug_helper.rb +3 -3
- data/lib/action_view/helpers/form_helper.rb +248 -214
- data/lib/action_view/helpers/form_options_helper.rb +2 -1
- data/lib/action_view/helpers/form_tag_helper.rb +121 -54
- data/lib/action_view/helpers/javascript_helper.rb +1 -0
- data/lib/action_view/helpers/number_helper.rb +37 -330
- data/lib/action_view/helpers/output_safety_helper.rb +6 -6
- data/lib/action_view/helpers/rendering_helper.rb +1 -1
- data/lib/action_view/helpers/sanitize_helper.rb +51 -21
- data/lib/action_view/helpers/tag_helper.rb +210 -42
- data/lib/action_view/helpers/tags/base.rb +11 -52
- data/lib/action_view/helpers/tags/collection_check_boxes.rb +1 -0
- data/lib/action_view/helpers/tags/collection_radio_buttons.rb +1 -0
- data/lib/action_view/helpers/tags/collection_select.rb +3 -0
- data/lib/action_view/helpers/tags/date_field.rb +1 -1
- data/lib/action_view/helpers/tags/date_select.rb +2 -0
- data/lib/action_view/helpers/tags/datetime_field.rb +14 -6
- data/lib/action_view/helpers/tags/datetime_local_field.rb +11 -2
- data/lib/action_view/helpers/tags/grouped_collection_select.rb +3 -0
- data/lib/action_view/helpers/tags/month_field.rb +1 -1
- data/lib/action_view/helpers/tags/select.rb +3 -0
- data/lib/action_view/helpers/tags/select_renderer.rb +56 -0
- data/lib/action_view/helpers/tags/time_field.rb +1 -1
- data/lib/action_view/helpers/tags/time_zone_select.rb +3 -0
- data/lib/action_view/helpers/tags/week_field.rb +1 -1
- data/lib/action_view/helpers/tags/weekday_select.rb +3 -0
- data/lib/action_view/helpers/tags.rb +2 -0
- data/lib/action_view/helpers/text_helper.rb +156 -84
- data/lib/action_view/helpers/translation_helper.rb +3 -3
- data/lib/action_view/helpers/url_helper.rb +31 -78
- data/lib/action_view/helpers.rb +2 -0
- data/lib/action_view/layouts.rb +8 -8
- data/lib/action_view/log_subscriber.rb +57 -36
- data/lib/action_view/lookup_context.rb +29 -13
- data/lib/action_view/path_registry.rb +57 -0
- data/lib/action_view/path_set.rb +13 -14
- data/lib/action_view/railtie.rb +25 -3
- data/lib/action_view/record_identifier.rb +15 -8
- data/lib/action_view/render_parser/prism_render_parser.rb +127 -0
- data/lib/action_view/render_parser/ripper_render_parser.rb +341 -0
- data/lib/action_view/render_parser.rb +21 -169
- data/lib/action_view/renderer/abstract_renderer.rb +2 -2
- data/lib/action_view/renderer/collection_renderer.rb +10 -2
- data/lib/action_view/renderer/partial_renderer/collection_caching.rb +2 -1
- data/lib/action_view/renderer/partial_renderer.rb +2 -1
- data/lib/action_view/renderer/renderer.rb +34 -38
- data/lib/action_view/renderer/streaming_template_renderer.rb +3 -2
- data/lib/action_view/renderer/template_renderer.rb +3 -2
- data/lib/action_view/rendering.rb +26 -8
- data/lib/action_view/template/error.rb +14 -1
- data/lib/action_view/template/handlers/builder.rb +4 -4
- data/lib/action_view/template/handlers/erb/erubi.rb +23 -27
- data/lib/action_view/template/handlers/erb.rb +73 -1
- data/lib/action_view/template/handlers.rb +1 -1
- data/lib/action_view/template/html.rb +1 -1
- data/lib/action_view/template/raw_file.rb +1 -1
- data/lib/action_view/template/renderable.rb +8 -2
- data/lib/action_view/template/resolver.rb +9 -3
- data/lib/action_view/template/text.rb +1 -1
- data/lib/action_view/template/types.rb +25 -34
- data/lib/action_view/template.rb +268 -55
- data/lib/action_view/template_path.rb +2 -0
- data/lib/action_view/test_case.rb +181 -28
- data/lib/action_view/unbound_template.rb +17 -7
- data/lib/action_view/version.rb +1 -1
- data/lib/action_view/view_paths.rb +15 -24
- data/lib/action_view.rb +4 -1
- metadata +28 -28
- data/lib/action_view/ripper_ast_parser.rb +0 -198
- data/lib/assets/compiled/rails-ujs.js +0 -777
data/lib/action_view/template.rb
CHANGED
@@ -4,18 +4,20 @@ require "thread"
|
|
4
4
|
require "delegate"
|
5
5
|
|
6
6
|
module ActionView
|
7
|
-
# = Action View Template
|
7
|
+
# = Action View \Template
|
8
8
|
class Template
|
9
9
|
extend ActiveSupport::Autoload
|
10
10
|
|
11
|
+
STRICT_LOCALS_REGEX = /\#\s+locals:\s+\((.*)\)/
|
12
|
+
|
11
13
|
# === Encodings in ActionView::Template
|
12
14
|
#
|
13
15
|
# ActionView::Template is one of a few sources of potential
|
14
|
-
# encoding issues in Rails. This is because the source for
|
16
|
+
# encoding issues in \Rails. This is because the source for
|
15
17
|
# templates are usually read from disk, and Ruby (like most
|
16
18
|
# encoding-aware programming languages) assumes that the
|
17
19
|
# String retrieved through File IO is encoded in the
|
18
|
-
# <tt>default_external</tt> encoding. In Rails, the default
|
20
|
+
# <tt>default_external</tt> encoding. In \Rails, the default
|
19
21
|
# <tt>default_external</tt> encoding is UTF-8.
|
20
22
|
#
|
21
23
|
# As a result, if a user saves their template as ISO-8859-1
|
@@ -34,13 +36,13 @@ module ActionView
|
|
34
36
|
# to the problem.
|
35
37
|
# 2. The user can specify the encoding using Ruby-style
|
36
38
|
# encoding comments in any template engine. If such
|
37
|
-
# a comment is supplied, Rails will apply that encoding
|
39
|
+
# a comment is supplied, \Rails will apply that encoding
|
38
40
|
# to the resulting compiled source returned by the
|
39
41
|
# template handler.
|
40
42
|
# 3. In all cases, we transcode the resulting String to
|
41
43
|
# the UTF-8.
|
42
44
|
#
|
43
|
-
# This means that other parts of Rails can always assume
|
45
|
+
# This means that other parts of \Rails can always assume
|
44
46
|
# that templates are encoded in UTF-8, even if the original
|
45
47
|
# source of the template was not UTF-8.
|
46
48
|
#
|
@@ -51,7 +53,7 @@ module ActionView
|
|
51
53
|
# === Instructions for template handlers
|
52
54
|
#
|
53
55
|
# The easiest thing for you to do is to simply ignore
|
54
|
-
# encodings. Rails will hand you the template source
|
56
|
+
# encodings. \Rails will hand you the template source
|
55
57
|
# as the default_internal (generally UTF-8), raising
|
56
58
|
# an exception for the user before sending the template
|
57
59
|
# to you if it could not determine the original encoding.
|
@@ -68,7 +70,7 @@ module ActionView
|
|
68
70
|
# you may indicate that you will handle encodings yourself
|
69
71
|
# by implementing <tt>handles_encoding?</tt> on your handler.
|
70
72
|
#
|
71
|
-
# If you do, Rails will not try to encode the String
|
73
|
+
# If you do, \Rails will not try to encode the String
|
72
74
|
# into the default_internal, passing you the unaltered
|
73
75
|
# bytes tagged with the assumed encoding (from
|
74
76
|
# default_external).
|
@@ -94,11 +96,72 @@ module ActionView
|
|
94
96
|
#
|
95
97
|
# Given this sub template rendering:
|
96
98
|
#
|
97
|
-
# <%= render "
|
99
|
+
# <%= render "application/header", { headline: "Welcome", person: person } %>
|
98
100
|
#
|
99
101
|
# You can use +local_assigns+ in the sub templates to access the local variables:
|
100
102
|
#
|
101
103
|
# local_assigns[:headline] # => "Welcome"
|
104
|
+
#
|
105
|
+
# Each key in +local_assigns+ is available as a partial-local variable:
|
106
|
+
#
|
107
|
+
# local_assigns[:headline] # => "Welcome"
|
108
|
+
# headline # => "Welcome"
|
109
|
+
#
|
110
|
+
# Since +local_assigns+ is a +Hash+, it's compatible with Ruby 3.1's pattern
|
111
|
+
# matching assignment operator:
|
112
|
+
#
|
113
|
+
# local_assigns => { headline:, **options }
|
114
|
+
# headline # => "Welcome"
|
115
|
+
# options # => {}
|
116
|
+
#
|
117
|
+
# Pattern matching assignment also supports variable renaming:
|
118
|
+
#
|
119
|
+
# local_assigns => { headline: title }
|
120
|
+
# title # => "Welcome"
|
121
|
+
#
|
122
|
+
# If a template refers to a variable that isn't passed into the view as part
|
123
|
+
# of the <tt>locals: { ... }</tt> Hash, the template will raise an
|
124
|
+
# +ActionView::Template::Error+:
|
125
|
+
#
|
126
|
+
# <%# => raises ActionView::Template::Error %>
|
127
|
+
# <% alerts.each do |alert| %>
|
128
|
+
# <p><%= alert %></p>
|
129
|
+
# <% end %>
|
130
|
+
#
|
131
|
+
# Since +local_assigns+ returns a +Hash+ instance, you can conditionally
|
132
|
+
# read a variable, then fall back to a default value when
|
133
|
+
# the key isn't part of the <tt>locals: { ... }</tt> options:
|
134
|
+
#
|
135
|
+
# <% local_assigns.fetch(:alerts, []).each do |alert| %>
|
136
|
+
# <p><%= alert %></p>
|
137
|
+
# <% end %>
|
138
|
+
#
|
139
|
+
# Combining Ruby 3.1's pattern matching assignment with calls to
|
140
|
+
# +Hash#with_defaults+ enables compact partial-local variable
|
141
|
+
# assignments:
|
142
|
+
#
|
143
|
+
# <% local_assigns.with_defaults(alerts: []) => { headline:, alerts: } %>
|
144
|
+
#
|
145
|
+
# <h1><%= headline %></h1>
|
146
|
+
#
|
147
|
+
# <% alerts.each do |alert| %>
|
148
|
+
# <p><%= alert %></p>
|
149
|
+
# <% end %>
|
150
|
+
#
|
151
|
+
# By default, templates will accept any <tt>locals</tt> as keyword arguments
|
152
|
+
# and make them available to <tt>local_assigns</tt>. To restrict what
|
153
|
+
# <tt>local_assigns</tt> a template will accept, add a <tt>locals:</tt> magic comment:
|
154
|
+
#
|
155
|
+
# <%# locals: (headline:, alerts: []) %>
|
156
|
+
#
|
157
|
+
# <h1><%= headline %></h1>
|
158
|
+
#
|
159
|
+
# <% alerts.each do |alert| %>
|
160
|
+
# <p><%= alert %></p>
|
161
|
+
# <% end %>
|
162
|
+
#
|
163
|
+
# Read more about strict locals in {Action View Overview}[https://guides.rubyonrails.org/action_view_overview.html#strict-locals]
|
164
|
+
# in the guides.
|
102
165
|
|
103
166
|
eager_autoload do
|
104
167
|
autoload :Error
|
@@ -107,6 +170,7 @@ module ActionView
|
|
107
170
|
autoload :Handlers
|
108
171
|
autoload :HTML
|
109
172
|
autoload :Inline
|
173
|
+
autoload :Types
|
110
174
|
autoload :Sources
|
111
175
|
autoload :Text
|
112
176
|
autoload :Types
|
@@ -117,11 +181,24 @@ module ActionView
|
|
117
181
|
singleton_class.attr_accessor :frozen_string_literal
|
118
182
|
@frozen_string_literal = false
|
119
183
|
|
184
|
+
class << self # :nodoc:
|
185
|
+
def mime_types_implementation=(implementation)
|
186
|
+
# This method isn't thread-safe, but it's not supposed
|
187
|
+
# to be called after initialization
|
188
|
+
if self::Types != implementation
|
189
|
+
remove_const(:Types)
|
190
|
+
const_set(:Types, implementation)
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
120
195
|
attr_reader :identifier, :handler
|
121
|
-
attr_reader :variable, :format, :variant, :
|
196
|
+
attr_reader :variable, :format, :variant, :virtual_path
|
197
|
+
|
198
|
+
NONE = Object.new
|
122
199
|
|
123
200
|
def initialize(source, identifier, handler, locals:, format: nil, variant: nil, virtual_path: nil)
|
124
|
-
@source = source
|
201
|
+
@source = source.dup
|
125
202
|
@identifier = identifier
|
126
203
|
@handler = handler
|
127
204
|
@compiled = false
|
@@ -137,6 +214,37 @@ module ActionView
|
|
137
214
|
@format = format
|
138
215
|
@variant = variant
|
139
216
|
@compile_mutex = Mutex.new
|
217
|
+
@strict_locals = NONE
|
218
|
+
@strict_local_keys = nil
|
219
|
+
@type = nil
|
220
|
+
end
|
221
|
+
|
222
|
+
# The locals this template has been or will be compiled for, or nil if this
|
223
|
+
# is a strict locals template.
|
224
|
+
def locals
|
225
|
+
if strict_locals?
|
226
|
+
nil
|
227
|
+
else
|
228
|
+
@locals
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
def spot(location) # :nodoc:
|
233
|
+
ast = RubyVM::AbstractSyntaxTree.parse(compiled_source, keep_script_lines: true)
|
234
|
+
node_id = RubyVM::AbstractSyntaxTree.node_id_for_backtrace_location(location)
|
235
|
+
node = find_node_by_id(ast, node_id)
|
236
|
+
|
237
|
+
ErrorHighlight.spot(node)
|
238
|
+
end
|
239
|
+
|
240
|
+
# Translate an error location returned by ErrorHighlight to the correct
|
241
|
+
# source location inside the template.
|
242
|
+
def translate_location(backtrace_location, spot)
|
243
|
+
if handler.respond_to?(:translate_location)
|
244
|
+
handler.translate_location(spot, backtrace_location, encode!) || spot
|
245
|
+
else
|
246
|
+
spot
|
247
|
+
end
|
140
248
|
end
|
141
249
|
|
142
250
|
# Returns whether the underlying handler supports streaming. If so,
|
@@ -151,10 +259,22 @@ module ActionView
|
|
151
259
|
# This method is instrumented as "!render_template.action_view". Notice that
|
152
260
|
# we use a bang in this instrumentation because you don't want to
|
153
261
|
# consume this in production. This is only slow if it's being listened to.
|
154
|
-
def render(view, locals, buffer =
|
262
|
+
def render(view, locals, buffer = nil, implicit_locals: [], add_to_stack: true, &block)
|
155
263
|
instrument_render_template do
|
156
264
|
compile!(view)
|
157
|
-
|
265
|
+
|
266
|
+
if strict_locals? && @strict_local_keys && !implicit_locals.empty?
|
267
|
+
locals_to_ignore = implicit_locals - @strict_local_keys
|
268
|
+
locals.except!(*locals_to_ignore)
|
269
|
+
end
|
270
|
+
|
271
|
+
if buffer
|
272
|
+
view._run(method_name, self, locals, buffer, add_to_stack: add_to_stack, has_strict_locals: strict_locals?, &block)
|
273
|
+
nil
|
274
|
+
else
|
275
|
+
result = view._run(method_name, self, locals, OutputBuffer.new, add_to_stack: add_to_stack, has_strict_locals: strict_locals?, &block)
|
276
|
+
result.is_a?(OutputBuffer) ? result.to_s : result
|
277
|
+
end
|
158
278
|
end
|
159
279
|
rescue => e
|
160
280
|
handle_render_error(view, e)
|
@@ -169,20 +289,23 @@ module ActionView
|
|
169
289
|
end
|
170
290
|
|
171
291
|
def inspect
|
172
|
-
"#<#{self.class.name} #{short_identifier} locals=#{
|
292
|
+
"#<#{self.class.name} #{short_identifier} locals=#{locals.inspect}>"
|
173
293
|
end
|
174
294
|
|
175
295
|
def source
|
176
296
|
@source.to_s
|
177
297
|
end
|
178
298
|
|
299
|
+
LEADING_ENCODING_REGEXP = /\A#{ENCODING_FLAG}/
|
300
|
+
private_constant :LEADING_ENCODING_REGEXP
|
301
|
+
|
179
302
|
# This method is responsible for properly setting the encoding of the
|
180
303
|
# source. Until this point, we assume that the source is BINARY data.
|
181
304
|
# If no additional information is supplied, we assume the encoding is
|
182
305
|
# the same as <tt>Encoding.default_external</tt>.
|
183
306
|
#
|
184
307
|
# The user can also specify the encoding via a comment on the first
|
185
|
-
# line of the template (
|
308
|
+
# line of the template (<tt># encoding: NAME-OF-ENCODING</tt>). This will work
|
186
309
|
# with any template engine, as we process out the encoding comment
|
187
310
|
# before passing the source on to the template engine, leaving a
|
188
311
|
# blank line in its stead.
|
@@ -194,7 +317,7 @@ module ActionView
|
|
194
317
|
# Look for # encoding: *. If we find one, we'll encode the
|
195
318
|
# String in that encoding, otherwise, we'll use the
|
196
319
|
# default external encoding.
|
197
|
-
if source.sub!(
|
320
|
+
if source.sub!(LEADING_ENCODING_REGEXP, "")
|
198
321
|
encoding = magic_encoding = $1
|
199
322
|
else
|
200
323
|
encoding = Encoding.default_external
|
@@ -222,6 +345,32 @@ module ActionView
|
|
222
345
|
end
|
223
346
|
end
|
224
347
|
|
348
|
+
# This method is responsible for marking a template as having strict locals
|
349
|
+
# which means the template can only accept the locals defined in a magic
|
350
|
+
# comment. For example, if your template acceps the locals +title+ and
|
351
|
+
# +comment_count+, add the following to your template file:
|
352
|
+
#
|
353
|
+
# <%# locals: (title: "Default title", comment_count: 0) %>
|
354
|
+
#
|
355
|
+
# Strict locals are useful for validating template arguments and for
|
356
|
+
# specifying defaults.
|
357
|
+
def strict_locals!
|
358
|
+
if @strict_locals == NONE
|
359
|
+
self.source.sub!(STRICT_LOCALS_REGEX, "")
|
360
|
+
@strict_locals = $1
|
361
|
+
|
362
|
+
return if @strict_locals.nil? # Magic comment not found
|
363
|
+
|
364
|
+
@strict_locals = "**nil" if @strict_locals.blank?
|
365
|
+
end
|
366
|
+
|
367
|
+
@strict_locals
|
368
|
+
end
|
369
|
+
|
370
|
+
# Returns whether a template is using strict locals.
|
371
|
+
def strict_locals?
|
372
|
+
strict_locals!
|
373
|
+
end
|
225
374
|
|
226
375
|
# Exceptions are marshalled when using the parallel test runner with DRb, so we need
|
227
376
|
# to ensure that references to the template object can be marshalled as well. This means forgoing
|
@@ -235,7 +384,26 @@ module ActionView
|
|
235
384
|
@compile_mutex = Mutex.new
|
236
385
|
end
|
237
386
|
|
387
|
+
def method_name # :nodoc:
|
388
|
+
@method_name ||= begin
|
389
|
+
m = +"_#{identifier_method_name}__#{@identifier.hash}_#{__id__}"
|
390
|
+
m.tr!("-", "_")
|
391
|
+
m
|
392
|
+
end
|
393
|
+
end
|
394
|
+
|
238
395
|
private
|
396
|
+
def find_node_by_id(node, node_id)
|
397
|
+
return node if node.node_id == node_id
|
398
|
+
|
399
|
+
node.children.grep(node.class).each do |child|
|
400
|
+
found = find_node_by_id(child, node_id)
|
401
|
+
return found if found
|
402
|
+
end
|
403
|
+
|
404
|
+
false
|
405
|
+
end
|
406
|
+
|
239
407
|
# Compile a template. This method ensures a template is compiled
|
240
408
|
# just once and removes the source after it is compiled.
|
241
409
|
def compile!(view)
|
@@ -260,27 +428,29 @@ module ActionView
|
|
260
428
|
end
|
261
429
|
end
|
262
430
|
|
263
|
-
#
|
264
|
-
#
|
265
|
-
#
|
266
|
-
|
267
|
-
|
268
|
-
# the template engine to support additional mechanisms for
|
269
|
-
# specifying the encoding. For instance, ERB supports <%# encoding: %>
|
270
|
-
#
|
271
|
-
# Otherwise, after we figure out the correct encoding, we then
|
272
|
-
# encode the source into <tt>Encoding.default_internal</tt>.
|
273
|
-
# In general, this means that templates will be UTF-8 inside of Rails,
|
274
|
-
# regardless of the original source encoding.
|
275
|
-
def compile(mod)
|
431
|
+
# This method compiles the source of the template. The compilation of templates
|
432
|
+
# involves setting strict_locals! if applicable, encoding the template, and setting
|
433
|
+
# frozen string literal.
|
434
|
+
def compiled_source
|
435
|
+
set_strict_locals = strict_locals!
|
276
436
|
source = encode!
|
277
437
|
code = @handler.call(self, source)
|
278
438
|
|
439
|
+
method_arguments =
|
440
|
+
if set_strict_locals
|
441
|
+
if set_strict_locals.include?("&")
|
442
|
+
"local_assigns, output_buffer, #{set_strict_locals}"
|
443
|
+
else
|
444
|
+
"local_assigns, output_buffer, #{set_strict_locals}, &_"
|
445
|
+
end
|
446
|
+
else
|
447
|
+
"local_assigns, output_buffer, &_"
|
448
|
+
end
|
449
|
+
|
279
450
|
# Make sure that the resulting String to be eval'd is in the
|
280
451
|
# encoding of the code
|
281
|
-
original_source = source
|
282
452
|
source = +<<-end_src
|
283
|
-
def #{method_name}(
|
453
|
+
def #{method_name}(#{method_arguments})
|
284
454
|
@virtual_path = #{@virtual_path.inspect};#{locals_code};#{code}
|
285
455
|
end
|
286
456
|
end_src
|
@@ -299,17 +469,71 @@ module ActionView
|
|
299
469
|
raise WrongEncodingError.new(source, Encoding.default_internal)
|
300
470
|
end
|
301
471
|
|
472
|
+
if Template.frozen_string_literal
|
473
|
+
"# frozen_string_literal: true\n#{source}"
|
474
|
+
else
|
475
|
+
source
|
476
|
+
end
|
477
|
+
end
|
478
|
+
|
479
|
+
# Among other things, this method is responsible for properly setting
|
480
|
+
# the encoding of the compiled template.
|
481
|
+
#
|
482
|
+
# If the template engine handles encodings, we send the encoded
|
483
|
+
# String to the engine without further processing. This allows
|
484
|
+
# the template engine to support additional mechanisms for
|
485
|
+
# specifying the encoding. For instance, ERB supports <%# encoding: %>
|
486
|
+
#
|
487
|
+
# Otherwise, after we figure out the correct encoding, we then
|
488
|
+
# encode the source into <tt>Encoding.default_internal</tt>.
|
489
|
+
# In general, this means that templates will be UTF-8 inside of Rails,
|
490
|
+
# regardless of the original source encoding.
|
491
|
+
def compile(mod)
|
302
492
|
begin
|
303
|
-
|
304
|
-
mod.module_eval("# frozen_string_literal: true\n#{source}", identifier, -1)
|
305
|
-
else
|
306
|
-
mod.module_eval(source, identifier, 0)
|
307
|
-
end
|
493
|
+
mod.module_eval(compiled_source, identifier, offset)
|
308
494
|
rescue SyntaxError
|
309
495
|
# Account for when code in the template is not syntactically valid; e.g. if we're using
|
310
496
|
# ERB and the user writes <%= foo( %>, attempting to call a helper `foo` and interpolate
|
311
497
|
# the result into the template, but missing an end parenthesis.
|
312
|
-
raise SyntaxErrorInTemplate.new(self,
|
498
|
+
raise SyntaxErrorInTemplate.new(self, encode!)
|
499
|
+
end
|
500
|
+
|
501
|
+
return unless strict_locals?
|
502
|
+
|
503
|
+
parameters = mod.instance_method(method_name).parameters
|
504
|
+
parameters -= [[:req, :local_assigns], [:req, :output_buffer]]
|
505
|
+
|
506
|
+
# Check compiled method parameters to ensure that only kwargs
|
507
|
+
# were provided as strict locals, preventing `locals: (foo, *foo)` etc
|
508
|
+
# and allowing `locals: (foo:)`.
|
509
|
+
non_kwarg_parameters = parameters.select do |parameter|
|
510
|
+
![:keyreq, :key, :keyrest, :nokey].include?(parameter[0])
|
511
|
+
end
|
512
|
+
|
513
|
+
non_kwarg_parameters.pop if non_kwarg_parameters.last == %i(block _)
|
514
|
+
|
515
|
+
unless non_kwarg_parameters.empty?
|
516
|
+
mod.undef_method(method_name)
|
517
|
+
|
518
|
+
raise ArgumentError.new(
|
519
|
+
"#{non_kwarg_parameters.map { |_, name| "`#{name}`" }.to_sentence} set as non-keyword " \
|
520
|
+
"#{'argument'.pluralize(non_kwarg_parameters.length)} for #{short_identifier}. " \
|
521
|
+
"Locals can only be set as keyword arguments."
|
522
|
+
)
|
523
|
+
end
|
524
|
+
|
525
|
+
unless parameters.any? { |type, _| type == :keyrest }
|
526
|
+
parameters.map!(&:last)
|
527
|
+
parameters.sort!
|
528
|
+
@strict_local_keys = parameters.freeze
|
529
|
+
end
|
530
|
+
end
|
531
|
+
|
532
|
+
def offset
|
533
|
+
if Template.frozen_string_literal
|
534
|
+
-1
|
535
|
+
else
|
536
|
+
0
|
313
537
|
end
|
314
538
|
end
|
315
539
|
|
@@ -322,33 +546,22 @@ module ActionView
|
|
322
546
|
end
|
323
547
|
end
|
324
548
|
|
549
|
+
RUBY_RESERVED_KEYWORDS = ::ActiveSupport::Delegation::RUBY_RESERVED_KEYWORDS
|
550
|
+
private_constant :RUBY_RESERVED_KEYWORDS
|
551
|
+
|
325
552
|
def locals_code
|
553
|
+
return "" if strict_locals?
|
554
|
+
|
326
555
|
# Only locals with valid variable names get set directly. Others will
|
327
556
|
# still be available in local_assigns.
|
328
|
-
locals = @locals -
|
329
|
-
|
330
|
-
|
331
|
-
ActiveSupport::Deprecation.warn(<<~MSG)
|
332
|
-
Passing instance variables to `render` is deprecated.
|
333
|
-
In Rails 7.1, #{deprecated_locals.to_sentence} will be ignored.
|
334
|
-
MSG
|
335
|
-
locals = locals.grep(/\A@?(?![A-Z0-9])(?:[[:alnum:]_]|[^\0-\177])+\z/)
|
336
|
-
else
|
337
|
-
locals = locals.grep(/\A(?![A-Z0-9])(?:[[:alnum:]_]|[^\0-\177])+\z/)
|
338
|
-
end
|
557
|
+
locals = @locals - RUBY_RESERVED_KEYWORDS
|
558
|
+
|
559
|
+
locals = locals.grep(/\A(?![A-Z0-9])(?:[[:alnum:]_]|[^\0-\177])+\z/)
|
339
560
|
|
340
561
|
# Assign for the same variable is to suppress unused variable warning
|
341
562
|
locals.each_with_object(+"") { |key, code| code << "#{key} = local_assigns[:#{key}]; #{key} = #{key};" }
|
342
563
|
end
|
343
564
|
|
344
|
-
def method_name
|
345
|
-
@method_name ||= begin
|
346
|
-
m = +"_#{identifier_method_name}__#{@identifier.hash}_#{__id__}"
|
347
|
-
m.tr!("-", "_")
|
348
|
-
m
|
349
|
-
end
|
350
|
-
end
|
351
|
-
|
352
565
|
def identifier_method_name
|
353
566
|
short_identifier.tr("^a-z_", "_")
|
354
567
|
end
|