actionview 6.0.0.beta3 → 6.0.0.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 +40 -4
- data/README.rdoc +3 -1
- data/lib/action_view.rb +2 -1
- data/lib/action_view/base.rb +4 -4
- data/lib/action_view/cache_expiry.rb +49 -0
- data/lib/action_view/digestor.rb +0 -6
- data/lib/action_view/gem_version.rb +1 -1
- data/lib/action_view/helpers/form_helper.rb +2 -2
- data/lib/action_view/helpers/form_tag_helper.rb +1 -1
- data/lib/action_view/helpers/output_safety_helper.rb +1 -1
- data/lib/action_view/helpers/tags/base.rb +1 -1
- data/lib/action_view/helpers/translation_helper.rb +2 -2
- data/lib/action_view/helpers/url_helper.rb +1 -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/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 +9 -10
- data/lib/action_view/unbound_template.rb +32 -0
- data/lib/assets/compiled/rails-ujs.js +14 -8
- metadata +16 -12
- data/lib/action_view/file_template.rb +0 -33
@@ -34,7 +34,7 @@ module ActionView
|
|
34
34
|
return unless logger
|
35
35
|
|
36
36
|
message = +"\n#{exception.class} (#{exception.message}):\n"
|
37
|
-
message << exception.
|
37
|
+
message << exception.annotated_source_code.to_s if exception.respond_to?(:annotated_source_code)
|
38
38
|
message << " " << exception.backtrace.join("\n ")
|
39
39
|
logger.fatal("#{message}\n\n")
|
40
40
|
end
|
@@ -26,7 +26,12 @@ module ActionView
|
|
26
26
|
elsif options.key?(:html)
|
27
27
|
Template::HTML.new(options[:html], formats.first)
|
28
28
|
elsif options.key?(:file)
|
29
|
-
|
29
|
+
if File.exist?(options[:file])
|
30
|
+
Template::RawFile.new(options[:file])
|
31
|
+
else
|
32
|
+
ActiveSupport::Deprecation.warn "render file: should be given the absolute path to a file"
|
33
|
+
@lookup_context.with_fallbacks.find_template(options[:file], nil, false, keys, @details)
|
34
|
+
end
|
30
35
|
elsif options.key?(:inline)
|
31
36
|
handler = Template.handler_for_extension(options[:type] || "erb")
|
32
37
|
format = if handler.respond_to?(:default_format)
|
@@ -49,14 +54,14 @@ module ActionView
|
|
49
54
|
# Renders the given template. A string representing the layout can be
|
50
55
|
# supplied as well.
|
51
56
|
def render_template(view, template, layout_name, locals)
|
52
|
-
render_with_layout(view,
|
57
|
+
render_with_layout(view, template, layout_name, locals) do |layout|
|
53
58
|
instrument(:template, identifier: template.identifier, layout: layout.try(:virtual_path)) do
|
54
59
|
template.render(view, locals) { |*name| view._layout_for(*name) }
|
55
60
|
end
|
56
61
|
end
|
57
62
|
end
|
58
63
|
|
59
|
-
def render_with_layout(view,
|
64
|
+
def render_with_layout(view, template, path, locals)
|
60
65
|
layout = path && find_layout(path, locals.keys, [formats.first])
|
61
66
|
content = yield(layout)
|
62
67
|
|
@@ -84,6 +89,7 @@ module ActionView
|
|
84
89
|
when String
|
85
90
|
begin
|
86
91
|
if layout.start_with?("/")
|
92
|
+
ActiveSupport::Deprecation.warn "Rendering layouts from an absolute path is deprecated."
|
87
93
|
@lookup_context.with_fallbacks.find_template(layout, nil, false, [], details)
|
88
94
|
else
|
89
95
|
@lookup_context.find_template(layout, nil, false, [], details)
|
@@ -118,7 +118,8 @@ module ActionView
|
|
118
118
|
renderer.render_to_object(context, options)
|
119
119
|
end
|
120
120
|
|
121
|
-
|
121
|
+
rendered_format = rendered_template.format || lookup_context.formats.first
|
122
|
+
@rendered_format = Template::Types[rendered_format]
|
122
123
|
|
123
124
|
rendered_template.body
|
124
125
|
end
|
@@ -126,7 +127,7 @@ module ActionView
|
|
126
127
|
# Assign the rendered format to look up context.
|
127
128
|
def _process_format(format)
|
128
129
|
super
|
129
|
-
lookup_context.formats = [format.to_sym]
|
130
|
+
lookup_context.formats = [format.to_sym] if format.to_sym
|
130
131
|
end
|
131
132
|
|
132
133
|
# Normalize args by converting render "foo" to render :action => "foo" and
|
data/lib/action_view/template.rb
CHANGED
@@ -113,51 +113,59 @@ module ActionView
|
|
113
113
|
|
114
114
|
eager_autoload do
|
115
115
|
autoload :Error
|
116
|
+
autoload :RawFile
|
116
117
|
autoload :Handlers
|
117
118
|
autoload :HTML
|
118
119
|
autoload :Inline
|
120
|
+
autoload :Sources
|
119
121
|
autoload :Text
|
120
122
|
autoload :Types
|
121
123
|
end
|
122
124
|
|
123
125
|
extend Template::Handlers
|
124
126
|
|
125
|
-
|
127
|
+
attr_reader :identifier, :handler, :original_encoding, :updated_at
|
128
|
+
attr_reader :variable, :format, :variant, :locals, :virtual_path
|
126
129
|
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
unless format
|
132
|
-
ActiveSupport::Deprecation.warn "ActionView::Template#initialize requires a format parameter"
|
133
|
-
format = :html
|
130
|
+
def initialize(source, identifier, handler, format: nil, variant: nil, locals: nil, virtual_path: nil, updated_at: nil)
|
131
|
+
unless locals
|
132
|
+
ActiveSupport::Deprecation.warn "ActionView::Template#initialize requires a locals parameter"
|
133
|
+
locals = []
|
134
134
|
end
|
135
135
|
|
136
136
|
@source = source
|
137
137
|
@identifier = identifier
|
138
138
|
@handler = handler
|
139
139
|
@compiled = false
|
140
|
-
@
|
141
|
-
@
|
142
|
-
@virtual_path = details[:virtual_path]
|
140
|
+
@locals = locals
|
141
|
+
@virtual_path = virtual_path
|
143
142
|
|
144
143
|
@variable = if @virtual_path
|
145
|
-
base = @virtual_path[-1] == "/" ? "" : File.basename(@virtual_path)
|
144
|
+
base = @virtual_path[-1] == "/" ? "" : ::File.basename(@virtual_path)
|
146
145
|
base =~ /\A_?(.*?)(?:\.\w+)*\z/
|
147
146
|
$1.to_sym
|
148
147
|
end
|
149
148
|
|
150
|
-
|
149
|
+
if updated_at
|
150
|
+
ActiveSupport::Deprecation.warn "ActionView::Template#updated_at is deprecated"
|
151
|
+
@updated_at = updated_at
|
152
|
+
else
|
153
|
+
@updated_at = Time.now
|
154
|
+
end
|
151
155
|
@format = format
|
152
|
-
@
|
156
|
+
@variant = variant
|
153
157
|
@compile_mutex = Mutex.new
|
154
158
|
end
|
155
159
|
|
156
|
-
|
157
|
-
|
158
|
-
deprecate
|
159
|
-
|
160
|
+
deprecate :original_encoding
|
161
|
+
deprecate :updated_at
|
162
|
+
deprecate def virtual_path=(_); end
|
163
|
+
deprecate def locals=(_); end
|
164
|
+
deprecate def formats=(_); end
|
160
165
|
deprecate def formats; Array(format); end
|
166
|
+
deprecate def variants=(_); end
|
167
|
+
deprecate def variants; [variant]; end
|
168
|
+
deprecate def refresh(_); self; end
|
161
169
|
|
162
170
|
# Returns whether the underlying handler supports streaming. If so,
|
163
171
|
# a streaming buffer *may* be passed when it starts rendering.
|
@@ -174,7 +182,7 @@ module ActionView
|
|
174
182
|
def render(view, locals, buffer = ActionView::OutputBuffer.new, &block)
|
175
183
|
instrument_render_template do
|
176
184
|
compile!(view)
|
177
|
-
view.
|
185
|
+
view._run(method_name, self, locals, buffer, &block)
|
178
186
|
end
|
179
187
|
rescue => e
|
180
188
|
handle_render_error(view, e)
|
@@ -184,25 +192,6 @@ module ActionView
|
|
184
192
|
@type ||= Types[format]
|
185
193
|
end
|
186
194
|
|
187
|
-
# Receives a view object and return a template similar to self by using @virtual_path.
|
188
|
-
#
|
189
|
-
# This method is useful if you have a template object but it does not contain its source
|
190
|
-
# anymore since it was already compiled. In such cases, all you need to do is to call
|
191
|
-
# refresh passing in the view object.
|
192
|
-
#
|
193
|
-
# Notice this method raises an error if the template to be refreshed does not have a
|
194
|
-
# virtual path set (true just for inline templates).
|
195
|
-
def refresh(view)
|
196
|
-
raise "A template needs to have a virtual path in order to be refreshed" unless @virtual_path
|
197
|
-
lookup = view.lookup_context
|
198
|
-
pieces = @virtual_path.split("/")
|
199
|
-
name = pieces.pop
|
200
|
-
partial = !!name.sub!(/^_/, "")
|
201
|
-
lookup.disable_cache do
|
202
|
-
lookup.find_template(name, [ pieces.join("/") ], partial, @locals)
|
203
|
-
end
|
204
|
-
end
|
205
|
-
|
206
195
|
def short_identifier
|
207
196
|
@short_identifier ||= defined?(Rails.root) ? identifier.sub("#{Rails.root}/", "") : identifier
|
208
197
|
end
|
@@ -211,6 +200,10 @@ module ActionView
|
|
211
200
|
"#<#{self.class.name} #{short_identifier} locals=#{@locals.inspect}>"
|
212
201
|
end
|
213
202
|
|
203
|
+
def source
|
204
|
+
@source.to_s
|
205
|
+
end
|
206
|
+
|
214
207
|
# This method is responsible for properly setting the encoding of the
|
215
208
|
# source. Until this point, we assume that the source is BINARY data.
|
216
209
|
# If no additional information is supplied, we assume the encoding is
|
@@ -262,11 +255,11 @@ module ActionView
|
|
262
255
|
# to ensure that references to the template object can be marshalled as well. This means forgoing
|
263
256
|
# the marshalling of the compiler mutex and instantiating that again on unmarshalling.
|
264
257
|
def marshal_dump # :nodoc:
|
265
|
-
[ @source, @identifier, @handler, @compiled, @
|
258
|
+
[ @source, @identifier, @handler, @compiled, @locals, @virtual_path, @updated_at, @format, @variant ]
|
266
259
|
end
|
267
260
|
|
268
261
|
def marshal_load(array) # :nodoc:
|
269
|
-
@source, @identifier, @handler, @compiled, @
|
262
|
+
@source, @identifier, @handler, @compiled, @locals, @virtual_path, @updated_at, @format, @variant = *array
|
270
263
|
@compile_mutex = Mutex.new
|
271
264
|
end
|
272
265
|
|
@@ -292,9 +285,6 @@ module ActionView
|
|
292
285
|
compile(mod)
|
293
286
|
end
|
294
287
|
|
295
|
-
# Just discard the source if we have a virtual path. This
|
296
|
-
# means we can get the template back.
|
297
|
-
@source = nil if @virtual_path
|
298
288
|
@compiled = true
|
299
289
|
end
|
300
290
|
end
|
@@ -326,6 +316,7 @@ module ActionView
|
|
326
316
|
|
327
317
|
# Make sure that the resulting String to be eval'd is in the
|
328
318
|
# encoding of the code
|
319
|
+
original_source = source
|
329
320
|
source = +<<-end_src
|
330
321
|
def #{method_name}(local_assigns, output_buffer)
|
331
322
|
@virtual_path = #{@virtual_path.inspect};#{locals_code};#{code}
|
@@ -346,7 +337,14 @@ module ActionView
|
|
346
337
|
raise WrongEncodingError.new(source, Encoding.default_internal)
|
347
338
|
end
|
348
339
|
|
349
|
-
|
340
|
+
begin
|
341
|
+
mod.module_eval(source, identifier, 0)
|
342
|
+
rescue SyntaxError
|
343
|
+
# Account for when code in the template is not syntactically valid; e.g. if we're using
|
344
|
+
# ERB and the user writes <%= foo( %>, attempting to call a helper `foo` and interpolate
|
345
|
+
# the result into the template, but missing an end parenthesis.
|
346
|
+
raise SyntaxErrorInTemplate.new(self, original_source)
|
347
|
+
end
|
350
348
|
end
|
351
349
|
|
352
350
|
def handle_render_error(view, e)
|
@@ -354,12 +352,7 @@ module ActionView
|
|
354
352
|
e.sub_template_of(self)
|
355
353
|
raise e
|
356
354
|
else
|
357
|
-
|
358
|
-
unless template.source
|
359
|
-
template = refresh(view)
|
360
|
-
template.encode!
|
361
|
-
end
|
362
|
-
raise Template::Error.new(template)
|
355
|
+
raise Template::Error.new(self)
|
363
356
|
end
|
364
357
|
end
|
365
358
|
|
@@ -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
|