actionview 6.0.0.beta1 → 6.1.4
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 +273 -119
- data/MIT-LICENSE +1 -1
- data/README.rdoc +5 -3
- data/lib/action_view/base.rb +81 -15
- data/lib/action_view/cache_expiry.rb +52 -0
- data/lib/action_view/context.rb +0 -5
- data/lib/action_view/dependency_tracker.rb +10 -4
- data/lib/action_view/digestor.rb +11 -19
- data/lib/action_view/flows.rb +0 -1
- data/lib/action_view/gem_version.rb +3 -3
- data/lib/action_view/helpers/active_model_helper.rb +0 -1
- data/lib/action_view/helpers/asset_tag_helper.rb +62 -22
- data/lib/action_view/helpers/asset_url_helper.rb +6 -4
- data/lib/action_view/helpers/atom_feed_helper.rb +2 -1
- data/lib/action_view/helpers/cache_helper.rb +16 -23
- data/lib/action_view/helpers/csp_helper.rb +4 -2
- data/lib/action_view/helpers/date_helper.rb +5 -6
- data/lib/action_view/helpers/form_helper.rb +70 -34
- data/lib/action_view/helpers/form_options_helper.rb +10 -18
- data/lib/action_view/helpers/form_tag_helper.rb +12 -9
- data/lib/action_view/helpers/javascript_helper.rb +7 -5
- data/lib/action_view/helpers/number_helper.rb +9 -8
- data/lib/action_view/helpers/output_safety_helper.rb +1 -1
- data/lib/action_view/helpers/rendering_helper.rb +17 -7
- data/lib/action_view/helpers/sanitize_helper.rb +10 -16
- data/lib/action_view/helpers/tag_helper.rb +94 -19
- data/lib/action_view/helpers/tags/base.rb +10 -7
- data/lib/action_view/helpers/tags/check_box.rb +0 -1
- data/lib/action_view/helpers/tags/collection_check_boxes.rb +0 -1
- data/lib/action_view/helpers/tags/collection_helpers.rb +0 -1
- data/lib/action_view/helpers/tags/collection_radio_buttons.rb +0 -1
- data/lib/action_view/helpers/tags/color_field.rb +0 -1
- data/lib/action_view/helpers/tags/date_field.rb +1 -2
- data/lib/action_view/helpers/tags/date_select.rb +2 -3
- data/lib/action_view/helpers/tags/datetime_field.rb +0 -1
- data/lib/action_view/helpers/tags/datetime_local_field.rb +1 -2
- data/lib/action_view/helpers/tags/label.rb +4 -1
- data/lib/action_view/helpers/tags/month_field.rb +1 -2
- data/lib/action_view/helpers/tags/radio_button.rb +0 -1
- data/lib/action_view/helpers/tags/select.rb +1 -2
- data/lib/action_view/helpers/tags/text_field.rb +0 -1
- data/lib/action_view/helpers/tags/time_field.rb +1 -2
- data/lib/action_view/helpers/tags/week_field.rb +1 -2
- data/lib/action_view/helpers/text_helper.rb +2 -3
- data/lib/action_view/helpers/translation_helper.rb +98 -51
- data/lib/action_view/helpers/url_helper.rb +124 -16
- data/lib/action_view/layouts.rb +8 -10
- data/lib/action_view/log_subscriber.rb +26 -11
- data/lib/action_view/lookup_context.rb +59 -31
- data/lib/action_view/path_set.rb +3 -12
- data/lib/action_view/railtie.rb +39 -41
- data/lib/action_view/record_identifier.rb +0 -1
- data/lib/action_view/renderer/abstract_renderer.rb +142 -11
- data/lib/action_view/renderer/collection_renderer.rb +196 -0
- data/lib/action_view/renderer/object_renderer.rb +34 -0
- data/lib/action_view/renderer/partial_renderer/collection_caching.rb +35 -29
- data/lib/action_view/renderer/partial_renderer.rb +21 -273
- data/lib/action_view/renderer/renderer.rb +59 -4
- data/lib/action_view/renderer/streaming_template_renderer.rb +9 -7
- data/lib/action_view/renderer/template_renderer.rb +35 -27
- data/lib/action_view/rendering.rb +49 -29
- data/lib/action_view/routing_url_for.rb +1 -1
- data/lib/action_view/template/error.rb +30 -15
- data/lib/action_view/template/handlers/builder.rb +2 -2
- data/lib/action_view/template/handlers/erb/erubi.rb +15 -9
- data/lib/action_view/template/handlers/erb.rb +14 -19
- data/lib/action_view/template/handlers/html.rb +1 -1
- data/lib/action_view/template/handlers/raw.rb +2 -2
- data/lib/action_view/template/handlers.rb +1 -1
- data/lib/action_view/template/html.rb +5 -6
- data/lib/action_view/template/inline.rb +22 -0
- data/lib/action_view/template/raw_file.rb +25 -0
- data/lib/action_view/template/renderable.rb +24 -0
- data/lib/action_view/template/resolver.rb +141 -140
- data/lib/action_view/template/sources/file.rb +17 -0
- data/lib/action_view/template/sources.rb +13 -0
- data/lib/action_view/template/text.rb +2 -3
- data/lib/action_view/template.rb +49 -75
- data/lib/action_view/test_case.rb +20 -28
- data/lib/action_view/testing/resolvers.rb +18 -27
- data/lib/action_view/unbound_template.rb +31 -0
- data/lib/action_view/view_paths.rb +59 -38
- data/lib/action_view.rb +7 -2
- data/lib/assets/compiled/rails-ujs.js +25 -16
- metadata +30 -18
@@ -4,12 +4,11 @@ module ActionView #:nodoc:
|
|
4
4
|
# = Action View HTML Template
|
5
5
|
class Template #:nodoc:
|
6
6
|
class HTML #:nodoc:
|
7
|
-
|
7
|
+
attr_reader :type
|
8
8
|
|
9
|
-
def initialize(string, type
|
9
|
+
def initialize(string, type)
|
10
10
|
@string = string.to_s
|
11
|
-
@type =
|
12
|
-
@type ||= Types[:html]
|
11
|
+
@type = type
|
13
12
|
end
|
14
13
|
|
15
14
|
def identifier
|
@@ -26,8 +25,8 @@ module ActionView #:nodoc:
|
|
26
25
|
to_str
|
27
26
|
end
|
28
27
|
|
29
|
-
def
|
30
|
-
|
28
|
+
def format
|
29
|
+
@type
|
31
30
|
end
|
32
31
|
end
|
33
32
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActionView #:nodoc:
|
4
|
+
class Template #:nodoc:
|
5
|
+
class Inline < Template #:nodoc:
|
6
|
+
# This finalizer is needed (and exactly with a proc inside another proc)
|
7
|
+
# otherwise templates leak in development.
|
8
|
+
Finalizer = proc do |method_name, mod| # :nodoc:
|
9
|
+
proc do
|
10
|
+
mod.module_eval do
|
11
|
+
remove_possible_method method_name
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def compile(mod)
|
17
|
+
super
|
18
|
+
ObjectSpace.define_finalizer(self, Finalizer[method_name, mod])
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,25 @@
|
|
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
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActionView
|
4
|
+
# = Action View Renderable Template for objects that respond to #render_in
|
5
|
+
class Template
|
6
|
+
class Renderable # :nodoc:
|
7
|
+
def initialize(renderable)
|
8
|
+
@renderable = renderable
|
9
|
+
end
|
10
|
+
|
11
|
+
def identifier
|
12
|
+
@renderable.class.name
|
13
|
+
end
|
14
|
+
|
15
|
+
def render(context, *args)
|
16
|
+
@renderable.render_in(context)
|
17
|
+
end
|
18
|
+
|
19
|
+
def format
|
20
|
+
@renderable.format
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -35,6 +35,41 @@ module ActionView
|
|
35
35
|
alias :to_s :to_str
|
36
36
|
end
|
37
37
|
|
38
|
+
class PathParser # :nodoc:
|
39
|
+
def build_path_regex
|
40
|
+
handlers = Template::Handlers.extensions.map { |x| Regexp.escape(x) }.join("|")
|
41
|
+
formats = Template::Types.symbols.map { |x| Regexp.escape(x) }.join("|")
|
42
|
+
locales = "[a-z]{2}(?:-[A-Z]{2})?"
|
43
|
+
variants = "[^.]*"
|
44
|
+
|
45
|
+
%r{
|
46
|
+
\A
|
47
|
+
(?:(?<prefix>.*)/)?
|
48
|
+
(?<partial>_)?
|
49
|
+
(?<action>.*?)
|
50
|
+
(?:\.(?<locale>#{locales}))??
|
51
|
+
(?:\.(?<format>#{formats}))??
|
52
|
+
(?:\+(?<variant>#{variants}))??
|
53
|
+
(?:\.(?<handler>#{handlers}))?
|
54
|
+
\z
|
55
|
+
}x
|
56
|
+
end
|
57
|
+
|
58
|
+
def parse(path)
|
59
|
+
@regex ||= build_path_regex
|
60
|
+
match = @regex.match(path)
|
61
|
+
{
|
62
|
+
prefix: match[:prefix] || "",
|
63
|
+
action: match[:action],
|
64
|
+
partial: !!match[:partial],
|
65
|
+
locale: match[:locale]&.to_sym,
|
66
|
+
handler: match[:handler]&.to_sym,
|
67
|
+
format: match[:format]&.to_sym,
|
68
|
+
variant: match[:variant]
|
69
|
+
}
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
38
73
|
# Threadsafe template cache
|
39
74
|
class Cache #:nodoc:
|
40
75
|
class SmallCache < Concurrent::Map
|
@@ -43,13 +78,13 @@ module ActionView
|
|
43
78
|
end
|
44
79
|
end
|
45
80
|
|
46
|
-
#
|
81
|
+
# Preallocate all the default blocks for performance/memory consumption reasons
|
47
82
|
PARTIAL_BLOCK = lambda { |cache, partial| cache[partial] = SmallCache.new }
|
48
83
|
PREFIX_BLOCK = lambda { |cache, prefix| cache[prefix] = SmallCache.new(&PARTIAL_BLOCK) }
|
49
84
|
NAME_BLOCK = lambda { |cache, name| cache[name] = SmallCache.new(&PREFIX_BLOCK) }
|
50
85
|
KEY_BLOCK = lambda { |cache, key| cache[key] = SmallCache.new(&NAME_BLOCK) }
|
51
86
|
|
52
|
-
#
|
87
|
+
# Usually a majority of template look ups return nothing, use this canonical preallocated array to save memory
|
53
88
|
NO_TEMPLATES = [].freeze
|
54
89
|
|
55
90
|
def initialize
|
@@ -58,31 +93,16 @@ module ActionView
|
|
58
93
|
end
|
59
94
|
|
60
95
|
def inspect
|
61
|
-
"
|
96
|
+
"#{to_s[0..-2]} keys=#{@data.size} queries=#{@query_cache.size}>"
|
62
97
|
end
|
63
98
|
|
64
99
|
# Cache the templates returned by the block
|
65
100
|
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
|
101
|
+
@data[key][name][prefix][partial][locals] ||= canonical_no_templates(yield)
|
78
102
|
end
|
79
103
|
|
80
104
|
def cache_query(query) # :nodoc:
|
81
|
-
|
82
|
-
@query_cache[query] ||= canonical_no_templates(yield)
|
83
|
-
else
|
84
|
-
yield
|
85
|
-
end
|
105
|
+
@query_cache[query] ||= canonical_no_templates(yield)
|
86
106
|
end
|
87
107
|
|
88
108
|
def clear
|
@@ -90,7 +110,7 @@ module ActionView
|
|
90
110
|
@query_cache.clear
|
91
111
|
end
|
92
112
|
|
93
|
-
# Get the cache size.
|
113
|
+
# Get the cache size. Do not call this
|
94
114
|
# method. This method is not guaranteed to be here ever.
|
95
115
|
def size # :nodoc:
|
96
116
|
size = 0
|
@@ -108,23 +128,9 @@ module ActionView
|
|
108
128
|
end
|
109
129
|
|
110
130
|
private
|
111
|
-
|
112
131
|
def canonical_no_templates(templates)
|
113
132
|
templates.empty? ? NO_TEMPLATES : templates
|
114
133
|
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
134
|
end
|
129
135
|
|
130
136
|
cattr_accessor :caching, default: true
|
@@ -143,14 +149,10 @@ module ActionView
|
|
143
149
|
|
144
150
|
# Normalizes the arguments and passes it on to find_templates.
|
145
151
|
def find_all(name, prefix = nil, partial = false, details = {}, key = nil, locals = [])
|
146
|
-
|
147
|
-
find_templates(name, prefix, partial, details)
|
148
|
-
end
|
149
|
-
end
|
152
|
+
locals = locals.map(&:to_s).sort!.freeze
|
150
153
|
|
151
|
-
def find_all_anywhere(name, prefix, partial = false, details = {}, key = nil, locals = [])
|
152
154
|
cached(key, [name, prefix, partial], details, locals) do
|
153
|
-
|
155
|
+
_find_all(name, prefix, partial, details, key, locals)
|
154
156
|
end
|
155
157
|
end
|
156
158
|
|
@@ -159,19 +161,17 @@ module ActionView
|
|
159
161
|
end
|
160
162
|
|
161
163
|
private
|
164
|
+
def _find_all(name, prefix, partial, details, key, locals)
|
165
|
+
find_templates(name, prefix, partial, details, locals)
|
166
|
+
end
|
162
167
|
|
163
168
|
delegate :caching?, to: :class
|
164
169
|
|
165
170
|
# This is what child classes implement. No defaults are needed
|
166
171
|
# because Resolver guarantees that the arguments are present and
|
167
172
|
# 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)
|
173
|
+
def find_templates(name, prefix, partial, details, locals = [])
|
174
|
+
raise NotImplementedError, "Subclasses must implement a find_templates(name, prefix, partial, details, locals = []) method"
|
175
175
|
end
|
176
176
|
|
177
177
|
# Handles templates caching. If a key is given and caching is on
|
@@ -180,25 +180,13 @@ module ActionView
|
|
180
180
|
# resolver is fresher before returning it.
|
181
181
|
def cached(key, path_info, details, locals)
|
182
182
|
name, prefix, partial = path_info
|
183
|
-
locals = locals.map(&:to_s).sort!
|
184
183
|
|
185
184
|
if key
|
186
185
|
@cache.cache(key, name, prefix, partial, locals) do
|
187
|
-
|
186
|
+
yield
|
188
187
|
end
|
189
188
|
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.formats = details[:formats] || [:html] if t.formats.empty?
|
200
|
-
t.variants = details[:variants] || [] if t.variants.empty?
|
201
|
-
t.virtual_path ||= (cached ||= build_path(*path_info))
|
189
|
+
yield
|
202
190
|
end
|
203
191
|
end
|
204
192
|
end
|
@@ -208,40 +196,70 @@ module ActionView
|
|
208
196
|
EXTENSIONS = { locale: ".", formats: ".", variants: "+", handlers: "." }
|
209
197
|
DEFAULT_PATTERN = ":prefix/:action{.:locale,}{.:formats,}{+:variants,}{.:handlers,}"
|
210
198
|
|
211
|
-
def initialize
|
212
|
-
@pattern =
|
213
|
-
|
199
|
+
def initialize
|
200
|
+
@pattern = DEFAULT_PATTERN
|
201
|
+
@unbound_templates = Concurrent::Map.new
|
202
|
+
@path_parser = PathParser.new
|
203
|
+
super
|
214
204
|
end
|
215
205
|
|
216
|
-
|
206
|
+
def clear_cache
|
207
|
+
@unbound_templates.clear
|
208
|
+
@path_parser = PathParser.new
|
209
|
+
super
|
210
|
+
end
|
217
211
|
|
218
|
-
|
212
|
+
private
|
213
|
+
def _find_all(name, prefix, partial, details, key, locals)
|
219
214
|
path = Path.build(name, prefix, partial)
|
220
|
-
query(path, details, details[:formats],
|
215
|
+
query(path, details, details[:formats], locals, cache: !!key)
|
221
216
|
end
|
222
217
|
|
223
|
-
def query(path, details, formats,
|
218
|
+
def query(path, details, formats, locals, cache:)
|
224
219
|
template_paths = find_template_paths_from_details(path, details)
|
225
|
-
template_paths = reject_files_external_to_app(template_paths)
|
220
|
+
template_paths = reject_files_external_to_app(template_paths)
|
226
221
|
|
227
222
|
template_paths.map do |template|
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
223
|
+
unbound_template =
|
224
|
+
if cache
|
225
|
+
@unbound_templates.compute_if_absent([template, path.virtual]) do
|
226
|
+
build_unbound_template(template, path.virtual)
|
227
|
+
end
|
228
|
+
else
|
229
|
+
build_unbound_template(template, path.virtual)
|
230
|
+
end
|
231
|
+
|
232
|
+
unbound_template.bind_locals(locals)
|
237
233
|
end
|
238
234
|
end
|
239
235
|
|
236
|
+
def source_for_template(template)
|
237
|
+
Template::Sources::File.new(template)
|
238
|
+
end
|
239
|
+
|
240
|
+
def build_unbound_template(template, virtual_path)
|
241
|
+
handler, format, variant = extract_handler_and_format_and_variant(template)
|
242
|
+
source = source_for_template(template)
|
243
|
+
|
244
|
+
UnboundTemplate.new(
|
245
|
+
source,
|
246
|
+
template,
|
247
|
+
handler,
|
248
|
+
virtual_path: virtual_path,
|
249
|
+
format: format,
|
250
|
+
variant: variant,
|
251
|
+
)
|
252
|
+
end
|
253
|
+
|
240
254
|
def reject_files_external_to_app(files)
|
241
255
|
files.reject { |filename| !inside_path?(@path, filename) }
|
242
256
|
end
|
243
257
|
|
244
258
|
def find_template_paths_from_details(path, details)
|
259
|
+
if path.name.include?(".")
|
260
|
+
ActiveSupport::Deprecation.warn("Rendering actions with '.' in the name is deprecated: #{path}")
|
261
|
+
end
|
262
|
+
|
245
263
|
query = build_query(path, details)
|
246
264
|
find_template_paths(query)
|
247
265
|
end
|
@@ -268,7 +286,7 @@ module ActionView
|
|
268
286
|
query.gsub!(/:prefix(\/)?/, prefix)
|
269
287
|
|
270
288
|
partial = escape_entry(path.partial? ? "_#{path.name}" : path.name)
|
271
|
-
query.gsub!(
|
289
|
+
query.gsub!(":action", partial)
|
272
290
|
|
273
291
|
details.each do |ext, candidates|
|
274
292
|
if ext == :variants && candidates == :any
|
@@ -285,70 +303,28 @@ module ActionView
|
|
285
303
|
entry.gsub(/[*?{}\[\]]/, '\\\\\\&')
|
286
304
|
end
|
287
305
|
|
288
|
-
# Returns the file mtime from the filesystem.
|
289
|
-
def mtime(p)
|
290
|
-
File.mtime(p)
|
291
|
-
end
|
292
|
-
|
293
306
|
# Extract handler, formats and variant from path. If a format cannot be found neither
|
294
307
|
# from the path, or the handler, we should return the array of formats given
|
295
308
|
# to the resolver.
|
296
309
|
def extract_handler_and_format_and_variant(path)
|
297
|
-
|
298
|
-
pieces.shift
|
299
|
-
|
300
|
-
extension = pieces.pop
|
310
|
+
details = @path_parser.parse(path)
|
301
311
|
|
302
|
-
handler = Template.handler_for_extension(
|
303
|
-
format
|
304
|
-
|
312
|
+
handler = Template.handler_for_extension(details[:handler])
|
313
|
+
format = details[:format] || handler.try(:default_format)
|
314
|
+
variant = details[:variant]
|
305
315
|
|
316
|
+
# Template::Types[format] and handler.default_format can return nil
|
306
317
|
[handler, format, variant]
|
307
318
|
end
|
308
319
|
end
|
309
320
|
|
310
|
-
# A resolver that loads files from the filesystem.
|
311
|
-
# resolving pattern. Such pattern can be a glob string supported by some variables.
|
312
|
-
#
|
313
|
-
# ==== Examples
|
314
|
-
#
|
315
|
-
# Default pattern, loads views the same way as previous versions of rails, eg. when you're
|
316
|
-
# looking for <tt>users/new</tt> it will produce query glob: <tt>users/new{.{en},}{.{html,js},}{.{erb,haml},}</tt>
|
317
|
-
#
|
318
|
-
# FileSystemResolver.new("/path/to/views", ":prefix/:action{.:locale,}{.:formats,}{+:variants,}{.:handlers,}")
|
319
|
-
#
|
320
|
-
# This one allows you to keep files with different formats in separate subdirectories,
|
321
|
-
# eg. <tt>users/new.html</tt> will be loaded from <tt>users/html/new.erb</tt> or <tt>users/new.html.erb</tt>,
|
322
|
-
# <tt>users/new.js</tt> from <tt>users/js/new.erb</tt> or <tt>users/new.js.erb</tt>, etc.
|
323
|
-
#
|
324
|
-
# FileSystemResolver.new("/path/to/views", ":prefix/{:formats/,}:action{.:locale,}{.:formats,}{+:variants,}{.:handlers,}")
|
325
|
-
#
|
326
|
-
# If you don't specify a pattern then the default will be used.
|
327
|
-
#
|
328
|
-
# In order to use any of the customized resolvers above in a Rails application, you just need
|
329
|
-
# to configure ActionController::Base.view_paths in an initializer, for example:
|
330
|
-
#
|
331
|
-
# ActionController::Base.view_paths = FileSystemResolver.new(
|
332
|
-
# Rails.root.join("app/views"),
|
333
|
-
# ":prefix/:action{.:locale,}{.:formats,}{+:variants,}{.:handlers,}",
|
334
|
-
# )
|
335
|
-
#
|
336
|
-
# ==== Pattern format and variables
|
337
|
-
#
|
338
|
-
# Pattern has to be a valid glob string, and it allows you to use the
|
339
|
-
# following variables:
|
340
|
-
#
|
341
|
-
# * <tt>:prefix</tt> - usually the controller path
|
342
|
-
# * <tt>:action</tt> - name of the action
|
343
|
-
# * <tt>:locale</tt> - possible locale versions
|
344
|
-
# * <tt>:formats</tt> - possible request formats (for example html, json, xml...)
|
345
|
-
# * <tt>:variants</tt> - possible request variants (for example phone, tablet...)
|
346
|
-
# * <tt>:handlers</tt> - possible handlers (for example erb, haml, builder...)
|
347
|
-
#
|
321
|
+
# A resolver that loads files from the filesystem.
|
348
322
|
class FileSystemResolver < PathResolver
|
349
|
-
|
323
|
+
attr_reader :path
|
324
|
+
|
325
|
+
def initialize(path)
|
350
326
|
raise ArgumentError, "path already is a Resolver class" if path.is_a?(Resolver)
|
351
|
-
super(
|
327
|
+
super()
|
352
328
|
@path = File.expand_path(path)
|
353
329
|
end
|
354
330
|
|
@@ -365,16 +341,32 @@ module ActionView
|
|
365
341
|
|
366
342
|
# An Optimized resolver for Rails' most common case.
|
367
343
|
class OptimizedFileSystemResolver < FileSystemResolver #:nodoc:
|
368
|
-
|
344
|
+
def initialize(path)
|
345
|
+
super(path)
|
346
|
+
end
|
369
347
|
|
370
|
-
|
348
|
+
private
|
349
|
+
def find_candidate_template_paths(path)
|
371
350
|
# Instead of checking for every possible path, as our other globs would
|
372
351
|
# do, scan the directory for files with the right prefix.
|
373
352
|
query = "#{escape_entry(File.join(@path, path))}*"
|
374
353
|
|
354
|
+
Dir[query].reject do |filename|
|
355
|
+
File.directory?(filename)
|
356
|
+
end
|
357
|
+
end
|
358
|
+
|
359
|
+
def find_template_paths_from_details(path, details)
|
360
|
+
if path.name.include?(".")
|
361
|
+
# Fall back to the unoptimized resolver, which will warn
|
362
|
+
return super
|
363
|
+
end
|
364
|
+
|
365
|
+
candidates = find_candidate_template_paths(path)
|
366
|
+
|
375
367
|
regex = build_regex(path, details)
|
376
368
|
|
377
|
-
|
369
|
+
candidates.uniq.reject do |filename|
|
378
370
|
# This regex match does double duty of finding only files which match
|
379
371
|
# details (instead of just matching the prefix) and also filtering for
|
380
372
|
# case-insensitive file systems.
|
@@ -386,7 +378,7 @@ module ActionView
|
|
386
378
|
# We can use the matches found by the regex and sort by their index in
|
387
379
|
# details.
|
388
380
|
match = filename.match(regex)
|
389
|
-
EXTENSIONS.keys.
|
381
|
+
EXTENSIONS.keys.map do |ext|
|
390
382
|
if ext == :variants && details[ext] == :any
|
391
383
|
match[ext].nil? ? 0 : 1
|
392
384
|
elsif match[ext].nil?
|
@@ -401,13 +393,16 @@ module ActionView
|
|
401
393
|
end
|
402
394
|
|
403
395
|
def build_regex(path, details)
|
404
|
-
query =
|
396
|
+
query = Regexp.escape(File.join(@path, path))
|
405
397
|
exts = EXTENSIONS.map do |ext, prefix|
|
406
398
|
match =
|
407
399
|
if ext == :variants && details[ext] == :any
|
408
400
|
".*?"
|
409
401
|
else
|
410
|
-
details[ext].compact
|
402
|
+
arr = details[ext].compact
|
403
|
+
arr.uniq!
|
404
|
+
arr.map! { |e| Regexp.escape(e) }
|
405
|
+
arr.join("|")
|
411
406
|
end
|
412
407
|
prefix = Regexp.escape(prefix)
|
413
408
|
"(#{prefix}(?<#{ext}>#{match}))?"
|
@@ -420,12 +415,18 @@ module ActionView
|
|
420
415
|
# The same as FileSystemResolver but does not allow templates to store
|
421
416
|
# a virtual path since it is invalid for such resolvers.
|
422
417
|
class FallbackFileSystemResolver < FileSystemResolver #:nodoc:
|
418
|
+
private_class_method :new
|
419
|
+
|
423
420
|
def self.instances
|
424
421
|
[new(""), new("/")]
|
425
422
|
end
|
426
423
|
|
427
|
-
def
|
428
|
-
super
|
424
|
+
def build_unbound_template(template, _)
|
425
|
+
super(template, nil)
|
426
|
+
end
|
427
|
+
|
428
|
+
def reject_files_external_to_app(files)
|
429
|
+
files
|
429
430
|
end
|
430
431
|
end
|
431
432
|
end
|
@@ -8,7 +8,6 @@ module ActionView #:nodoc:
|
|
8
8
|
|
9
9
|
def initialize(string)
|
10
10
|
@string = string.to_s
|
11
|
-
@type = Types[:text]
|
12
11
|
end
|
13
12
|
|
14
13
|
def identifier
|
@@ -25,8 +24,8 @@ module ActionView #:nodoc:
|
|
25
24
|
to_str
|
26
25
|
end
|
27
26
|
|
28
|
-
def
|
29
|
-
|
27
|
+
def format
|
28
|
+
:text
|
30
29
|
end
|
31
30
|
end
|
32
31
|
end
|