actionview 6.1.7.10 → 7.0.0.alpha1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of actionview might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +89 -409
- data/MIT-LICENSE +2 -1
- data/lib/action_view/base.rb +3 -3
- data/lib/action_view/buffers.rb +2 -2
- data/lib/action_view/cache_expiry.rb +46 -32
- 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/digestor.rb +7 -4
- 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 +1 -1
- data/lib/action_view/helpers/asset_tag_helper.rb +84 -29
- data/lib/action_view/helpers/asset_url_helper.rb +7 -7
- data/lib/action_view/helpers/atom_feed_helper.rb +3 -4
- data/lib/action_view/helpers/cache_helper.rb +51 -3
- data/lib/action_view/helpers/capture_helper.rb +2 -2
- data/lib/action_view/helpers/controller_helper.rb +2 -2
- data/lib/action_view/helpers/csp_helper.rb +1 -1
- data/lib/action_view/helpers/csrf_helper.rb +1 -1
- data/lib/action_view/helpers/date_helper.rb +6 -7
- data/lib/action_view/helpers/debug_helper.rb +3 -1
- data/lib/action_view/helpers/form_helper.rb +72 -12
- data/lib/action_view/helpers/form_options_helper.rb +65 -33
- data/lib/action_view/helpers/form_tag_helper.rb +75 -32
- data/lib/action_view/helpers/javascript_helper.rb +3 -5
- data/lib/action_view/helpers/number_helper.rb +3 -4
- data/lib/action_view/helpers/output_safety_helper.rb +2 -2
- data/lib/action_view/helpers/rendering_helper.rb +1 -1
- data/lib/action_view/helpers/sanitize_helper.rb +2 -2
- data/lib/action_view/helpers/tag_helper.rb +25 -44
- data/lib/action_view/helpers/tags/base.rb +3 -15
- data/lib/action_view/helpers/tags/check_box.rb +2 -2
- data/lib/action_view/helpers/tags/collection_select.rb +1 -1
- data/lib/action_view/helpers/tags/hidden_field.rb +0 -4
- data/lib/action_view/helpers/tags/time_field.rb +10 -1
- data/lib/action_view/helpers/tags/weekday_select.rb +27 -0
- data/lib/action_view/helpers/tags.rb +3 -2
- data/lib/action_view/helpers/text_helper.rb +24 -13
- data/lib/action_view/helpers/translation_helper.rb +1 -2
- data/lib/action_view/helpers/url_helper.rb +102 -77
- data/lib/action_view/helpers.rb +25 -25
- data/lib/action_view/lookup_context.rb +33 -52
- data/lib/action_view/model_naming.rb +1 -1
- data/lib/action_view/path_set.rb +16 -22
- data/lib/action_view/railtie.rb +14 -1
- data/lib/action_view/render_parser.rb +188 -0
- data/lib/action_view/renderer/abstract_renderer.rb +2 -2
- data/lib/action_view/renderer/partial_renderer.rb +0 -34
- data/lib/action_view/renderer/renderer.rb +4 -4
- data/lib/action_view/renderer/streaming_template_renderer.rb +3 -3
- data/lib/action_view/renderer/template_renderer.rb +6 -2
- data/lib/action_view/rendering.rb +2 -2
- data/lib/action_view/ripper_ast_parser.rb +198 -0
- data/lib/action_view/routing_url_for.rb +1 -1
- data/lib/action_view/template/error.rb +108 -13
- data/lib/action_view/template/handlers/erb.rb +6 -0
- data/lib/action_view/template/handlers.rb +3 -3
- data/lib/action_view/template/html.rb +3 -3
- data/lib/action_view/template/inline.rb +3 -3
- data/lib/action_view/template/raw_file.rb +3 -3
- data/lib/action_view/template/resolver.rb +84 -311
- data/lib/action_view/template/text.rb +3 -3
- data/lib/action_view/template/types.rb +14 -12
- data/lib/action_view/template.rb +10 -1
- data/lib/action_view/template_details.rb +66 -0
- data/lib/action_view/template_path.rb +64 -0
- data/lib/action_view/test_case.rb +6 -2
- data/lib/action_view/testing/resolvers.rb +11 -12
- data/lib/action_view/unbound_template.rb +33 -7
- data/lib/action_view.rb +3 -4
- data/lib/assets/compiled/rails-ujs.js +5 -36
- metadata +22 -16
@@ -10,32 +10,12 @@ require "concurrent/map"
|
|
10
10
|
module ActionView
|
11
11
|
# = Action View Resolver
|
12
12
|
class Resolver
|
13
|
-
|
14
|
-
|
15
|
-
attr_reader :name, :prefix, :partial, :virtual
|
16
|
-
alias_method :partial?, :partial
|
17
|
-
|
18
|
-
def self.build(name, prefix, partial)
|
19
|
-
virtual = +""
|
20
|
-
virtual << "#{prefix}/" unless prefix.empty?
|
21
|
-
virtual << (partial ? "_#{name}" : name)
|
22
|
-
new name, prefix, partial, virtual
|
23
|
-
end
|
24
|
-
|
25
|
-
def initialize(name, prefix, partial, virtual)
|
26
|
-
@name = name
|
27
|
-
@prefix = prefix
|
28
|
-
@partial = partial
|
29
|
-
@virtual = virtual
|
30
|
-
end
|
31
|
-
|
32
|
-
def to_str
|
33
|
-
@virtual
|
34
|
-
end
|
35
|
-
alias :to_s :to_str
|
36
|
-
end
|
13
|
+
Path = ActionView::TemplatePath
|
14
|
+
deprecate_constant :Path
|
37
15
|
|
38
16
|
class PathParser # :nodoc:
|
17
|
+
ParsedPath = Struct.new(:path, :details)
|
18
|
+
|
39
19
|
def build_path_regex
|
40
20
|
handlers = Template::Handlers.extensions.map { |x| Regexp.escape(x) }.join("|")
|
41
21
|
formats = Template::Types.symbols.map { |x| Regexp.escape(x) }.join("|")
|
@@ -58,79 +38,15 @@ module ActionView
|
|
58
38
|
def parse(path)
|
59
39
|
@regex ||= build_path_regex
|
60
40
|
match = @regex.match(path)
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
}
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
# Threadsafe template cache
|
74
|
-
class Cache #:nodoc:
|
75
|
-
class SmallCache < Concurrent::Map
|
76
|
-
def initialize(options = {})
|
77
|
-
super(options.merge(initial_capacity: 2))
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
# Preallocate all the default blocks for performance/memory consumption reasons
|
82
|
-
PARTIAL_BLOCK = lambda { |cache, partial| cache[partial] = SmallCache.new }
|
83
|
-
PREFIX_BLOCK = lambda { |cache, prefix| cache[prefix] = SmallCache.new(&PARTIAL_BLOCK) }
|
84
|
-
NAME_BLOCK = lambda { |cache, name| cache[name] = SmallCache.new(&PREFIX_BLOCK) }
|
85
|
-
KEY_BLOCK = lambda { |cache, key| cache[key] = SmallCache.new(&NAME_BLOCK) }
|
86
|
-
|
87
|
-
# Usually a majority of template look ups return nothing, use this canonical preallocated array to save memory
|
88
|
-
NO_TEMPLATES = [].freeze
|
89
|
-
|
90
|
-
def initialize
|
91
|
-
@data = SmallCache.new(&KEY_BLOCK)
|
92
|
-
@query_cache = SmallCache.new
|
93
|
-
end
|
94
|
-
|
95
|
-
def inspect
|
96
|
-
"#{to_s[0..-2]} keys=#{@data.size} queries=#{@query_cache.size}>"
|
97
|
-
end
|
98
|
-
|
99
|
-
# Cache the templates returned by the block
|
100
|
-
def cache(key, name, prefix, partial, locals)
|
101
|
-
@data[key][name][prefix][partial][locals] ||= canonical_no_templates(yield)
|
102
|
-
end
|
103
|
-
|
104
|
-
def cache_query(query) # :nodoc:
|
105
|
-
@query_cache[query] ||= canonical_no_templates(yield)
|
106
|
-
end
|
107
|
-
|
108
|
-
def clear
|
109
|
-
@data.clear
|
110
|
-
@query_cache.clear
|
111
|
-
end
|
112
|
-
|
113
|
-
# Get the cache size. Do not call this
|
114
|
-
# method. This method is not guaranteed to be here ever.
|
115
|
-
def size # :nodoc:
|
116
|
-
size = 0
|
117
|
-
@data.each_value do |v1|
|
118
|
-
v1.each_value do |v2|
|
119
|
-
v2.each_value do |v3|
|
120
|
-
v3.each_value do |v4|
|
121
|
-
size += v4.size
|
122
|
-
end
|
123
|
-
end
|
124
|
-
end
|
125
|
-
end
|
126
|
-
|
127
|
-
size + @query_cache.size
|
41
|
+
path = TemplatePath.build(match[:action], match[:prefix] || "", !!match[:partial])
|
42
|
+
details = TemplateDetails.new(
|
43
|
+
match[:locale]&.to_sym,
|
44
|
+
match[:handler]&.to_sym,
|
45
|
+
match[:format]&.to_sym,
|
46
|
+
match[:variant]&.to_sym
|
47
|
+
)
|
48
|
+
ParsedPath.new(path, details)
|
128
49
|
end
|
129
|
-
|
130
|
-
private
|
131
|
-
def canonical_no_templates(templates)
|
132
|
-
templates.empty? ? NO_TEMPLATES : templates
|
133
|
-
end
|
134
50
|
end
|
135
51
|
|
136
52
|
cattr_accessor :caching, default: true
|
@@ -139,25 +55,17 @@ module ActionView
|
|
139
55
|
alias :caching? :caching
|
140
56
|
end
|
141
57
|
|
142
|
-
def initialize
|
143
|
-
@cache = Cache.new
|
144
|
-
end
|
145
|
-
|
146
58
|
def clear_cache
|
147
|
-
@cache.clear
|
148
59
|
end
|
149
60
|
|
150
61
|
# Normalizes the arguments and passes it on to find_templates.
|
151
62
|
def find_all(name, prefix = nil, partial = false, details = {}, key = nil, locals = [])
|
152
|
-
|
153
|
-
|
154
|
-
cached(key, [name, prefix, partial], details, locals) do
|
155
|
-
_find_all(name, prefix, partial, details, key, locals)
|
156
|
-
end
|
63
|
+
_find_all(name, prefix, partial, details, key, locals)
|
157
64
|
end
|
158
65
|
|
159
|
-
def
|
160
|
-
|
66
|
+
def all_template_paths # :nodoc:
|
67
|
+
# Not implemented by default
|
68
|
+
[]
|
161
69
|
end
|
162
70
|
|
163
71
|
private
|
@@ -173,34 +81,18 @@ module ActionView
|
|
173
81
|
def find_templates(name, prefix, partial, details, locals = [])
|
174
82
|
raise NotImplementedError, "Subclasses must implement a find_templates(name, prefix, partial, details, locals = []) method"
|
175
83
|
end
|
176
|
-
|
177
|
-
# Handles templates caching. If a key is given and caching is on
|
178
|
-
# always check the cache before hitting the resolver. Otherwise,
|
179
|
-
# it always hits the resolver but if the key is present, check if the
|
180
|
-
# resolver is fresher before returning it.
|
181
|
-
def cached(key, path_info, details, locals)
|
182
|
-
name, prefix, partial = path_info
|
183
|
-
|
184
|
-
if key
|
185
|
-
@cache.cache(key, name, prefix, partial, locals) do
|
186
|
-
yield
|
187
|
-
end
|
188
|
-
else
|
189
|
-
yield
|
190
|
-
end
|
191
|
-
end
|
192
84
|
end
|
193
85
|
|
194
|
-
#
|
195
|
-
class
|
196
|
-
|
197
|
-
DEFAULT_PATTERN = ":prefix/:action{.:locale,}{.:formats,}{+:variants,}{.:handlers,}"
|
86
|
+
# A resolver that loads files from the filesystem.
|
87
|
+
class FileSystemResolver < Resolver
|
88
|
+
attr_reader :path
|
198
89
|
|
199
|
-
def initialize
|
200
|
-
|
90
|
+
def initialize(path)
|
91
|
+
raise ArgumentError, "path already is a Resolver class" if path.is_a?(Resolver)
|
201
92
|
@unbound_templates = Concurrent::Map.new
|
202
93
|
@path_parser = PathParser.new
|
203
|
-
|
94
|
+
@path = File.expand_path(path)
|
95
|
+
super()
|
204
96
|
end
|
205
97
|
|
206
98
|
def clear_cache
|
@@ -209,26 +101,37 @@ module ActionView
|
|
209
101
|
super
|
210
102
|
end
|
211
103
|
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
104
|
+
def to_s
|
105
|
+
@path.to_s
|
106
|
+
end
|
107
|
+
alias :to_path :to_s
|
108
|
+
|
109
|
+
def eql?(resolver)
|
110
|
+
self.class.equal?(resolver.class) && to_path == resolver.to_path
|
111
|
+
end
|
112
|
+
alias :== :eql?
|
113
|
+
|
114
|
+
def all_template_paths # :nodoc:
|
115
|
+
paths = template_glob("**/*")
|
116
|
+
paths.map do |filename|
|
117
|
+
filename.from(@path.size + 1).remove(/\.[^\/]*\z/)
|
118
|
+
end.uniq.map do |filename|
|
119
|
+
TemplatePath.parse(filename)
|
216
120
|
end
|
121
|
+
end
|
217
122
|
|
218
|
-
|
219
|
-
|
220
|
-
|
123
|
+
private
|
124
|
+
def _find_all(name, prefix, partial, details, key, locals)
|
125
|
+
requested_details = key || TemplateDetails::Requested.new(**details)
|
126
|
+
cache = key ? @unbound_templates : Concurrent::Map.new
|
221
127
|
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
end
|
228
|
-
else
|
229
|
-
build_unbound_template(template, path.virtual)
|
230
|
-
end
|
128
|
+
unbound_templates =
|
129
|
+
cache.compute_if_absent(TemplatePath.virtual(name, prefix, partial)) do
|
130
|
+
path = TemplatePath.build(name, prefix, partial)
|
131
|
+
unbound_templates_from_path(path)
|
132
|
+
end
|
231
133
|
|
134
|
+
filter_and_sort_by_details(unbound_templates, requested_details).map do |unbound_template|
|
232
135
|
unbound_template.bind_locals(locals)
|
233
136
|
end
|
234
137
|
end
|
@@ -237,196 +140,66 @@ module ActionView
|
|
237
140
|
Template::Sources::File.new(template)
|
238
141
|
end
|
239
142
|
|
240
|
-
def build_unbound_template(template
|
241
|
-
|
143
|
+
def build_unbound_template(template)
|
144
|
+
parsed = @path_parser.parse(template.from(@path.size + 1))
|
145
|
+
details = parsed.details
|
242
146
|
source = source_for_template(template)
|
243
147
|
|
244
148
|
UnboundTemplate.new(
|
245
149
|
source,
|
246
150
|
template,
|
247
|
-
|
248
|
-
virtual_path:
|
249
|
-
format: format,
|
250
|
-
variant: variant,
|
151
|
+
details: details,
|
152
|
+
virtual_path: parsed.path.virtual,
|
251
153
|
)
|
252
154
|
end
|
253
155
|
|
254
|
-
def
|
255
|
-
files.reject { |filename| !inside_path?(@path, filename) }
|
256
|
-
end
|
257
|
-
|
258
|
-
def find_template_paths_from_details(path, details)
|
156
|
+
def unbound_templates_from_path(path)
|
259
157
|
if path.name.include?(".")
|
260
|
-
|
261
|
-
end
|
262
|
-
|
263
|
-
query = build_query(path, details)
|
264
|
-
find_template_paths(query)
|
265
|
-
end
|
266
|
-
|
267
|
-
def find_template_paths(query)
|
268
|
-
Dir[query].uniq.reject do |filename|
|
269
|
-
File.directory?(filename) ||
|
270
|
-
# deals with case-insensitive file systems.
|
271
|
-
!File.fnmatch(query, filename, File::FNM_EXTGLOB)
|
272
|
-
end
|
273
|
-
end
|
274
|
-
|
275
|
-
def inside_path?(path, filename)
|
276
|
-
filename = File.expand_path(filename)
|
277
|
-
path = File.join(path, "")
|
278
|
-
filename.start_with?(path)
|
279
|
-
end
|
280
|
-
|
281
|
-
# Helper for building query glob string based on resolver's pattern.
|
282
|
-
def build_query(path, details)
|
283
|
-
query = @pattern.dup
|
284
|
-
|
285
|
-
prefix = path.prefix.empty? ? "" : "#{escape_entry(path.prefix)}\\1"
|
286
|
-
query.gsub!(/:prefix(\/)?/, prefix)
|
287
|
-
|
288
|
-
partial = escape_entry(path.partial? ? "_#{path.name}" : path.name)
|
289
|
-
query.gsub!(":action", partial)
|
290
|
-
|
291
|
-
details.each do |ext, candidates|
|
292
|
-
if ext == :variants && candidates == :any
|
293
|
-
query.gsub!(/:#{ext}/, "*")
|
294
|
-
else
|
295
|
-
query.gsub!(/:#{ext}/, "{#{candidates.compact.uniq.join(',')}}")
|
296
|
-
end
|
158
|
+
return []
|
297
159
|
end
|
298
160
|
|
299
|
-
File.expand_path(query, @path)
|
300
|
-
end
|
301
|
-
|
302
|
-
def escape_entry(entry)
|
303
|
-
entry.gsub(/[*?{}\[\]]/, '\\\\\\&')
|
304
|
-
end
|
305
|
-
|
306
|
-
# Extract handler, formats and variant from path. If a format cannot be found neither
|
307
|
-
# from the path, or the handler, we should return the array of formats given
|
308
|
-
# to the resolver.
|
309
|
-
def extract_handler_and_format_and_variant(path)
|
310
|
-
details = @path_parser.parse(path)
|
311
|
-
|
312
|
-
handler = Template.handler_for_extension(details[:handler])
|
313
|
-
format = details[:format] || handler.try(:default_format)
|
314
|
-
variant = details[:variant]
|
315
|
-
|
316
|
-
# Template::Types[format] and handler.default_format can return nil
|
317
|
-
[handler, format, variant]
|
318
|
-
end
|
319
|
-
end
|
320
|
-
|
321
|
-
# A resolver that loads files from the filesystem.
|
322
|
-
class FileSystemResolver < PathResolver
|
323
|
-
attr_reader :path
|
324
|
-
|
325
|
-
def initialize(path)
|
326
|
-
raise ArgumentError, "path already is a Resolver class" if path.is_a?(Resolver)
|
327
|
-
super()
|
328
|
-
@path = File.expand_path(path)
|
329
|
-
end
|
330
|
-
|
331
|
-
def to_s
|
332
|
-
@path.to_s
|
333
|
-
end
|
334
|
-
alias :to_path :to_s
|
335
|
-
|
336
|
-
def eql?(resolver)
|
337
|
-
self.class.equal?(resolver.class) && to_path == resolver.to_path
|
338
|
-
end
|
339
|
-
alias :== :eql?
|
340
|
-
end
|
341
|
-
|
342
|
-
# An Optimized resolver for Rails' most common case.
|
343
|
-
class OptimizedFileSystemResolver < FileSystemResolver #:nodoc:
|
344
|
-
def initialize(path)
|
345
|
-
super(path)
|
346
|
-
end
|
347
|
-
|
348
|
-
private
|
349
|
-
def find_candidate_template_paths(path)
|
350
161
|
# Instead of checking for every possible path, as our other globs would
|
351
162
|
# do, scan the directory for files with the right prefix.
|
352
|
-
|
163
|
+
paths = template_glob("#{escape_entry(path.to_s)}*")
|
353
164
|
|
354
|
-
|
355
|
-
|
165
|
+
paths.map do |path|
|
166
|
+
build_unbound_template(path)
|
167
|
+
end.select do |template|
|
168
|
+
# Select for exact virtual path match, including case sensitivity
|
169
|
+
template.virtual_path == path.virtual
|
356
170
|
end
|
357
171
|
end
|
358
172
|
|
359
|
-
def
|
360
|
-
|
361
|
-
|
362
|
-
return super
|
173
|
+
def filter_and_sort_by_details(templates, requested_details)
|
174
|
+
filtered_templates = templates.select do |template|
|
175
|
+
template.details.matches?(requested_details)
|
363
176
|
end
|
364
177
|
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
candidates.uniq.reject do |filename|
|
370
|
-
# This regex match does double duty of finding only files which match
|
371
|
-
# details (instead of just matching the prefix) and also filtering for
|
372
|
-
# case-insensitive file systems.
|
373
|
-
!regex.match?(filename) ||
|
374
|
-
File.directory?(filename)
|
375
|
-
end.sort_by do |filename|
|
376
|
-
# Because we scanned the directory, instead of checking for files
|
377
|
-
# one-by-one, they will be returned in an arbitrary order.
|
378
|
-
# We can use the matches found by the regex and sort by their index in
|
379
|
-
# details.
|
380
|
-
match = filename.match(regex)
|
381
|
-
EXTENSIONS.keys.map do |ext|
|
382
|
-
if ext == :variants && details[ext] == :any
|
383
|
-
match[ext].nil? ? 0 : 1
|
384
|
-
elsif match[ext].nil?
|
385
|
-
# No match should be last
|
386
|
-
details[ext].length
|
387
|
-
else
|
388
|
-
found = match[ext].to_sym
|
389
|
-
details[ext].index(found)
|
390
|
-
end
|
178
|
+
if filtered_templates.count > 1
|
179
|
+
filtered_templates.sort_by! do |template|
|
180
|
+
template.details.sort_key_for(requested_details)
|
391
181
|
end
|
392
182
|
end
|
393
|
-
end
|
394
183
|
|
395
|
-
|
396
|
-
query = Regexp.escape(File.join(@path, path))
|
397
|
-
exts = EXTENSIONS.map do |ext, prefix|
|
398
|
-
match =
|
399
|
-
if ext == :variants && details[ext] == :any
|
400
|
-
".*?"
|
401
|
-
else
|
402
|
-
arr = details[ext].compact
|
403
|
-
arr.uniq!
|
404
|
-
arr.map! { |e| Regexp.escape(e) }
|
405
|
-
arr.join("|")
|
406
|
-
end
|
407
|
-
prefix = Regexp.escape(prefix)
|
408
|
-
"(#{prefix}(?<#{ext}>#{match}))?"
|
409
|
-
end.join
|
410
|
-
|
411
|
-
%r{\A#{query}#{exts}\z}
|
184
|
+
filtered_templates
|
412
185
|
end
|
413
|
-
end
|
414
186
|
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
187
|
+
# Safe glob within @path
|
188
|
+
def template_glob(glob)
|
189
|
+
query = File.join(escape_entry(@path), glob)
|
190
|
+
path_with_slash = File.join(@path, "")
|
419
191
|
|
420
|
-
|
421
|
-
|
422
|
-
|
192
|
+
Dir.glob(query).filter_map do |filename|
|
193
|
+
filename = File.expand_path(filename)
|
194
|
+
next if File.directory?(filename)
|
195
|
+
next unless filename.start_with?(path_with_slash)
|
423
196
|
|
424
|
-
|
425
|
-
|
426
|
-
|
197
|
+
filename
|
198
|
+
end
|
199
|
+
end
|
427
200
|
|
428
|
-
|
429
|
-
|
430
|
-
|
201
|
+
def escape_entry(entry)
|
202
|
+
entry.gsub(/[*?{}\[\]]/, '\\\\\\&')
|
203
|
+
end
|
431
204
|
end
|
432
205
|
end
|
@@ -1,9 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
module ActionView
|
3
|
+
module ActionView # :nodoc:
|
4
4
|
# = Action View Text Template
|
5
|
-
class Template
|
6
|
-
class Text
|
5
|
+
class Template # :nodoc:
|
6
|
+
class Text # :nodoc:
|
7
7
|
attr_accessor :type
|
8
8
|
|
9
9
|
def initialize(string)
|
@@ -3,8 +3,8 @@
|
|
3
3
|
require "active_support/core_ext/module/attribute_accessors"
|
4
4
|
|
5
5
|
module ActionView
|
6
|
-
class Template
|
7
|
-
|
6
|
+
class Template # :nodoc:
|
7
|
+
module Types
|
8
8
|
class Type
|
9
9
|
SET = Struct.new(:symbols).new([ :html, :text, :js, :css, :xml, :json ])
|
10
10
|
|
@@ -37,21 +37,23 @@ module ActionView
|
|
37
37
|
end
|
38
38
|
end
|
39
39
|
|
40
|
-
|
40
|
+
class << self
|
41
|
+
attr_accessor :type_klass
|
41
42
|
|
42
|
-
|
43
|
-
|
44
|
-
|
43
|
+
def delegate_to(klass)
|
44
|
+
self.type_klass = klass
|
45
|
+
end
|
45
46
|
|
46
|
-
|
47
|
+
def [](type)
|
48
|
+
type_klass[type]
|
49
|
+
end
|
47
50
|
|
48
|
-
|
49
|
-
|
51
|
+
def symbols
|
52
|
+
type_klass::SET.symbols
|
53
|
+
end
|
50
54
|
end
|
51
55
|
|
52
|
-
|
53
|
-
type_klass::SET.symbols
|
54
|
-
end
|
56
|
+
delegate_to Type
|
55
57
|
end
|
56
58
|
end
|
57
59
|
end
|
data/lib/action_view/template.rb
CHANGED
@@ -319,7 +319,16 @@ module ActionView
|
|
319
319
|
# Only locals with valid variable names get set directly. Others will
|
320
320
|
# still be available in local_assigns.
|
321
321
|
locals = @locals - Module::RUBY_RESERVED_KEYWORDS
|
322
|
-
|
322
|
+
deprecated_locals = locals.grep(/\A@+/)
|
323
|
+
if deprecated_locals.any?
|
324
|
+
ActiveSupport::Deprecation.warn(<<~MSG)
|
325
|
+
Passing instance variables to `render` is deprecated.
|
326
|
+
In Rails 7.1, #{deprecated_locals.to_sentence} will be ignored.
|
327
|
+
MSG
|
328
|
+
locals = locals.grep(/\A@?(?![A-Z0-9])(?:[[:alnum:]_]|[^\0-\177])+\z/)
|
329
|
+
else
|
330
|
+
locals = locals.grep(/\A(?![A-Z0-9])(?:[[:alnum:]_]|[^\0-\177])+\z/)
|
331
|
+
end
|
323
332
|
|
324
333
|
# Assign for the same variable is to suppress unused variable warning
|
325
334
|
locals.each_with_object(+"") { |key, code| code << "#{key} = local_assigns[:#{key}]; #{key} = #{key};" }
|
@@ -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,64 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActionView
|
4
|
+
# Represents a template path within ActionView's lookup and rendering system,
|
5
|
+
# like "users/show"
|
6
|
+
#
|
7
|
+
# TemplatePath makes it convenient to convert between separate name, prefix,
|
8
|
+
# partial arguments and the virtual path.
|
9
|
+
class TemplatePath
|
10
|
+
attr_reader :name, :prefix, :partial, :virtual
|
11
|
+
alias_method :partial?, :partial
|
12
|
+
alias_method :virtual_path, :virtual
|
13
|
+
|
14
|
+
# Convert name, prefix, and partial into a virtual path string
|
15
|
+
def self.virtual(name, prefix, partial)
|
16
|
+
if prefix.empty?
|
17
|
+
"#{partial ? "_" : ""}#{name}"
|
18
|
+
elsif partial
|
19
|
+
"#{prefix}/_#{name}"
|
20
|
+
else
|
21
|
+
"#{prefix}/#{name}"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# Build a TemplatePath form a virtual path
|
26
|
+
def self.parse(virtual)
|
27
|
+
if nameidx = virtual.rindex("/")
|
28
|
+
prefix = virtual[0, nameidx]
|
29
|
+
name = virtual.from(nameidx + 1)
|
30
|
+
prefix = prefix[1..] if prefix.start_with?("/")
|
31
|
+
else
|
32
|
+
prefix = ""
|
33
|
+
name = virtual
|
34
|
+
end
|
35
|
+
partial = name.start_with?("_")
|
36
|
+
name = name[1..] if partial
|
37
|
+
new name, prefix, partial, virtual
|
38
|
+
end
|
39
|
+
|
40
|
+
# Convert name, prefix, and partial into a TemplatePath
|
41
|
+
def self.build(name, prefix, partial)
|
42
|
+
new name, prefix, partial, virtual(name, prefix, partial)
|
43
|
+
end
|
44
|
+
|
45
|
+
def initialize(name, prefix, partial, virtual)
|
46
|
+
@name = name
|
47
|
+
@prefix = prefix
|
48
|
+
@partial = partial
|
49
|
+
@virtual = virtual
|
50
|
+
end
|
51
|
+
|
52
|
+
alias :to_str :virtual
|
53
|
+
alias :to_s :virtual
|
54
|
+
|
55
|
+
def hash # :nodoc:
|
56
|
+
@virtual.hash
|
57
|
+
end
|
58
|
+
|
59
|
+
def eql?(other) # :nodoc:
|
60
|
+
@virtual == other.virtual
|
61
|
+
end
|
62
|
+
alias :== :eql? # :nodoc:
|
63
|
+
end
|
64
|
+
end
|