actionview 6.1.7.2 → 7.1.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +299 -277
- data/MIT-LICENSE +2 -1
- data/README.rdoc +3 -3
- 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 +37 -19
- data/lib/action_view/buffers.rb +107 -9
- data/lib/action_view/cache_expiry.rb +48 -37
- data/lib/action_view/context.rb +1 -1
- data/lib/action_view/dependency_tracker/erb_tracker.rb +154 -0
- data/lib/action_view/dependency_tracker/ripper_tracker.rb +59 -0
- data/lib/action_view/dependency_tracker.rb +6 -147
- data/lib/action_view/deprecator.rb +7 -0
- data/lib/action_view/digestor.rb +8 -5
- data/lib/action_view/flows.rb +4 -4
- data/lib/action_view/gem_version.rb +4 -4
- data/lib/action_view/helpers/active_model_helper.rb +3 -3
- data/lib/action_view/helpers/asset_tag_helper.rb +200 -60
- data/lib/action_view/helpers/asset_url_helper.rb +22 -21
- data/lib/action_view/helpers/atom_feed_helper.rb +8 -9
- data/lib/action_view/helpers/cache_helper.rb +55 -12
- data/lib/action_view/helpers/capture_helper.rb +34 -14
- data/lib/action_view/helpers/content_exfiltration_prevention_helper.rb +70 -0
- data/lib/action_view/helpers/controller_helper.rb +8 -2
- data/lib/action_view/helpers/csp_helper.rb +3 -3
- data/lib/action_view/helpers/csrf_helper.rb +4 -4
- data/lib/action_view/helpers/date_helper.rb +123 -57
- data/lib/action_view/helpers/debug_helper.rb +6 -4
- data/lib/action_view/helpers/form_helper.rb +253 -97
- data/lib/action_view/helpers/form_options_helper.rb +72 -34
- data/lib/action_view/helpers/form_tag_helper.rb +189 -58
- data/lib/action_view/helpers/javascript_helper.rb +4 -5
- data/lib/action_view/helpers/number_helper.rb +43 -335
- data/lib/action_view/helpers/output_safety_helper.rb +6 -6
- data/lib/action_view/helpers/rendering_helper.rb +6 -7
- data/lib/action_view/helpers/sanitize_helper.rb +54 -24
- data/lib/action_view/helpers/tag_helper.rb +42 -35
- data/lib/action_view/helpers/tags/base.rb +16 -77
- data/lib/action_view/helpers/tags/check_box.rb +1 -1
- 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 +4 -1
- 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/file_field.rb +16 -0
- 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 +4 -1
- data/lib/action_view/helpers/tags/select_renderer.rb +56 -0
- data/lib/action_view/helpers/tags/time_field.rb +11 -2
- 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 +31 -0
- data/lib/action_view/helpers/tags.rb +5 -2
- data/lib/action_view/helpers/text_helper.rb +180 -97
- data/lib/action_view/helpers/translation_helper.rb +14 -45
- data/lib/action_view/helpers/url_helper.rb +230 -132
- data/lib/action_view/helpers.rb +27 -25
- data/lib/action_view/layouts.rb +15 -10
- data/lib/action_view/log_subscriber.rb +49 -32
- data/lib/action_view/lookup_context.rb +58 -61
- data/lib/action_view/model_naming.rb +2 -2
- data/lib/action_view/path_registry.rb +57 -0
- data/lib/action_view/path_set.rb +28 -35
- data/lib/action_view/railtie.rb +44 -9
- data/lib/action_view/record_identifier.rb +16 -9
- data/lib/action_view/render_parser.rb +188 -0
- data/lib/action_view/renderer/abstract_renderer.rb +3 -3
- data/lib/action_view/renderer/collection_renderer.rb +10 -2
- data/lib/action_view/renderer/partial_renderer/collection_caching.rb +21 -3
- data/lib/action_view/renderer/partial_renderer.rb +3 -36
- data/lib/action_view/renderer/renderer.rb +6 -4
- data/lib/action_view/renderer/streaming_template_renderer.rb +6 -5
- data/lib/action_view/renderer/template_renderer.rb +9 -4
- data/lib/action_view/rendering.rb +25 -7
- data/lib/action_view/ripper_ast_parser.rb +198 -0
- data/lib/action_view/routing_url_for.rb +8 -5
- data/lib/action_view/template/error.rb +122 -14
- 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 +79 -1
- data/lib/action_view/template/handlers.rb +4 -4
- data/lib/action_view/template/html.rb +4 -4
- data/lib/action_view/template/inline.rb +3 -3
- data/lib/action_view/template/raw_file.rb +4 -4
- data/lib/action_view/template/renderable.rb +1 -1
- data/lib/action_view/template/resolver.rb +96 -313
- data/lib/action_view/template/text.rb +4 -4
- data/lib/action_view/template/types.rb +25 -32
- data/lib/action_view/template.rb +245 -41
- data/lib/action_view/template_details.rb +66 -0
- data/lib/action_view/template_path.rb +66 -0
- data/lib/action_view/test_case.rb +182 -23
- data/lib/action_view/testing/resolvers.rb +11 -12
- data/lib/action_view/unbound_template.rb +43 -7
- data/lib/action_view/version.rb +1 -1
- data/lib/action_view/view_paths.rb +19 -28
- data/lib/action_view.rb +6 -4
- data/lib/assets/compiled/rails-ujs.js +36 -5
- metadata +32 -25
@@ -1,9 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
module ActionView
|
4
|
-
|
5
|
-
|
6
|
-
class Text
|
3
|
+
module ActionView # :nodoc:
|
4
|
+
class Template # :nodoc:
|
5
|
+
# = Action View Text Template
|
6
|
+
class Text # :nodoc:
|
7
7
|
attr_accessor :type
|
8
8
|
|
9
9
|
def initialize(string)
|
@@ -3,12 +3,15 @@
|
|
3
3
|
require "active_support/core_ext/module/attribute_accessors"
|
4
4
|
|
5
5
|
module ActionView
|
6
|
-
class Template
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
6
|
+
class Template # :nodoc:
|
7
|
+
# SimpleType is mostly just a stub implementation for when Action View
|
8
|
+
# is used without Action Dispatch.
|
9
|
+
class SimpleType # :nodoc:
|
10
|
+
@symbols = [ :html, :text, :js, :css, :xml, :json ]
|
11
|
+
class << self
|
12
|
+
attr_reader :symbols
|
13
|
+
|
14
|
+
def [](type)
|
12
15
|
if type.is_a?(self)
|
13
16
|
type
|
14
17
|
else
|
@@ -16,42 +19,32 @@ module ActionView
|
|
16
19
|
end
|
17
20
|
end
|
18
21
|
|
19
|
-
|
20
|
-
|
21
|
-
def initialize(symbol)
|
22
|
-
@symbol = symbol.to_sym
|
23
|
-
end
|
24
|
-
|
25
|
-
def to_s
|
26
|
-
@symbol.to_s
|
27
|
-
end
|
28
|
-
alias to_str to_s
|
29
|
-
|
30
|
-
def ref
|
31
|
-
@symbol
|
32
|
-
end
|
33
|
-
alias to_sym ref
|
34
|
-
|
35
|
-
def ==(type)
|
36
|
-
@symbol == type.to_sym unless type.blank?
|
22
|
+
def valid_symbols?(symbols) # :nodoc
|
23
|
+
symbols.all? { |s| @symbols.include?(s) }
|
37
24
|
end
|
38
25
|
end
|
39
26
|
|
40
|
-
|
27
|
+
attr_reader :symbol
|
41
28
|
|
42
|
-
def
|
43
|
-
|
29
|
+
def initialize(symbol)
|
30
|
+
@symbol = symbol.to_sym
|
44
31
|
end
|
45
32
|
|
46
|
-
|
33
|
+
def to_s
|
34
|
+
@symbol.to_s
|
35
|
+
end
|
36
|
+
alias to_str to_s
|
47
37
|
|
48
|
-
def
|
49
|
-
|
38
|
+
def ref
|
39
|
+
@symbol
|
50
40
|
end
|
41
|
+
alias to_sym ref
|
51
42
|
|
52
|
-
def
|
53
|
-
|
43
|
+
def ==(type)
|
44
|
+
@symbol == type.to_sym unless type.blank?
|
54
45
|
end
|
55
46
|
end
|
47
|
+
|
48
|
+
Types = SimpleType # :nodoc:
|
56
49
|
end
|
57
50
|
end
|
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,58 @@ 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
|
+
#
|
102
151
|
|
103
152
|
eager_autoload do
|
104
153
|
autoload :Error
|
@@ -107,6 +156,7 @@ module ActionView
|
|
107
156
|
autoload :Handlers
|
108
157
|
autoload :HTML
|
109
158
|
autoload :Inline
|
159
|
+
autoload :Types
|
110
160
|
autoload :Sources
|
111
161
|
autoload :Text
|
112
162
|
autoload :Types
|
@@ -114,11 +164,27 @@ module ActionView
|
|
114
164
|
|
115
165
|
extend Template::Handlers
|
116
166
|
|
167
|
+
singleton_class.attr_accessor :frozen_string_literal
|
168
|
+
@frozen_string_literal = false
|
169
|
+
|
170
|
+
class << self # :nodoc:
|
171
|
+
def mime_types_implementation=(implementation)
|
172
|
+
# This method isn't thread-safe, but it's not supposed
|
173
|
+
# to be called after initialization
|
174
|
+
if self::Types != implementation
|
175
|
+
remove_const(:Types)
|
176
|
+
const_set(:Types, implementation)
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
117
181
|
attr_reader :identifier, :handler
|
118
|
-
attr_reader :variable, :format, :variant, :
|
182
|
+
attr_reader :variable, :format, :variant, :virtual_path
|
183
|
+
|
184
|
+
NONE = Object.new
|
119
185
|
|
120
186
|
def initialize(source, identifier, handler, locals:, format: nil, variant: nil, virtual_path: nil)
|
121
|
-
@source = source
|
187
|
+
@source = source.dup
|
122
188
|
@identifier = identifier
|
123
189
|
@handler = handler
|
124
190
|
@compiled = false
|
@@ -134,6 +200,37 @@ module ActionView
|
|
134
200
|
@format = format
|
135
201
|
@variant = variant
|
136
202
|
@compile_mutex = Mutex.new
|
203
|
+
@strict_locals = NONE
|
204
|
+
@strict_local_keys = nil
|
205
|
+
@type = nil
|
206
|
+
end
|
207
|
+
|
208
|
+
# The locals this template has been or will be compiled for, or nil if this
|
209
|
+
# is a strict locals template.
|
210
|
+
def locals
|
211
|
+
if strict_locals?
|
212
|
+
nil
|
213
|
+
else
|
214
|
+
@locals
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
def spot(location) # :nodoc:
|
219
|
+
ast = RubyVM::AbstractSyntaxTree.parse(compiled_source, keep_script_lines: true)
|
220
|
+
node_id = RubyVM::AbstractSyntaxTree.node_id_for_backtrace_location(location)
|
221
|
+
node = find_node_by_id(ast, node_id)
|
222
|
+
|
223
|
+
ErrorHighlight.spot(node)
|
224
|
+
end
|
225
|
+
|
226
|
+
# Translate an error location returned by ErrorHighlight to the correct
|
227
|
+
# source location inside the template.
|
228
|
+
def translate_location(backtrace_location, spot)
|
229
|
+
if handler.respond_to?(:translate_location)
|
230
|
+
handler.translate_location(spot, backtrace_location, encode!) || spot
|
231
|
+
else
|
232
|
+
spot
|
233
|
+
end
|
137
234
|
end
|
138
235
|
|
139
236
|
# Returns whether the underlying handler supports streaming. If so,
|
@@ -148,10 +245,21 @@ module ActionView
|
|
148
245
|
# This method is instrumented as "!render_template.action_view". Notice that
|
149
246
|
# we use a bang in this instrumentation because you don't want to
|
150
247
|
# consume this in production. This is only slow if it's being listened to.
|
151
|
-
def render(view, locals, buffer =
|
248
|
+
def render(view, locals, buffer = nil, implicit_locals: [], add_to_stack: true, &block)
|
152
249
|
instrument_render_template do
|
153
250
|
compile!(view)
|
154
|
-
|
251
|
+
|
252
|
+
if strict_locals? && @strict_local_keys && !implicit_locals.empty?
|
253
|
+
locals_to_ignore = implicit_locals - @strict_local_keys
|
254
|
+
locals.except!(*locals_to_ignore)
|
255
|
+
end
|
256
|
+
|
257
|
+
if buffer
|
258
|
+
view._run(method_name, self, locals, buffer, add_to_stack: add_to_stack, has_strict_locals: strict_locals?, &block)
|
259
|
+
nil
|
260
|
+
else
|
261
|
+
view._run(method_name, self, locals, OutputBuffer.new, add_to_stack: add_to_stack, has_strict_locals: strict_locals?, &block)&.to_s
|
262
|
+
end
|
155
263
|
end
|
156
264
|
rescue => e
|
157
265
|
handle_render_error(view, e)
|
@@ -166,20 +274,23 @@ module ActionView
|
|
166
274
|
end
|
167
275
|
|
168
276
|
def inspect
|
169
|
-
"#<#{self.class.name} #{short_identifier} locals=#{
|
277
|
+
"#<#{self.class.name} #{short_identifier} locals=#{locals.inspect}>"
|
170
278
|
end
|
171
279
|
|
172
280
|
def source
|
173
281
|
@source.to_s
|
174
282
|
end
|
175
283
|
|
284
|
+
LEADING_ENCODING_REGEXP = /\A#{ENCODING_FLAG}/
|
285
|
+
private_constant :LEADING_ENCODING_REGEXP
|
286
|
+
|
176
287
|
# This method is responsible for properly setting the encoding of the
|
177
288
|
# source. Until this point, we assume that the source is BINARY data.
|
178
289
|
# If no additional information is supplied, we assume the encoding is
|
179
290
|
# the same as <tt>Encoding.default_external</tt>.
|
180
291
|
#
|
181
292
|
# The user can also specify the encoding via a comment on the first
|
182
|
-
# line of the template (
|
293
|
+
# line of the template (<tt># encoding: NAME-OF-ENCODING</tt>). This will work
|
183
294
|
# with any template engine, as we process out the encoding comment
|
184
295
|
# before passing the source on to the template engine, leaving a
|
185
296
|
# blank line in its stead.
|
@@ -191,7 +302,7 @@ module ActionView
|
|
191
302
|
# Look for # encoding: *. If we find one, we'll encode the
|
192
303
|
# String in that encoding, otherwise, we'll use the
|
193
304
|
# default external encoding.
|
194
|
-
if source.sub!(
|
305
|
+
if source.sub!(LEADING_ENCODING_REGEXP, "")
|
195
306
|
encoding = magic_encoding = $1
|
196
307
|
else
|
197
308
|
encoding = Encoding.default_external
|
@@ -219,6 +330,32 @@ module ActionView
|
|
219
330
|
end
|
220
331
|
end
|
221
332
|
|
333
|
+
# This method is responsible for marking a template as having strict locals
|
334
|
+
# which means the template can only accept the locals defined in a magic
|
335
|
+
# comment. For example, if your template acceps the locals +title+ and
|
336
|
+
# +comment_count+, add the following to your template file:
|
337
|
+
#
|
338
|
+
# <%# locals: (title: "Default title", comment_count: 0) %>
|
339
|
+
#
|
340
|
+
# Strict locals are useful for validating template arguments and for
|
341
|
+
# specifying defaults.
|
342
|
+
def strict_locals!
|
343
|
+
if @strict_locals == NONE
|
344
|
+
self.source.sub!(STRICT_LOCALS_REGEX, "")
|
345
|
+
@strict_locals = $1
|
346
|
+
|
347
|
+
return if @strict_locals.nil? # Magic comment not found
|
348
|
+
|
349
|
+
@strict_locals = "**nil" if @strict_locals.blank?
|
350
|
+
end
|
351
|
+
|
352
|
+
@strict_locals
|
353
|
+
end
|
354
|
+
|
355
|
+
# Returns whether a template is using strict locals.
|
356
|
+
def strict_locals?
|
357
|
+
strict_locals!
|
358
|
+
end
|
222
359
|
|
223
360
|
# Exceptions are marshalled when using the parallel test runner with DRb, so we need
|
224
361
|
# to ensure that references to the template object can be marshalled as well. This means forgoing
|
@@ -232,7 +369,26 @@ module ActionView
|
|
232
369
|
@compile_mutex = Mutex.new
|
233
370
|
end
|
234
371
|
|
372
|
+
def method_name # :nodoc:
|
373
|
+
@method_name ||= begin
|
374
|
+
m = +"_#{identifier_method_name}__#{@identifier.hash}_#{__id__}"
|
375
|
+
m.tr!("-", "_")
|
376
|
+
m
|
377
|
+
end
|
378
|
+
end
|
379
|
+
|
235
380
|
private
|
381
|
+
def find_node_by_id(node, node_id)
|
382
|
+
return node if node.node_id == node_id
|
383
|
+
|
384
|
+
node.children.grep(node.class).each do |child|
|
385
|
+
found = find_node_by_id(child, node_id)
|
386
|
+
return found if found
|
387
|
+
end
|
388
|
+
|
389
|
+
false
|
390
|
+
end
|
391
|
+
|
236
392
|
# Compile a template. This method ensures a template is compiled
|
237
393
|
# just once and removes the source after it is compiled.
|
238
394
|
def compile!(view)
|
@@ -257,27 +413,25 @@ module ActionView
|
|
257
413
|
end
|
258
414
|
end
|
259
415
|
|
260
|
-
#
|
261
|
-
#
|
262
|
-
#
|
263
|
-
|
264
|
-
|
265
|
-
# the template engine to support additional mechanisms for
|
266
|
-
# specifying the encoding. For instance, ERB supports <%# encoding: %>
|
267
|
-
#
|
268
|
-
# Otherwise, after we figure out the correct encoding, we then
|
269
|
-
# encode the source into <tt>Encoding.default_internal</tt>.
|
270
|
-
# In general, this means that templates will be UTF-8 inside of Rails,
|
271
|
-
# regardless of the original source encoding.
|
272
|
-
def compile(mod)
|
416
|
+
# This method compiles the source of the template. The compilation of templates
|
417
|
+
# involves setting strict_locals! if applicable, encoding the template, and setting
|
418
|
+
# frozen string literal.
|
419
|
+
def compiled_source
|
420
|
+
set_strict_locals = strict_locals!
|
273
421
|
source = encode!
|
274
422
|
code = @handler.call(self, source)
|
275
423
|
|
424
|
+
method_arguments =
|
425
|
+
if set_strict_locals
|
426
|
+
"output_buffer, #{set_strict_locals}"
|
427
|
+
else
|
428
|
+
"local_assigns, output_buffer"
|
429
|
+
end
|
430
|
+
|
276
431
|
# Make sure that the resulting String to be eval'd is in the
|
277
432
|
# encoding of the code
|
278
|
-
original_source = source
|
279
433
|
source = +<<-end_src
|
280
|
-
def #{method_name}(
|
434
|
+
def #{method_name}(#{method_arguments})
|
281
435
|
@virtual_path = #{@virtual_path.inspect};#{locals_code};#{code}
|
282
436
|
end
|
283
437
|
end_src
|
@@ -296,13 +450,68 @@ module ActionView
|
|
296
450
|
raise WrongEncodingError.new(source, Encoding.default_internal)
|
297
451
|
end
|
298
452
|
|
453
|
+
if Template.frozen_string_literal
|
454
|
+
"# frozen_string_literal: true\n#{source}"
|
455
|
+
else
|
456
|
+
source
|
457
|
+
end
|
458
|
+
end
|
459
|
+
|
460
|
+
# Among other things, this method is responsible for properly setting
|
461
|
+
# the encoding of the compiled template.
|
462
|
+
#
|
463
|
+
# If the template engine handles encodings, we send the encoded
|
464
|
+
# String to the engine without further processing. This allows
|
465
|
+
# the template engine to support additional mechanisms for
|
466
|
+
# specifying the encoding. For instance, ERB supports <%# encoding: %>
|
467
|
+
#
|
468
|
+
# Otherwise, after we figure out the correct encoding, we then
|
469
|
+
# encode the source into <tt>Encoding.default_internal</tt>.
|
470
|
+
# In general, this means that templates will be UTF-8 inside of Rails,
|
471
|
+
# regardless of the original source encoding.
|
472
|
+
def compile(mod)
|
299
473
|
begin
|
300
|
-
mod.module_eval(
|
474
|
+
mod.module_eval(compiled_source, identifier, offset)
|
301
475
|
rescue SyntaxError
|
302
476
|
# Account for when code in the template is not syntactically valid; e.g. if we're using
|
303
477
|
# ERB and the user writes <%= foo( %>, attempting to call a helper `foo` and interpolate
|
304
478
|
# the result into the template, but missing an end parenthesis.
|
305
|
-
raise SyntaxErrorInTemplate.new(self,
|
479
|
+
raise SyntaxErrorInTemplate.new(self, encode!)
|
480
|
+
end
|
481
|
+
|
482
|
+
return unless strict_locals?
|
483
|
+
|
484
|
+
parameters = mod.instance_method(method_name).parameters - [[:req, :output_buffer]]
|
485
|
+
# Check compiled method parameters to ensure that only kwargs
|
486
|
+
# were provided as strict locals, preventing `locals: (foo, *foo)` etc
|
487
|
+
# and allowing `locals: (foo:)`.
|
488
|
+
|
489
|
+
non_kwarg_parameters = parameters.select do |parameter|
|
490
|
+
![:keyreq, :key, :keyrest, :nokey].include?(parameter[0])
|
491
|
+
end
|
492
|
+
|
493
|
+
unless non_kwarg_parameters.empty?
|
494
|
+
mod.undef_method(method_name)
|
495
|
+
|
496
|
+
raise ArgumentError.new(
|
497
|
+
"#{non_kwarg_parameters.map { |_, name| "`#{name}`" }.to_sentence} set as non-keyword " \
|
498
|
+
"#{'argument'.pluralize(non_kwarg_parameters.length)} for #{short_identifier}. " \
|
499
|
+
"Locals can only be set as keyword arguments."
|
500
|
+
)
|
501
|
+
end
|
502
|
+
|
503
|
+
unless parameters.any? { |type, _| type == :keyrest }
|
504
|
+
parameters.map!(&:last)
|
505
|
+
parameters.sort!
|
506
|
+
@strict_local_keys = parameters.freeze
|
507
|
+
end
|
508
|
+
end
|
509
|
+
|
510
|
+
def offset
|
511
|
+
if Template.frozen_string_literal
|
512
|
+
-1
|
513
|
+
else
|
514
|
+
0
|
306
515
|
end
|
307
516
|
end
|
308
517
|
|
@@ -316,23 +525,18 @@ module ActionView
|
|
316
525
|
end
|
317
526
|
|
318
527
|
def locals_code
|
528
|
+
return "" if strict_locals?
|
529
|
+
|
319
530
|
# Only locals with valid variable names get set directly. Others will
|
320
531
|
# still be available in local_assigns.
|
321
532
|
locals = @locals - Module::RUBY_RESERVED_KEYWORDS
|
322
|
-
|
533
|
+
|
534
|
+
locals = locals.grep(/\A(?![A-Z0-9])(?:[[:alnum:]_]|[^\0-\177])+\z/)
|
323
535
|
|
324
536
|
# Assign for the same variable is to suppress unused variable warning
|
325
537
|
locals.each_with_object(+"") { |key, code| code << "#{key} = local_assigns[:#{key}]; #{key} = #{key};" }
|
326
538
|
end
|
327
539
|
|
328
|
-
def method_name
|
329
|
-
@method_name ||= begin
|
330
|
-
m = +"_#{identifier_method_name}__#{@identifier.hash}_#{__id__}"
|
331
|
-
m.tr!("-", "_")
|
332
|
-
m
|
333
|
-
end
|
334
|
-
end
|
335
|
-
|
336
540
|
def identifier_method_name
|
337
541
|
short_identifier.tr("^a-z_", "_")
|
338
542
|
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActionView
|
4
|
+
class TemplateDetails # :nodoc:
|
5
|
+
class Requested
|
6
|
+
attr_reader :locale, :handlers, :formats, :variants
|
7
|
+
attr_reader :locale_idx, :handlers_idx, :formats_idx, :variants_idx
|
8
|
+
|
9
|
+
ANY_HASH = Hash.new(1).merge(nil => 0).freeze
|
10
|
+
|
11
|
+
def initialize(locale:, handlers:, formats:, variants:)
|
12
|
+
@locale = locale
|
13
|
+
@handlers = handlers
|
14
|
+
@formats = formats
|
15
|
+
@variants = variants
|
16
|
+
|
17
|
+
@locale_idx = build_idx_hash(locale)
|
18
|
+
@handlers_idx = build_idx_hash(handlers)
|
19
|
+
@formats_idx = build_idx_hash(formats)
|
20
|
+
if variants == :any
|
21
|
+
@variants_idx = ANY_HASH
|
22
|
+
else
|
23
|
+
@variants_idx = build_idx_hash(variants)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
def build_idx_hash(arr)
|
29
|
+
[*arr, nil].each_with_index.to_h.freeze
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
attr_reader :locale, :handler, :format, :variant
|
34
|
+
|
35
|
+
def initialize(locale, handler, format, variant)
|
36
|
+
@locale = locale
|
37
|
+
@handler = handler
|
38
|
+
@format = format
|
39
|
+
@variant = variant
|
40
|
+
end
|
41
|
+
|
42
|
+
def matches?(requested)
|
43
|
+
requested.formats_idx[@format] &&
|
44
|
+
requested.locale_idx[@locale] &&
|
45
|
+
requested.variants_idx[@variant] &&
|
46
|
+
requested.handlers_idx[@handler]
|
47
|
+
end
|
48
|
+
|
49
|
+
def sort_key_for(requested)
|
50
|
+
[
|
51
|
+
requested.formats_idx[@format],
|
52
|
+
requested.locale_idx[@locale],
|
53
|
+
requested.variants_idx[@variant],
|
54
|
+
requested.handlers_idx[@handler]
|
55
|
+
]
|
56
|
+
end
|
57
|
+
|
58
|
+
def handler_class
|
59
|
+
Template.handler_for_extension(handler)
|
60
|
+
end
|
61
|
+
|
62
|
+
def format_or_default
|
63
|
+
format || handler_class.try(:default_format)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActionView
|
4
|
+
# = Action View \TemplatePath
|
5
|
+
#
|
6
|
+
# Represents a template path within ActionView's lookup and rendering system,
|
7
|
+
# like "users/show"
|
8
|
+
#
|
9
|
+
# TemplatePath makes it convenient to convert between separate name, prefix,
|
10
|
+
# partial arguments and the virtual path.
|
11
|
+
class TemplatePath
|
12
|
+
attr_reader :name, :prefix, :partial, :virtual
|
13
|
+
alias_method :partial?, :partial
|
14
|
+
alias_method :virtual_path, :virtual
|
15
|
+
|
16
|
+
# Convert name, prefix, and partial into a virtual path string
|
17
|
+
def self.virtual(name, prefix, partial)
|
18
|
+
if prefix.empty?
|
19
|
+
"#{partial ? "_" : ""}#{name}"
|
20
|
+
elsif partial
|
21
|
+
"#{prefix}/_#{name}"
|
22
|
+
else
|
23
|
+
"#{prefix}/#{name}"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# Build a TemplatePath form a virtual path
|
28
|
+
def self.parse(virtual)
|
29
|
+
if nameidx = virtual.rindex("/")
|
30
|
+
prefix = virtual[0, nameidx]
|
31
|
+
name = virtual.from(nameidx + 1)
|
32
|
+
prefix = prefix[1..] if prefix.start_with?("/")
|
33
|
+
else
|
34
|
+
prefix = ""
|
35
|
+
name = virtual
|
36
|
+
end
|
37
|
+
partial = name.start_with?("_")
|
38
|
+
name = name[1..] if partial
|
39
|
+
new name, prefix, partial, virtual
|
40
|
+
end
|
41
|
+
|
42
|
+
# Convert name, prefix, and partial into a TemplatePath
|
43
|
+
def self.build(name, prefix, partial)
|
44
|
+
new name, prefix, partial, virtual(name, prefix, partial)
|
45
|
+
end
|
46
|
+
|
47
|
+
def initialize(name, prefix, partial, virtual)
|
48
|
+
@name = name
|
49
|
+
@prefix = prefix
|
50
|
+
@partial = partial
|
51
|
+
@virtual = virtual
|
52
|
+
end
|
53
|
+
|
54
|
+
alias :to_str :virtual
|
55
|
+
alias :to_s :virtual
|
56
|
+
|
57
|
+
def hash # :nodoc:
|
58
|
+
@virtual.hash
|
59
|
+
end
|
60
|
+
|
61
|
+
def eql?(other) # :nodoc:
|
62
|
+
@virtual == other.virtual
|
63
|
+
end
|
64
|
+
alias :== :eql? # :nodoc:
|
65
|
+
end
|
66
|
+
end
|