actionview 6.1.7.2 → 7.0.6
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 +268 -254
- data/MIT-LICENSE +1 -0
- data/README.rdoc +2 -2
- data/lib/action_view/base.rb +4 -7
- 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 +5 -5
- data/lib/action_view/helpers/active_model_helper.rb +2 -2
- data/lib/action_view/helpers/asset_tag_helper.rb +95 -39
- data/lib/action_view/helpers/asset_url_helper.rb +16 -16
- data/lib/action_view/helpers/atom_feed_helper.rb +3 -4
- data/lib/action_view/helpers/cache_helper.rb +52 -3
- data/lib/action_view/helpers/capture_helper.rb +4 -4
- 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 +2 -2
- data/lib/action_view/helpers/date_helper.rb +111 -43
- data/lib/action_view/helpers/debug_helper.rb +3 -1
- data/lib/action_view/helpers/form_helper.rb +211 -85
- data/lib/action_view/helpers/form_options_helper.rb +70 -33
- data/lib/action_view/helpers/form_tag_helper.rb +150 -53
- data/lib/action_view/helpers/javascript_helper.rb +3 -5
- data/lib/action_view/helpers/number_helper.rb +17 -16
- data/lib/action_view/helpers/output_safety_helper.rb +4 -4
- data/lib/action_view/helpers/rendering_helper.rb +5 -6
- data/lib/action_view/helpers/sanitize_helper.rb +3 -3
- data/lib/action_view/helpers/tag_helper.rb +37 -8
- data/lib/action_view/helpers/tags/base.rb +5 -25
- data/lib/action_view/helpers/tags/check_box.rb +1 -1
- data/lib/action_view/helpers/tags/collection_select.rb +1 -1
- data/lib/action_view/helpers/tags/file_field.rb +16 -0
- data/lib/action_view/helpers/tags/select.rb +1 -1
- data/lib/action_view/helpers/tags/time_field.rb +10 -1
- data/lib/action_view/helpers/tags/weekday_select.rb +28 -0
- data/lib/action_view/helpers/tags.rb +3 -2
- data/lib/action_view/helpers/text_helper.rb +25 -14
- data/lib/action_view/helpers/translation_helper.rb +12 -43
- data/lib/action_view/helpers/url_helper.rb +194 -123
- data/lib/action_view/helpers.rb +25 -25
- data/lib/action_view/layouts.rb +7 -4
- data/lib/action_view/lookup_context.rb +33 -52
- data/lib/action_view/model_naming.rb +2 -2
- data/lib/action_view/path_set.rb +16 -22
- data/lib/action_view/railtie.rb +19 -7
- data/lib/action_view/record_identifier.rb +1 -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 +1 -35
- 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 +3 -3
- 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 +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 +89 -314
- data/lib/action_view/template/text.rb +3 -3
- data/lib/action_view/template/types.rb +14 -12
- data/lib/action_view/template.rb +18 -2
- 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 +7 -3
- data/lib/action_view/testing/resolvers.rb +11 -12
- data/lib/action_view/unbound_template.rb +33 -7
- data/lib/action_view/version.rb +1 -1
- data/lib/action_view/view_paths.rb +4 -4
- data/lib/action_view.rb +2 -3
- data/lib/assets/compiled/rails-ujs.js +36 -5
- metadata +23 -16
@@ -10,36 +10,18 @@ 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
|
-
handlers = Template::Handlers.extensions.map
|
41
|
-
formats = Template::Types.symbols.map
|
42
|
-
|
20
|
+
handlers = Regexp.union(Template::Handlers.extensions.map(&:to_s))
|
21
|
+
formats = Regexp.union(Template::Types.symbols.map(&:to_s))
|
22
|
+
available_locales = I18n.available_locales.map(&:to_s)
|
23
|
+
regular_locales = [/[a-z]{2}(?:[-_][A-Z]{2})?/]
|
24
|
+
locales = Regexp.union(available_locales + regular_locales)
|
43
25
|
variants = "[^.]*"
|
44
26
|
|
45
27
|
%r{
|
@@ -58,79 +40,15 @@ module ActionView
|
|
58
40
|
def parse(path)
|
59
41
|
@regex ||= build_path_regex
|
60
42
|
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
|
43
|
+
path = TemplatePath.build(match[:action], match[:prefix] || "", !!match[:partial])
|
44
|
+
details = TemplateDetails.new(
|
45
|
+
match[:locale]&.to_sym,
|
46
|
+
match[:handler]&.to_sym,
|
47
|
+
match[:format]&.to_sym,
|
48
|
+
match[:variant]&.to_sym
|
49
|
+
)
|
50
|
+
ParsedPath.new(path, details)
|
128
51
|
end
|
129
|
-
|
130
|
-
private
|
131
|
-
def canonical_no_templates(templates)
|
132
|
-
templates.empty? ? NO_TEMPLATES : templates
|
133
|
-
end
|
134
52
|
end
|
135
53
|
|
136
54
|
cattr_accessor :caching, default: true
|
@@ -139,25 +57,17 @@ module ActionView
|
|
139
57
|
alias :caching? :caching
|
140
58
|
end
|
141
59
|
|
142
|
-
def initialize
|
143
|
-
@cache = Cache.new
|
144
|
-
end
|
145
|
-
|
146
60
|
def clear_cache
|
147
|
-
@cache.clear
|
148
61
|
end
|
149
62
|
|
150
63
|
# Normalizes the arguments and passes it on to find_templates.
|
151
64
|
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
|
65
|
+
_find_all(name, prefix, partial, details, key, locals)
|
157
66
|
end
|
158
67
|
|
159
|
-
def
|
160
|
-
|
68
|
+
def all_template_paths # :nodoc:
|
69
|
+
# Not implemented by default
|
70
|
+
[]
|
161
71
|
end
|
162
72
|
|
163
73
|
private
|
@@ -173,34 +83,18 @@ module ActionView
|
|
173
83
|
def find_templates(name, prefix, partial, details, locals = [])
|
174
84
|
raise NotImplementedError, "Subclasses must implement a find_templates(name, prefix, partial, details, locals = []) method"
|
175
85
|
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
86
|
end
|
193
87
|
|
194
|
-
#
|
195
|
-
class
|
196
|
-
|
197
|
-
DEFAULT_PATTERN = ":prefix/:action{.:locale,}{.:formats,}{+:variants,}{.:handlers,}"
|
88
|
+
# A resolver that loads files from the filesystem.
|
89
|
+
class FileSystemResolver < Resolver
|
90
|
+
attr_reader :path
|
198
91
|
|
199
|
-
def initialize
|
200
|
-
|
92
|
+
def initialize(path)
|
93
|
+
raise ArgumentError, "path already is a Resolver class" if path.is_a?(Resolver)
|
201
94
|
@unbound_templates = Concurrent::Map.new
|
202
95
|
@path_parser = PathParser.new
|
203
|
-
|
96
|
+
@path = File.expand_path(path)
|
97
|
+
super()
|
204
98
|
end
|
205
99
|
|
206
100
|
def clear_cache
|
@@ -209,26 +103,37 @@ module ActionView
|
|
209
103
|
super
|
210
104
|
end
|
211
105
|
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
106
|
+
def to_s
|
107
|
+
@path.to_s
|
108
|
+
end
|
109
|
+
alias :to_path :to_s
|
110
|
+
|
111
|
+
def eql?(resolver)
|
112
|
+
self.class.equal?(resolver.class) && to_path == resolver.to_path
|
113
|
+
end
|
114
|
+
alias :== :eql?
|
115
|
+
|
116
|
+
def all_template_paths # :nodoc:
|
117
|
+
paths = template_glob("**/*")
|
118
|
+
paths.map do |filename|
|
119
|
+
filename.from(@path.size + 1).remove(/\.[^\/]*\z/)
|
120
|
+
end.uniq.map do |filename|
|
121
|
+
TemplatePath.parse(filename)
|
216
122
|
end
|
123
|
+
end
|
217
124
|
|
218
|
-
|
219
|
-
|
220
|
-
|
125
|
+
private
|
126
|
+
def _find_all(name, prefix, partial, details, key, locals)
|
127
|
+
requested_details = key || TemplateDetails::Requested.new(**details)
|
128
|
+
cache = key ? @unbound_templates : Concurrent::Map.new
|
221
129
|
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
end
|
228
|
-
else
|
229
|
-
build_unbound_template(template, path.virtual)
|
230
|
-
end
|
130
|
+
unbound_templates =
|
131
|
+
cache.compute_if_absent(TemplatePath.virtual(name, prefix, partial)) do
|
132
|
+
path = TemplatePath.build(name, prefix, partial)
|
133
|
+
unbound_templates_from_path(path)
|
134
|
+
end
|
231
135
|
|
136
|
+
filter_and_sort_by_details(unbound_templates, requested_details).map do |unbound_template|
|
232
137
|
unbound_template.bind_locals(locals)
|
233
138
|
end
|
234
139
|
end
|
@@ -237,196 +142,66 @@ module ActionView
|
|
237
142
|
Template::Sources::File.new(template)
|
238
143
|
end
|
239
144
|
|
240
|
-
def build_unbound_template(template
|
241
|
-
|
145
|
+
def build_unbound_template(template)
|
146
|
+
parsed = @path_parser.parse(template.from(@path.size + 1))
|
147
|
+
details = parsed.details
|
242
148
|
source = source_for_template(template)
|
243
149
|
|
244
150
|
UnboundTemplate.new(
|
245
151
|
source,
|
246
152
|
template,
|
247
|
-
|
248
|
-
virtual_path:
|
249
|
-
format: format,
|
250
|
-
variant: variant,
|
153
|
+
details: details,
|
154
|
+
virtual_path: parsed.path.virtual,
|
251
155
|
)
|
252
156
|
end
|
253
157
|
|
254
|
-
def
|
255
|
-
files.reject { |filename| !inside_path?(@path, filename) }
|
256
|
-
end
|
257
|
-
|
258
|
-
def find_template_paths_from_details(path, details)
|
158
|
+
def unbound_templates_from_path(path)
|
259
159
|
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
|
160
|
+
return []
|
297
161
|
end
|
298
162
|
|
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
163
|
# Instead of checking for every possible path, as our other globs would
|
351
164
|
# do, scan the directory for files with the right prefix.
|
352
|
-
|
165
|
+
paths = template_glob("#{escape_entry(path.to_s)}*")
|
353
166
|
|
354
|
-
|
355
|
-
|
167
|
+
paths.map do |path|
|
168
|
+
build_unbound_template(path)
|
169
|
+
end.select do |template|
|
170
|
+
# Select for exact virtual path match, including case sensitivity
|
171
|
+
template.virtual_path == path.virtual
|
356
172
|
end
|
357
173
|
end
|
358
174
|
|
359
|
-
def
|
360
|
-
|
361
|
-
|
362
|
-
return super
|
175
|
+
def filter_and_sort_by_details(templates, requested_details)
|
176
|
+
filtered_templates = templates.select do |template|
|
177
|
+
template.details.matches?(requested_details)
|
363
178
|
end
|
364
179
|
|
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
|
180
|
+
if filtered_templates.count > 1
|
181
|
+
filtered_templates.sort_by! do |template|
|
182
|
+
template.details.sort_key_for(requested_details)
|
391
183
|
end
|
392
184
|
end
|
393
|
-
end
|
394
185
|
|
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}
|
186
|
+
filtered_templates
|
412
187
|
end
|
413
|
-
end
|
414
188
|
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
189
|
+
# Safe glob within @path
|
190
|
+
def template_glob(glob)
|
191
|
+
query = File.join(escape_entry(@path), glob)
|
192
|
+
path_with_slash = File.join(@path, "")
|
419
193
|
|
420
|
-
|
421
|
-
|
422
|
-
|
194
|
+
Dir.glob(query).filter_map do |filename|
|
195
|
+
filename = File.expand_path(filename)
|
196
|
+
next if File.directory?(filename)
|
197
|
+
next unless filename.start_with?(path_with_slash)
|
423
198
|
|
424
|
-
|
425
|
-
|
426
|
-
|
199
|
+
filename
|
200
|
+
end
|
201
|
+
end
|
427
202
|
|
428
|
-
|
429
|
-
|
430
|
-
|
203
|
+
def escape_entry(entry)
|
204
|
+
entry.gsub(/[*?{}\[\]]/, '\\\\\\&')
|
205
|
+
end
|
431
206
|
end
|
432
207
|
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
@@ -114,6 +114,9 @@ module ActionView
|
|
114
114
|
|
115
115
|
extend Template::Handlers
|
116
116
|
|
117
|
+
singleton_class.attr_accessor :frozen_string_literal
|
118
|
+
@frozen_string_literal = false
|
119
|
+
|
117
120
|
attr_reader :identifier, :handler
|
118
121
|
attr_reader :variable, :format, :variant, :locals, :virtual_path
|
119
122
|
|
@@ -297,7 +300,11 @@ module ActionView
|
|
297
300
|
end
|
298
301
|
|
299
302
|
begin
|
300
|
-
|
303
|
+
if Template.frozen_string_literal
|
304
|
+
mod.module_eval("# frozen_string_literal: true\n#{source}", identifier, -1)
|
305
|
+
else
|
306
|
+
mod.module_eval(source, identifier, 0)
|
307
|
+
end
|
301
308
|
rescue SyntaxError
|
302
309
|
# Account for when code in the template is not syntactically valid; e.g. if we're using
|
303
310
|
# ERB and the user writes <%= foo( %>, attempting to call a helper `foo` and interpolate
|
@@ -319,7 +326,16 @@ module ActionView
|
|
319
326
|
# Only locals with valid variable names get set directly. Others will
|
320
327
|
# still be available in local_assigns.
|
321
328
|
locals = @locals - Module::RUBY_RESERVED_KEYWORDS
|
322
|
-
|
329
|
+
deprecated_locals = locals.grep(/\A@+/)
|
330
|
+
if deprecated_locals.any?
|
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
|
323
339
|
|
324
340
|
# Assign for the same variable is to suppress unused variable warning
|
325
341
|
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
|