actionview 6.0.0.beta2 → 6.0.2.rc1
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 +80 -3
- data/README.rdoc +3 -1
- data/lib/action_view.rb +2 -1
- data/lib/action_view/base.rb +5 -5
- data/lib/action_view/cache_expiry.rb +54 -0
- data/lib/action_view/digestor.rb +5 -10
- data/lib/action_view/gem_version.rb +2 -2
- data/lib/action_view/helpers/form_helper.rb +2 -2
- data/lib/action_view/helpers/form_options_helper.rb +4 -3
- data/lib/action_view/helpers/form_tag_helper.rb +5 -2
- data/lib/action_view/helpers/output_safety_helper.rb +1 -1
- data/lib/action_view/helpers/sanitize_helper.rb +10 -16
- data/lib/action_view/helpers/tag_helper.rb +1 -1
- data/lib/action_view/helpers/tags/base.rb +1 -1
- data/lib/action_view/helpers/translation_helper.rb +3 -3
- data/lib/action_view/helpers/url_helper.rb +2 -2
- data/lib/action_view/layouts.rb +5 -1
- data/lib/action_view/lookup_context.rb +11 -4
- data/lib/action_view/path_set.rb +5 -10
- data/lib/action_view/railtie.rb +1 -1
- data/lib/action_view/renderer/partial_renderer.rb +0 -3
- data/lib/action_view/renderer/partial_renderer/collection_caching.rb +20 -13
- data/lib/action_view/renderer/streaming_template_renderer.rb +1 -1
- data/lib/action_view/renderer/template_renderer.rb +9 -3
- data/lib/action_view/rendering.rb +3 -2
- data/lib/action_view/template.rb +43 -50
- data/lib/action_view/template/error.rb +21 -1
- data/lib/action_view/template/handlers.rb +3 -3
- data/lib/action_view/template/handlers/erb/erubi.rb +2 -2
- data/lib/action_view/template/raw_file.rb +28 -0
- data/lib/action_view/template/resolver.rb +73 -117
- data/lib/action_view/template/sources.rb +13 -0
- data/lib/action_view/template/sources/file.rb +17 -0
- data/lib/action_view/testing/resolvers.rb +32 -21
- data/lib/action_view/unbound_template.rb +32 -0
- data/lib/assets/compiled/rails-ujs.js +21 -12
- metadata +24 -17
- data/lib/action_view/file_template.rb +0 -33
@@ -109,7 +109,7 @@ module ActionView
|
|
109
109
|
end
|
110
110
|
end
|
111
111
|
|
112
|
-
def
|
112
|
+
def annotated_source_code
|
113
113
|
source_extract(4)
|
114
114
|
end
|
115
115
|
|
@@ -138,4 +138,24 @@ module ActionView
|
|
138
138
|
end
|
139
139
|
|
140
140
|
TemplateError = Template::Error
|
141
|
+
|
142
|
+
class SyntaxErrorInTemplate < TemplateError #:nodoc
|
143
|
+
def initialize(template, offending_code_string)
|
144
|
+
@offending_code_string = offending_code_string
|
145
|
+
super(template)
|
146
|
+
end
|
147
|
+
|
148
|
+
def message
|
149
|
+
<<~MESSAGE
|
150
|
+
Encountered a syntax error while rendering template: check #{@offending_code_string}
|
151
|
+
MESSAGE
|
152
|
+
end
|
153
|
+
|
154
|
+
def annotated_source_code
|
155
|
+
@offending_code_string.split("\n").map.with_index(1) { |line, index|
|
156
|
+
indentation = " " * 4
|
157
|
+
"#{index}:#{indentation}#{line}"
|
158
|
+
}
|
159
|
+
end
|
160
|
+
end
|
141
161
|
end
|
@@ -45,12 +45,12 @@ module ActionView #:nodoc:
|
|
45
45
|
|
46
46
|
unless params.find_all { |type, _| type == :req || type == :opt }.length >= 2
|
47
47
|
ActiveSupport::Deprecation.warn <<~eowarn
|
48
|
-
Single arity template handlers are deprecated.
|
48
|
+
Single arity template handlers are deprecated. Template handlers must
|
49
49
|
now accept two parameters, the view object and the source for the view object.
|
50
50
|
Change:
|
51
|
-
>> #{handler.
|
51
|
+
>> #{handler}.call(#{params.map(&:last).join(", ")})
|
52
52
|
To:
|
53
|
-
>> #{handler.
|
53
|
+
>> #{handler}.call(#{params.map(&:last).join(", ")}, source)
|
54
54
|
eowarn
|
55
55
|
handler = LegacyHandlerWrapper.new(handler)
|
56
56
|
end
|
@@ -25,9 +25,9 @@ module ActionView
|
|
25
25
|
src = @src
|
26
26
|
view = Class.new(ActionView::Base) {
|
27
27
|
include action_view_erb_handler_context._routes.url_helpers
|
28
|
-
class_eval("define_method(:_template) { |local_assigns, output_buffer| #{src} }", @filename
|
28
|
+
class_eval("define_method(:_template) { |local_assigns, output_buffer| #{src} }", defined?(@filename) ? @filename : "(erubi)", 0)
|
29
29
|
}.empty
|
30
|
-
view.
|
30
|
+
view._run(:_template, nil, {}, ActionView::OutputBuffer.new)
|
31
31
|
end
|
32
32
|
|
33
33
|
private
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActionView #:nodoc:
|
4
|
+
# = Action View RawFile Template
|
5
|
+
class Template #:nodoc:
|
6
|
+
class RawFile #:nodoc:
|
7
|
+
attr_accessor :type, :format
|
8
|
+
|
9
|
+
def initialize(filename)
|
10
|
+
@filename = filename.to_s
|
11
|
+
extname = ::File.extname(filename).delete(".")
|
12
|
+
@type = Template::Types[extname] || Template::Types[:text]
|
13
|
+
@format = @type.symbol
|
14
|
+
end
|
15
|
+
|
16
|
+
def identifier
|
17
|
+
@filename
|
18
|
+
end
|
19
|
+
|
20
|
+
def render(*args)
|
21
|
+
::File.read(@filename)
|
22
|
+
end
|
23
|
+
|
24
|
+
def formats; Array(format); end
|
25
|
+
deprecate :formats
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -63,26 +63,11 @@ module ActionView
|
|
63
63
|
|
64
64
|
# Cache the templates returned by the block
|
65
65
|
def cache(key, name, prefix, partial, locals)
|
66
|
-
|
67
|
-
@data[key][name][prefix][partial][locals] ||= canonical_no_templates(yield)
|
68
|
-
else
|
69
|
-
fresh_templates = yield
|
70
|
-
cached_templates = @data[key][name][prefix][partial][locals]
|
71
|
-
|
72
|
-
if templates_have_changed?(cached_templates, fresh_templates)
|
73
|
-
@data[key][name][prefix][partial][locals] = canonical_no_templates(fresh_templates)
|
74
|
-
else
|
75
|
-
cached_templates || NO_TEMPLATES
|
76
|
-
end
|
77
|
-
end
|
66
|
+
@data[key][name][prefix][partial][locals] ||= canonical_no_templates(yield)
|
78
67
|
end
|
79
68
|
|
80
69
|
def cache_query(query) # :nodoc:
|
81
|
-
|
82
|
-
@query_cache[query] ||= canonical_no_templates(yield)
|
83
|
-
else
|
84
|
-
yield
|
85
|
-
end
|
70
|
+
@query_cache[query] ||= canonical_no_templates(yield)
|
86
71
|
end
|
87
72
|
|
88
73
|
def clear
|
@@ -112,19 +97,6 @@ module ActionView
|
|
112
97
|
def canonical_no_templates(templates)
|
113
98
|
templates.empty? ? NO_TEMPLATES : templates
|
114
99
|
end
|
115
|
-
|
116
|
-
def templates_have_changed?(cached_templates, fresh_templates)
|
117
|
-
# if either the old or new template list is empty, we don't need to (and can't)
|
118
|
-
# compare modification times, and instead just check whether the lists are different
|
119
|
-
if cached_templates.blank? || fresh_templates.blank?
|
120
|
-
return fresh_templates.blank? != cached_templates.blank?
|
121
|
-
end
|
122
|
-
|
123
|
-
cached_templates_max_updated_at = cached_templates.map(&:updated_at).max
|
124
|
-
|
125
|
-
# if a template has changed, it will be now be newer than all the cached templates
|
126
|
-
fresh_templates.any? { |t| t.updated_at > cached_templates_max_updated_at }
|
127
|
-
end
|
128
100
|
end
|
129
101
|
|
130
102
|
cattr_accessor :caching, default: true
|
@@ -143,35 +115,33 @@ module ActionView
|
|
143
115
|
|
144
116
|
# Normalizes the arguments and passes it on to find_templates.
|
145
117
|
def find_all(name, prefix = nil, partial = false, details = {}, key = nil, locals = [])
|
146
|
-
|
147
|
-
find_templates(name, prefix, partial, details)
|
148
|
-
end
|
149
|
-
end
|
118
|
+
locals = locals.map(&:to_s).sort!.freeze
|
150
119
|
|
151
|
-
def find_all_anywhere(name, prefix, partial = false, details = {}, key = nil, locals = [])
|
152
120
|
cached(key, [name, prefix, partial], details, locals) do
|
153
|
-
|
121
|
+
_find_all(name, prefix, partial, details, key, locals)
|
154
122
|
end
|
155
123
|
end
|
156
124
|
|
125
|
+
alias :find_all_anywhere :find_all
|
126
|
+
deprecate :find_all_anywhere
|
127
|
+
|
157
128
|
def find_all_with_query(query) # :nodoc:
|
158
129
|
@cache.cache_query(query) { find_template_paths(File.join(@path, query)) }
|
159
130
|
end
|
160
131
|
|
161
132
|
private
|
162
133
|
|
134
|
+
def _find_all(name, prefix, partial, details, key, locals)
|
135
|
+
find_templates(name, prefix, partial, details, locals)
|
136
|
+
end
|
137
|
+
|
163
138
|
delegate :caching?, to: :class
|
164
139
|
|
165
140
|
# This is what child classes implement. No defaults are needed
|
166
141
|
# because Resolver guarantees that the arguments are present and
|
167
142
|
# normalized.
|
168
|
-
def find_templates(name, prefix, partial, details,
|
169
|
-
raise NotImplementedError, "Subclasses must implement a find_templates(name, prefix, partial, details,
|
170
|
-
end
|
171
|
-
|
172
|
-
# Helpers that builds a path. Useful for building virtual paths.
|
173
|
-
def build_path(name, prefix, partial)
|
174
|
-
Path.build(name, prefix, partial)
|
143
|
+
def find_templates(name, prefix, partial, details, locals = [])
|
144
|
+
raise NotImplementedError, "Subclasses must implement a find_templates(name, prefix, partial, details, locals = []) method"
|
175
145
|
end
|
176
146
|
|
177
147
|
# Handles templates caching. If a key is given and caching is on
|
@@ -180,24 +150,13 @@ module ActionView
|
|
180
150
|
# resolver is fresher before returning it.
|
181
151
|
def cached(key, path_info, details, locals)
|
182
152
|
name, prefix, partial = path_info
|
183
|
-
locals = locals.map(&:to_s).sort!
|
184
153
|
|
185
154
|
if key
|
186
155
|
@cache.cache(key, name, prefix, partial, locals) do
|
187
|
-
|
156
|
+
yield
|
188
157
|
end
|
189
158
|
else
|
190
|
-
|
191
|
-
end
|
192
|
-
end
|
193
|
-
|
194
|
-
# Ensures all the resolver information is set in the template.
|
195
|
-
def decorate(templates, path_info, details, locals)
|
196
|
-
cached = nil
|
197
|
-
templates.each do |t|
|
198
|
-
t.locals = locals
|
199
|
-
t.variants = details[:variants] || [] if t.variants.empty?
|
200
|
-
t.virtual_path ||= (cached ||= build_path(*path_info))
|
159
|
+
yield
|
201
160
|
end
|
202
161
|
end
|
203
162
|
end
|
@@ -208,33 +167,60 @@ module ActionView
|
|
208
167
|
DEFAULT_PATTERN = ":prefix/:action{.:locale,}{.:formats,}{+:variants,}{.:handlers,}"
|
209
168
|
|
210
169
|
def initialize(pattern = nil)
|
211
|
-
|
170
|
+
if pattern
|
171
|
+
ActiveSupport::Deprecation.warn "Specifying a custom path for #{self.class} is deprecated. Implement a custom Resolver subclass instead."
|
172
|
+
@pattern = pattern
|
173
|
+
else
|
174
|
+
@pattern = DEFAULT_PATTERN
|
175
|
+
end
|
176
|
+
@unbound_templates = Concurrent::Map.new
|
177
|
+
super()
|
178
|
+
end
|
179
|
+
|
180
|
+
def clear_cache
|
181
|
+
@unbound_templates.clear
|
212
182
|
super()
|
213
183
|
end
|
214
184
|
|
215
185
|
private
|
216
186
|
|
217
|
-
def
|
187
|
+
def _find_all(name, prefix, partial, details, key, locals)
|
218
188
|
path = Path.build(name, prefix, partial)
|
219
|
-
query(path, details, details[:formats],
|
189
|
+
query(path, details, details[:formats], locals, cache: !!key)
|
220
190
|
end
|
221
191
|
|
222
|
-
def query(path, details, formats,
|
192
|
+
def query(path, details, formats, locals, cache:)
|
223
193
|
template_paths = find_template_paths_from_details(path, details)
|
224
|
-
template_paths = reject_files_external_to_app(template_paths)
|
194
|
+
template_paths = reject_files_external_to_app(template_paths)
|
225
195
|
|
226
196
|
template_paths.map do |template|
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
197
|
+
unbound_template =
|
198
|
+
if cache
|
199
|
+
@unbound_templates.compute_if_absent([template, path.virtual]) do
|
200
|
+
build_unbound_template(template, path.virtual)
|
201
|
+
end
|
202
|
+
else
|
203
|
+
build_unbound_template(template, path.virtual)
|
204
|
+
end
|
205
|
+
|
206
|
+
unbound_template.bind_locals(locals)
|
235
207
|
end
|
236
208
|
end
|
237
209
|
|
210
|
+
def build_unbound_template(template, virtual_path)
|
211
|
+
handler, format, variant = extract_handler_and_format_and_variant(template)
|
212
|
+
source = Template::Sources::File.new(template)
|
213
|
+
|
214
|
+
UnboundTemplate.new(
|
215
|
+
source,
|
216
|
+
template,
|
217
|
+
handler,
|
218
|
+
virtual_path: virtual_path,
|
219
|
+
format: format,
|
220
|
+
variant: variant,
|
221
|
+
)
|
222
|
+
end
|
223
|
+
|
238
224
|
def reject_files_external_to_app(files)
|
239
225
|
files.reject { |filename| !inside_path?(@path, filename) }
|
240
226
|
end
|
@@ -283,15 +269,10 @@ module ActionView
|
|
283
269
|
entry.gsub(/[*?{}\[\]]/, '\\\\\\&')
|
284
270
|
end
|
285
271
|
|
286
|
-
# Returns the file mtime from the filesystem.
|
287
|
-
def mtime(p)
|
288
|
-
File.mtime(p)
|
289
|
-
end
|
290
|
-
|
291
272
|
# Extract handler, formats and variant from path. If a format cannot be found neither
|
292
273
|
# from the path, or the handler, we should return the array of formats given
|
293
274
|
# to the resolver.
|
294
|
-
def extract_handler_and_format_and_variant(path
|
275
|
+
def extract_handler_and_format_and_variant(path)
|
295
276
|
pieces = File.basename(path).split(".")
|
296
277
|
pieces.shift
|
297
278
|
|
@@ -305,54 +286,19 @@ module ActionView
|
|
305
286
|
if handler.respond_to?(:default_format) # default_format can return nil
|
306
287
|
handler.default_format
|
307
288
|
else
|
308
|
-
|
289
|
+
nil
|
309
290
|
end
|
310
291
|
end
|
311
292
|
|
312
293
|
# Template::Types[format] and handler.default_format can return nil
|
313
|
-
[handler, format
|
294
|
+
[handler, format, variant]
|
314
295
|
end
|
315
296
|
end
|
316
297
|
|
317
|
-
# A resolver that loads files from the filesystem.
|
318
|
-
# resolving pattern. Such pattern can be a glob string supported by some variables.
|
319
|
-
#
|
320
|
-
# ==== Examples
|
321
|
-
#
|
322
|
-
# Default pattern, loads views the same way as previous versions of rails, eg. when you're
|
323
|
-
# looking for <tt>users/new</tt> it will produce query glob: <tt>users/new{.{en},}{.{html,js},}{.{erb,haml},}</tt>
|
324
|
-
#
|
325
|
-
# FileSystemResolver.new("/path/to/views", ":prefix/:action{.:locale,}{.:formats,}{+:variants,}{.:handlers,}")
|
326
|
-
#
|
327
|
-
# This one allows you to keep files with different formats in separate subdirectories,
|
328
|
-
# eg. <tt>users/new.html</tt> will be loaded from <tt>users/html/new.erb</tt> or <tt>users/new.html.erb</tt>,
|
329
|
-
# <tt>users/new.js</tt> from <tt>users/js/new.erb</tt> or <tt>users/new.js.erb</tt>, etc.
|
330
|
-
#
|
331
|
-
# FileSystemResolver.new("/path/to/views", ":prefix/{:formats/,}:action{.:locale,}{.:formats,}{+:variants,}{.:handlers,}")
|
332
|
-
#
|
333
|
-
# If you don't specify a pattern then the default will be used.
|
334
|
-
#
|
335
|
-
# In order to use any of the customized resolvers above in a Rails application, you just need
|
336
|
-
# to configure ActionController::Base.view_paths in an initializer, for example:
|
337
|
-
#
|
338
|
-
# ActionController::Base.view_paths = FileSystemResolver.new(
|
339
|
-
# Rails.root.join("app/views"),
|
340
|
-
# ":prefix/:action{.:locale,}{.:formats,}{+:variants,}{.:handlers,}",
|
341
|
-
# )
|
342
|
-
#
|
343
|
-
# ==== Pattern format and variables
|
344
|
-
#
|
345
|
-
# Pattern has to be a valid glob string, and it allows you to use the
|
346
|
-
# following variables:
|
347
|
-
#
|
348
|
-
# * <tt>:prefix</tt> - usually the controller path
|
349
|
-
# * <tt>:action</tt> - name of the action
|
350
|
-
# * <tt>:locale</tt> - possible locale versions
|
351
|
-
# * <tt>:formats</tt> - possible request formats (for example html, json, xml...)
|
352
|
-
# * <tt>:variants</tt> - possible request variants (for example phone, tablet...)
|
353
|
-
# * <tt>:handlers</tt> - possible handlers (for example erb, haml, builder...)
|
354
|
-
#
|
298
|
+
# A resolver that loads files from the filesystem.
|
355
299
|
class FileSystemResolver < PathResolver
|
300
|
+
attr_reader :path
|
301
|
+
|
356
302
|
def initialize(path, pattern = nil)
|
357
303
|
raise ArgumentError, "path already is a Resolver class" if path.is_a?(Resolver)
|
358
304
|
super(pattern)
|
@@ -372,6 +318,10 @@ module ActionView
|
|
372
318
|
|
373
319
|
# An Optimized resolver for Rails' most common case.
|
374
320
|
class OptimizedFileSystemResolver < FileSystemResolver #:nodoc:
|
321
|
+
def initialize(path)
|
322
|
+
super(path)
|
323
|
+
end
|
324
|
+
|
375
325
|
private
|
376
326
|
|
377
327
|
def find_template_paths_from_details(path, details)
|
@@ -427,12 +377,18 @@ module ActionView
|
|
427
377
|
# The same as FileSystemResolver but does not allow templates to store
|
428
378
|
# a virtual path since it is invalid for such resolvers.
|
429
379
|
class FallbackFileSystemResolver < FileSystemResolver #:nodoc:
|
380
|
+
private_class_method :new
|
381
|
+
|
430
382
|
def self.instances
|
431
383
|
[new(""), new("/")]
|
432
384
|
end
|
433
385
|
|
434
|
-
def
|
435
|
-
super
|
386
|
+
def build_unbound_template(template, _)
|
387
|
+
super(template, nil)
|
388
|
+
end
|
389
|
+
|
390
|
+
def reject_files_external_to_app(files)
|
391
|
+
files
|
436
392
|
end
|
437
393
|
end
|
438
394
|
end
|
@@ -7,10 +7,15 @@ module ActionView #:nodoc:
|
|
7
7
|
# file system. This is used internally by Rails' own test suite, and is
|
8
8
|
# useful for testing extensions that have no way of knowing what the file
|
9
9
|
# system will look like at runtime.
|
10
|
-
class FixtureResolver <
|
10
|
+
class FixtureResolver < OptimizedFileSystemResolver
|
11
11
|
def initialize(hash = {}, pattern = nil)
|
12
|
-
super(
|
12
|
+
super("")
|
13
|
+
if pattern
|
14
|
+
ActiveSupport::Deprecation.warn "Specifying a custom path for #{self.class} is deprecated. Implement a custom Resolver subclass instead."
|
15
|
+
@pattern = pattern
|
16
|
+
end
|
13
17
|
@hash = hash
|
18
|
+
@path = ""
|
14
19
|
end
|
15
20
|
|
16
21
|
def data
|
@@ -23,34 +28,40 @@ module ActionView #:nodoc:
|
|
23
28
|
|
24
29
|
private
|
25
30
|
|
26
|
-
def query(path, exts, _,
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
source, updated_at = array
|
36
|
-
next unless query.match?(_path)
|
37
|
-
handler, format, variant = extract_handler_and_format_and_variant(_path, :html)
|
38
|
-
templates << Template.new(source, _path, handler,
|
31
|
+
def query(path, exts, _, locals, cache:)
|
32
|
+
regex = build_regex(path, exts)
|
33
|
+
|
34
|
+
@hash.select do |_path, _|
|
35
|
+
("/" + _path).match?(regex)
|
36
|
+
end.map do |_path, source|
|
37
|
+
handler, format, variant = extract_handler_and_format_and_variant(_path)
|
38
|
+
|
39
|
+
Template.new(source, _path, handler,
|
39
40
|
virtual_path: path.virtual,
|
40
41
|
format: format,
|
41
42
|
variant: variant,
|
42
|
-
|
43
|
+
locals: locals
|
43
44
|
)
|
45
|
+
end.sort_by do |t|
|
46
|
+
match = ("/" + t.identifier).match(regex)
|
47
|
+
EXTENSIONS.keys.reverse.map do |ext|
|
48
|
+
if ext == :variants && exts[ext] == :any
|
49
|
+
match[ext].nil? ? 0 : 1
|
50
|
+
elsif match[ext].nil?
|
51
|
+
exts[ext].length
|
52
|
+
else
|
53
|
+
found = match[ext].to_sym
|
54
|
+
exts[ext].index(found)
|
55
|
+
end
|
56
|
+
end
|
44
57
|
end
|
45
|
-
|
46
|
-
templates.sort_by { |t| -t.identifier.match(/^#{query}$/).captures.reject(&:blank?).size }
|
47
58
|
end
|
48
59
|
end
|
49
60
|
|
50
61
|
class NullResolver < PathResolver
|
51
|
-
def query(path, exts, _,
|
52
|
-
handler, format, variant = extract_handler_and_format_and_variant(path
|
53
|
-
[ActionView::Template.new("Template generated by Null Resolver", path.virtual, handler, virtual_path: path.virtual, format: format, variant: variant)]
|
62
|
+
def query(path, exts, _, locals, cache:)
|
63
|
+
handler, format, variant = extract_handler_and_format_and_variant(path)
|
64
|
+
[ActionView::Template.new("Template generated by Null Resolver", path.virtual, handler, virtual_path: path.virtual, format: format, variant: variant, locals: locals)]
|
54
65
|
end
|
55
66
|
end
|
56
67
|
end
|