actionview 6.1.7.2 → 7.1.3
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 +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
|