actionview 4.2.11.1 → 6.0.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 +201 -192
- data/MIT-LICENSE +1 -1
- data/README.rdoc +9 -8
- data/lib/action_view/base.rb +144 -37
- data/lib/action_view/buffers.rb +18 -1
- data/lib/action_view/cache_expiry.rb +53 -0
- data/lib/action_view/context.rb +8 -12
- data/lib/action_view/dependency_tracker.rb +54 -20
- data/lib/action_view/digestor.rb +88 -85
- data/lib/action_view/flows.rb +11 -12
- data/lib/action_view/gem_version.rb +6 -4
- data/lib/action_view/helpers/active_model_helper.rb +16 -11
- data/lib/action_view/helpers/asset_tag_helper.rb +241 -82
- data/lib/action_view/helpers/asset_url_helper.rb +171 -67
- data/lib/action_view/helpers/atom_feed_helper.rb +19 -17
- data/lib/action_view/helpers/cache_helper.rb +112 -42
- data/lib/action_view/helpers/capture_helper.rb +20 -13
- data/lib/action_view/helpers/controller_helper.rb +15 -4
- data/lib/action_view/helpers/csp_helper.rb +26 -0
- data/lib/action_view/helpers/csrf_helper.rb +8 -6
- data/lib/action_view/helpers/date_helper.rb +230 -129
- data/lib/action_view/helpers/debug_helper.rb +7 -6
- data/lib/action_view/helpers/form_helper.rb +755 -129
- data/lib/action_view/helpers/form_options_helper.rb +130 -75
- data/lib/action_view/helpers/form_tag_helper.rb +116 -71
- data/lib/action_view/helpers/javascript_helper.rb +30 -14
- data/lib/action_view/helpers/number_helper.rb +84 -59
- data/lib/action_view/helpers/output_safety_helper.rb +36 -4
- data/lib/action_view/helpers/rendering_helper.rb +11 -8
- data/lib/action_view/helpers/sanitize_helper.rb +30 -31
- data/lib/action_view/helpers/tag_helper.rb +201 -75
- data/lib/action_view/helpers/tags/base.rb +138 -98
- data/lib/action_view/helpers/tags/check_box.rb +20 -19
- data/lib/action_view/helpers/tags/checkable.rb +4 -2
- data/lib/action_view/helpers/tags/collection_check_boxes.rb +12 -34
- data/lib/action_view/helpers/tags/collection_helpers.rb +69 -36
- data/lib/action_view/helpers/tags/collection_radio_buttons.rb +6 -12
- data/lib/action_view/helpers/tags/collection_select.rb +4 -2
- data/lib/action_view/helpers/tags/color_field.rb +4 -3
- data/lib/action_view/helpers/tags/date_field.rb +2 -1
- data/lib/action_view/helpers/tags/date_select.rb +37 -36
- data/lib/action_view/helpers/tags/datetime_field.rb +4 -3
- data/lib/action_view/helpers/tags/datetime_local_field.rb +2 -1
- data/lib/action_view/helpers/tags/datetime_select.rb +2 -0
- data/lib/action_view/helpers/tags/email_field.rb +2 -0
- data/lib/action_view/helpers/tags/file_field.rb +2 -0
- data/lib/action_view/helpers/tags/grouped_collection_select.rb +4 -2
- data/lib/action_view/helpers/tags/hidden_field.rb +2 -0
- data/lib/action_view/helpers/tags/label.rb +3 -2
- data/lib/action_view/helpers/tags/month_field.rb +2 -1
- data/lib/action_view/helpers/tags/number_field.rb +2 -0
- data/lib/action_view/helpers/tags/password_field.rb +3 -1
- data/lib/action_view/helpers/tags/placeholderable.rb +3 -1
- data/lib/action_view/helpers/tags/radio_button.rb +7 -6
- data/lib/action_view/helpers/tags/range_field.rb +2 -0
- data/lib/action_view/helpers/tags/search_field.rb +14 -9
- data/lib/action_view/helpers/tags/select.rb +11 -10
- data/lib/action_view/helpers/tags/tel_field.rb +2 -0
- data/lib/action_view/helpers/tags/text_area.rb +4 -2
- data/lib/action_view/helpers/tags/text_field.rb +8 -8
- data/lib/action_view/helpers/tags/time_field.rb +2 -1
- data/lib/action_view/helpers/tags/time_select.rb +2 -0
- data/lib/action_view/helpers/tags/time_zone_select.rb +3 -1
- data/lib/action_view/helpers/tags/translator.rb +15 -16
- data/lib/action_view/helpers/tags/url_field.rb +2 -0
- data/lib/action_view/helpers/tags/week_field.rb +2 -1
- data/lib/action_view/helpers/tags.rb +3 -1
- data/lib/action_view/helpers/text_helper.rb +56 -38
- data/lib/action_view/helpers/translation_helper.rb +91 -47
- data/lib/action_view/helpers/url_helper.rb +160 -105
- data/lib/action_view/helpers.rb +5 -3
- data/lib/action_view/layouts.rb +65 -61
- data/lib/action_view/log_subscriber.rb +61 -10
- data/lib/action_view/lookup_context.rb +147 -89
- data/lib/action_view/model_naming.rb +3 -1
- data/lib/action_view/path_set.rb +28 -23
- data/lib/action_view/railtie.rb +62 -6
- data/lib/action_view/record_identifier.rb +53 -26
- data/lib/action_view/renderer/abstract_renderer.rb +71 -13
- data/lib/action_view/renderer/partial_renderer/collection_caching.rb +103 -0
- data/lib/action_view/renderer/partial_renderer.rb +239 -225
- data/lib/action_view/renderer/renderer.rb +22 -8
- data/lib/action_view/renderer/streaming_template_renderer.rb +54 -54
- data/lib/action_view/renderer/template_renderer.rb +79 -73
- data/lib/action_view/rendering.rb +68 -44
- data/lib/action_view/routing_url_for.rb +33 -22
- data/lib/action_view/tasks/cache_digests.rake +25 -0
- data/lib/action_view/template/error.rb +44 -29
- data/lib/action_view/template/handlers/builder.rb +12 -13
- data/lib/action_view/template/handlers/erb/erubi.rb +87 -0
- data/lib/action_view/template/handlers/erb.rb +24 -86
- data/lib/action_view/template/handlers/html.rb +11 -0
- data/lib/action_view/template/handlers/raw.rb +4 -4
- data/lib/action_view/template/handlers.rb +38 -8
- data/lib/action_view/template/html.rb +19 -10
- data/lib/action_view/template/inline.rb +22 -0
- data/lib/action_view/template/raw_file.rb +28 -0
- data/lib/action_view/template/resolver.rb +217 -193
- 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 +11 -10
- data/lib/action_view/template/types.rb +18 -18
- data/lib/action_view/template.rb +146 -90
- data/lib/action_view/test_case.rb +52 -32
- data/lib/action_view/testing/resolvers.rb +46 -34
- data/lib/action_view/unbound_template.rb +31 -0
- data/lib/action_view/version.rb +3 -1
- data/lib/action_view/view_paths.rb +48 -31
- data/lib/action_view.rb +11 -8
- data/lib/assets/compiled/rails-ujs.js +746 -0
- metadata +38 -29
- data/lib/action_view/helpers/record_tag_helper.rb +0 -108
- data/lib/action_view/tasks/dependencies.rake +0 -23
@@ -1,10 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "pathname"
|
2
4
|
require "active_support/core_ext/class"
|
3
5
|
require "active_support/core_ext/module/attribute_accessors"
|
4
|
-
require 'active_support/core_ext/string/filters'
|
5
6
|
require "action_view/template"
|
6
7
|
require "thread"
|
7
|
-
require "
|
8
|
+
require "concurrent/map"
|
8
9
|
|
9
10
|
module ActionView
|
10
11
|
# = Action View Resolver
|
@@ -15,7 +16,7 @@ module ActionView
|
|
15
16
|
alias_method :partial?, :partial
|
16
17
|
|
17
18
|
def self.build(name, prefix, partial)
|
18
|
-
virtual = ""
|
19
|
+
virtual = +""
|
19
20
|
virtual << "#{prefix}/" unless prefix.empty?
|
20
21
|
virtual << (partial ? "_#{name}" : name)
|
21
22
|
new name, prefix, partial, virtual
|
@@ -36,67 +37,68 @@ module ActionView
|
|
36
37
|
|
37
38
|
# Threadsafe template cache
|
38
39
|
class Cache #:nodoc:
|
39
|
-
class SmallCache <
|
40
|
+
class SmallCache < Concurrent::Map
|
40
41
|
def initialize(options = {})
|
41
|
-
super(options.merge(:
|
42
|
+
super(options.merge(initial_capacity: 2))
|
42
43
|
end
|
43
44
|
end
|
44
45
|
|
45
46
|
# preallocate all the default blocks for performance/memory consumption reasons
|
46
|
-
PARTIAL_BLOCK = lambda {|cache, partial| cache[partial] = SmallCache.new}
|
47
|
-
PREFIX_BLOCK = lambda {|cache, prefix| cache[prefix] = SmallCache.new(&PARTIAL_BLOCK)}
|
48
|
-
NAME_BLOCK = lambda {|cache, name| cache[name] = SmallCache.new(&PREFIX_BLOCK)}
|
49
|
-
KEY_BLOCK = lambda {|cache, key| cache[key] = SmallCache.new(&NAME_BLOCK)}
|
47
|
+
PARTIAL_BLOCK = lambda { |cache, partial| cache[partial] = SmallCache.new }
|
48
|
+
PREFIX_BLOCK = lambda { |cache, prefix| cache[prefix] = SmallCache.new(&PARTIAL_BLOCK) }
|
49
|
+
NAME_BLOCK = lambda { |cache, name| cache[name] = SmallCache.new(&PREFIX_BLOCK) }
|
50
|
+
KEY_BLOCK = lambda { |cache, key| cache[key] = SmallCache.new(&NAME_BLOCK) }
|
50
51
|
|
51
52
|
# usually a majority of template look ups return nothing, use this canonical preallocated array to save memory
|
52
53
|
NO_TEMPLATES = [].freeze
|
53
54
|
|
54
55
|
def initialize
|
55
56
|
@data = SmallCache.new(&KEY_BLOCK)
|
57
|
+
@query_cache = SmallCache.new
|
58
|
+
end
|
59
|
+
|
60
|
+
def inspect
|
61
|
+
"#<#{self.class.name}:0x#{(object_id << 1).to_s(16)} keys=#{@data.size} queries=#{@query_cache.size}>"
|
56
62
|
end
|
57
63
|
|
58
64
|
# Cache the templates returned by the block
|
59
65
|
def cache(key, name, prefix, partial, locals)
|
60
|
-
|
61
|
-
|
62
|
-
else
|
63
|
-
fresh_templates = yield
|
64
|
-
cached_templates = @data[key][name][prefix][partial][locals]
|
66
|
+
@data[key][name][prefix][partial][locals] ||= canonical_no_templates(yield)
|
67
|
+
end
|
65
68
|
|
66
|
-
|
67
|
-
|
68
|
-
else
|
69
|
-
cached_templates || NO_TEMPLATES
|
70
|
-
end
|
71
|
-
end
|
69
|
+
def cache_query(query) # :nodoc:
|
70
|
+
@query_cache[query] ||= canonical_no_templates(yield)
|
72
71
|
end
|
73
72
|
|
74
73
|
def clear
|
75
74
|
@data.clear
|
75
|
+
@query_cache.clear
|
76
76
|
end
|
77
77
|
|
78
|
-
|
78
|
+
# Get the cache size. Do not call this
|
79
|
+
# method. This method is not guaranteed to be here ever.
|
80
|
+
def size # :nodoc:
|
81
|
+
size = 0
|
82
|
+
@data.each_value do |v1|
|
83
|
+
v1.each_value do |v2|
|
84
|
+
v2.each_value do |v3|
|
85
|
+
v3.each_value do |v4|
|
86
|
+
size += v4.size
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
79
91
|
|
80
|
-
|
81
|
-
templates.empty? ? NO_TEMPLATES : templates
|
92
|
+
size + @query_cache.size
|
82
93
|
end
|
83
94
|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
if cached_templates.blank? || fresh_templates.blank?
|
88
|
-
return fresh_templates.blank? != cached_templates.blank?
|
95
|
+
private
|
96
|
+
def canonical_no_templates(templates)
|
97
|
+
templates.empty? ? NO_TEMPLATES : templates
|
89
98
|
end
|
90
|
-
|
91
|
-
cached_templates_max_updated_at = cached_templates.map(&:updated_at).max
|
92
|
-
|
93
|
-
# if a template has changed, it will be now be newer than all the cached templates
|
94
|
-
fresh_templates.any? { |t| t.updated_at > cached_templates_max_updated_at }
|
95
|
-
end
|
96
99
|
end
|
97
100
|
|
98
|
-
cattr_accessor :caching
|
99
|
-
self.caching = true
|
101
|
+
cattr_accessor :caching, default: true
|
100
102
|
|
101
103
|
class << self
|
102
104
|
alias :caching? :caching
|
@@ -111,218 +113,190 @@ module ActionView
|
|
111
113
|
end
|
112
114
|
|
113
115
|
# Normalizes the arguments and passes it on to find_templates.
|
114
|
-
def find_all(name, prefix=nil, partial=false, details={}, key=nil, locals=[])
|
116
|
+
def find_all(name, prefix = nil, partial = false, details = {}, key = nil, locals = [])
|
117
|
+
locals = locals.map(&:to_s).sort!.freeze
|
118
|
+
|
115
119
|
cached(key, [name, prefix, partial], details, locals) do
|
116
|
-
|
120
|
+
_find_all(name, prefix, partial, details, key, locals)
|
117
121
|
end
|
118
122
|
end
|
119
123
|
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
+
alias :find_all_anywhere :find_all
|
125
|
+
deprecate :find_all_anywhere
|
126
|
+
|
127
|
+
def find_all_with_query(query) # :nodoc:
|
128
|
+
@cache.cache_query(query) { find_template_paths(File.join(@path, query)) }
|
124
129
|
end
|
125
130
|
|
126
131
|
private
|
132
|
+
def _find_all(name, prefix, partial, details, key, locals)
|
133
|
+
find_templates(name, prefix, partial, details, locals)
|
134
|
+
end
|
127
135
|
|
128
136
|
delegate :caching?, to: :class
|
129
137
|
|
130
138
|
# This is what child classes implement. No defaults are needed
|
131
139
|
# because Resolver guarantees that the arguments are present and
|
132
140
|
# normalized.
|
133
|
-
def find_templates(name, prefix, partial, details,
|
134
|
-
raise NotImplementedError, "Subclasses must implement a find_templates(name, prefix, partial, details,
|
135
|
-
end
|
136
|
-
|
137
|
-
# Helpers that builds a path. Useful for building virtual paths.
|
138
|
-
def build_path(name, prefix, partial)
|
139
|
-
Path.build(name, prefix, partial)
|
141
|
+
def find_templates(name, prefix, partial, details, locals = [])
|
142
|
+
raise NotImplementedError, "Subclasses must implement a find_templates(name, prefix, partial, details, locals = []) method"
|
140
143
|
end
|
141
144
|
|
142
145
|
# Handles templates caching. If a key is given and caching is on
|
143
146
|
# always check the cache before hitting the resolver. Otherwise,
|
144
147
|
# it always hits the resolver but if the key is present, check if the
|
145
148
|
# resolver is fresher before returning it.
|
146
|
-
def cached(key, path_info, details, locals)
|
149
|
+
def cached(key, path_info, details, locals)
|
147
150
|
name, prefix, partial = path_info
|
148
|
-
locals = locals.map { |x| x.to_s }.sort!
|
149
151
|
|
150
152
|
if key
|
151
153
|
@cache.cache(key, name, prefix, partial, locals) do
|
152
|
-
|
154
|
+
yield
|
153
155
|
end
|
154
156
|
else
|
155
|
-
|
156
|
-
end
|
157
|
-
end
|
158
|
-
|
159
|
-
# Ensures all the resolver information is set in the template.
|
160
|
-
def decorate(templates, path_info, details, locals) #:nodoc:
|
161
|
-
cached = nil
|
162
|
-
templates.each do |t|
|
163
|
-
t.locals = locals
|
164
|
-
t.formats = details[:formats] || [:html] if t.formats.empty?
|
165
|
-
t.variants = details[:variants] || [] if t.variants.empty?
|
166
|
-
t.virtual_path ||= (cached ||= build_path(*path_info))
|
157
|
+
yield
|
167
158
|
end
|
168
159
|
end
|
169
160
|
end
|
170
161
|
|
171
162
|
# An abstract class that implements a Resolver with path semantics.
|
172
163
|
class PathResolver < Resolver #:nodoc:
|
173
|
-
EXTENSIONS = { :
|
164
|
+
EXTENSIONS = { locale: ".", formats: ".", variants: "+", handlers: "." }
|
174
165
|
DEFAULT_PATTERN = ":prefix/:action{.:locale,}{.:formats,}{+:variants,}{.:handlers,}"
|
175
166
|
|
176
|
-
def initialize(pattern=nil)
|
177
|
-
|
167
|
+
def initialize(pattern = nil)
|
168
|
+
if pattern
|
169
|
+
ActiveSupport::Deprecation.warn "Specifying a custom path for #{self.class} is deprecated. Implement a custom Resolver subclass instead."
|
170
|
+
@pattern = pattern
|
171
|
+
else
|
172
|
+
@pattern = DEFAULT_PATTERN
|
173
|
+
end
|
174
|
+
@unbound_templates = Concurrent::Map.new
|
178
175
|
super()
|
179
176
|
end
|
180
177
|
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
path = Path.build(name, prefix, partial)
|
185
|
-
query(path, details, details[:formats], outside_app_allowed)
|
178
|
+
def clear_cache
|
179
|
+
@unbound_templates.clear
|
180
|
+
super()
|
186
181
|
end
|
187
182
|
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
183
|
+
private
|
184
|
+
def _find_all(name, prefix, partial, details, key, locals)
|
185
|
+
path = Path.build(name, prefix, partial)
|
186
|
+
query(path, details, details[:formats], locals, cache: !!key)
|
187
|
+
end
|
193
188
|
|
194
|
-
|
195
|
-
|
196
|
-
|
189
|
+
def query(path, details, formats, locals, cache:)
|
190
|
+
template_paths = find_template_paths_from_details(path, details)
|
191
|
+
template_paths = reject_files_external_to_app(template_paths)
|
192
|
+
|
193
|
+
template_paths.map do |template|
|
194
|
+
unbound_template =
|
195
|
+
if cache
|
196
|
+
@unbound_templates.compute_if_absent([template, path.virtual]) do
|
197
|
+
build_unbound_template(template, path.virtual)
|
198
|
+
end
|
199
|
+
else
|
200
|
+
build_unbound_template(template, path.virtual)
|
201
|
+
end
|
202
|
+
|
203
|
+
unbound_template.bind_locals(locals)
|
204
|
+
end
|
205
|
+
end
|
197
206
|
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
207
|
+
def build_unbound_template(template, virtual_path)
|
208
|
+
handler, format, variant = extract_handler_and_format_and_variant(template)
|
209
|
+
source = Template::Sources::File.new(template)
|
210
|
+
|
211
|
+
UnboundTemplate.new(
|
212
|
+
source,
|
213
|
+
template,
|
214
|
+
handler,
|
215
|
+
virtual_path: virtual_path,
|
216
|
+
format: format,
|
217
|
+
variant: variant,
|
203
218
|
)
|
204
|
-
|
205
|
-
end
|
219
|
+
end
|
206
220
|
|
207
|
-
|
208
|
-
|
209
|
-
|
221
|
+
def reject_files_external_to_app(files)
|
222
|
+
files.reject { |filename| !inside_path?(@path, filename) }
|
223
|
+
end
|
224
|
+
|
225
|
+
def find_template_paths_from_details(path, details)
|
226
|
+
query = build_query(path, details)
|
227
|
+
find_template_paths(query)
|
228
|
+
end
|
210
229
|
|
211
|
-
if RUBY_VERSION >= '2.2.0'
|
212
230
|
def find_template_paths(query)
|
213
|
-
Dir[query].reject
|
231
|
+
Dir[query].uniq.reject do |filename|
|
214
232
|
File.directory?(filename) ||
|
215
233
|
# deals with case-insensitive file systems.
|
216
234
|
!File.fnmatch(query, filename, File::FNM_EXTGLOB)
|
217
|
-
|
235
|
+
end
|
218
236
|
end
|
219
|
-
else
|
220
|
-
def find_template_paths(query)
|
221
|
-
# deals with case-insensitive file systems.
|
222
|
-
sanitizer = Hash.new { |h,dir| h[dir] = Dir["#{dir}/*"] }
|
223
237
|
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
238
|
+
def inside_path?(path, filename)
|
239
|
+
filename = File.expand_path(filename)
|
240
|
+
path = File.join(path, "")
|
241
|
+
filename.start_with?(path)
|
228
242
|
end
|
229
|
-
end
|
230
243
|
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
filename.start_with?(path)
|
235
|
-
end
|
244
|
+
# Helper for building query glob string based on resolver's pattern.
|
245
|
+
def build_query(path, details)
|
246
|
+
query = @pattern.dup
|
236
247
|
|
237
|
-
|
238
|
-
|
239
|
-
query = @pattern.dup
|
248
|
+
prefix = path.prefix.empty? ? "" : "#{escape_entry(path.prefix)}\\1"
|
249
|
+
query.gsub!(/:prefix(\/)?/, prefix)
|
240
250
|
|
241
|
-
|
242
|
-
|
251
|
+
partial = escape_entry(path.partial? ? "_#{path.name}" : path.name)
|
252
|
+
query.gsub!(/:action/, partial)
|
243
253
|
|
244
|
-
|
245
|
-
|
254
|
+
details.each do |ext, candidates|
|
255
|
+
if ext == :variants && candidates == :any
|
256
|
+
query.gsub!(/:#{ext}/, "*")
|
257
|
+
else
|
258
|
+
query.gsub!(/:#{ext}/, "{#{candidates.compact.uniq.join(',')}}")
|
259
|
+
end
|
260
|
+
end
|
246
261
|
|
247
|
-
|
248
|
-
query.gsub!(/\:#{ext}/, "{#{variants.compact.uniq.join(',')}}")
|
262
|
+
File.expand_path(query, @path)
|
249
263
|
end
|
250
264
|
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
def escape_entry(entry)
|
255
|
-
entry.gsub(/[*?{}\[\]]/, '\\\\\\&')
|
256
|
-
end
|
265
|
+
def escape_entry(entry)
|
266
|
+
entry.gsub(/[*?{}\[\]]/, '\\\\\\&')
|
267
|
+
end
|
257
268
|
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
269
|
+
# Extract handler, formats and variant from path. If a format cannot be found neither
|
270
|
+
# from the path, or the handler, we should return the array of formats given
|
271
|
+
# to the resolver.
|
272
|
+
def extract_handler_and_format_and_variant(path)
|
273
|
+
pieces = File.basename(path).split(".")
|
274
|
+
pieces.shift
|
262
275
|
|
263
|
-
|
264
|
-
# from the path, or the handler, we should return the array of formats given
|
265
|
-
# to the resolver.
|
266
|
-
def extract_handler_and_format_and_variant(path, default_formats)
|
267
|
-
pieces = File.basename(path).split(".")
|
268
|
-
pieces.shift
|
269
|
-
|
270
|
-
extension = pieces.pop
|
271
|
-
unless extension
|
272
|
-
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
273
|
-
The file #{path} did not specify a template handler. The default is
|
274
|
-
currently ERB, but will change to RAW in the future.
|
275
|
-
MSG
|
276
|
-
end
|
276
|
+
extension = pieces.pop
|
277
277
|
|
278
|
-
|
279
|
-
|
280
|
-
|
278
|
+
handler = Template.handler_for_extension(extension)
|
279
|
+
format, variant = pieces.last.split(EXTENSIONS[:variants], 2) if pieces.last
|
280
|
+
format = if format
|
281
|
+
Template::Types[format]&.ref
|
282
|
+
else
|
283
|
+
if handler.respond_to?(:default_format) # default_format can return nil
|
284
|
+
handler.default_format
|
285
|
+
else
|
286
|
+
nil
|
287
|
+
end
|
288
|
+
end
|
281
289
|
|
282
|
-
|
283
|
-
|
290
|
+
# Template::Types[format] and handler.default_format can return nil
|
291
|
+
[handler, format, variant]
|
292
|
+
end
|
284
293
|
end
|
285
294
|
|
286
|
-
# A resolver that loads files from the filesystem.
|
287
|
-
# resolving pattern. Such pattern can be a glob string supported by some variables.
|
288
|
-
#
|
289
|
-
# ==== Examples
|
290
|
-
#
|
291
|
-
# Default pattern, loads views the same way as previous versions of rails, eg. when you're
|
292
|
-
# looking for `users/new` it will produce query glob: `users/new{.{en},}{.{html,js},}{.{erb,haml},}`
|
293
|
-
#
|
294
|
-
# FileSystemResolver.new("/path/to/views", ":prefix/:action{.:locale,}{.:formats,}{+:variants,}{.:handlers,}")
|
295
|
-
#
|
296
|
-
# This one allows you to keep files with different formats in separate subdirectories,
|
297
|
-
# eg. `users/new.html` will be loaded from `users/html/new.erb` or `users/new.html.erb`,
|
298
|
-
# `users/new.js` from `users/js/new.erb` or `users/new.js.erb`, etc.
|
299
|
-
#
|
300
|
-
# FileSystemResolver.new("/path/to/views", ":prefix/{:formats/,}:action{.:locale,}{.:formats,}{+:variants,}{.:handlers,}")
|
301
|
-
#
|
302
|
-
# If you don't specify a pattern then the default will be used.
|
303
|
-
#
|
304
|
-
# In order to use any of the customized resolvers above in a Rails application, you just need
|
305
|
-
# to configure ActionController::Base.view_paths in an initializer, for example:
|
306
|
-
#
|
307
|
-
# ActionController::Base.view_paths = FileSystemResolver.new(
|
308
|
-
# Rails.root.join("app/views"),
|
309
|
-
# ":prefix{/:locale}/:action{.:formats,}{+:variants,}{.:handlers,}"
|
310
|
-
# )
|
311
|
-
#
|
312
|
-
# ==== Pattern format and variables
|
313
|
-
#
|
314
|
-
# Pattern has to be a valid glob string, and it allows you to use the
|
315
|
-
# following variables:
|
316
|
-
#
|
317
|
-
# * <tt>:prefix</tt> - usually the controller path
|
318
|
-
# * <tt>:action</tt> - name of the action
|
319
|
-
# * <tt>:locale</tt> - possible locale versions
|
320
|
-
# * <tt>:formats</tt> - possible request formats (for example html, json, xml...)
|
321
|
-
# * <tt>:variants</tt> - possible request variants (for example phone, tablet...)
|
322
|
-
# * <tt>:handlers</tt> - possible handlers (for example erb, haml, builder...)
|
323
|
-
#
|
295
|
+
# A resolver that loads files from the filesystem.
|
324
296
|
class FileSystemResolver < PathResolver
|
325
|
-
|
297
|
+
attr_reader :path
|
298
|
+
|
299
|
+
def initialize(path, pattern = nil)
|
326
300
|
raise ArgumentError, "path already is a Resolver class" if path.is_a?(Resolver)
|
327
301
|
super(pattern)
|
328
302
|
@path = File.expand_path(path)
|
@@ -341,26 +315,76 @@ module ActionView
|
|
341
315
|
|
342
316
|
# An Optimized resolver for Rails' most common case.
|
343
317
|
class OptimizedFileSystemResolver < FileSystemResolver #:nodoc:
|
344
|
-
def
|
345
|
-
|
318
|
+
def initialize(path)
|
319
|
+
super(path)
|
320
|
+
end
|
346
321
|
|
347
|
-
|
348
|
-
|
349
|
-
|
322
|
+
private
|
323
|
+
def find_template_paths_from_details(path, details)
|
324
|
+
# Instead of checking for every possible path, as our other globs would
|
325
|
+
# do, scan the directory for files with the right prefix.
|
326
|
+
query = "#{escape_entry(File.join(@path, path))}*"
|
327
|
+
|
328
|
+
regex = build_regex(path, details)
|
329
|
+
|
330
|
+
Dir[query].uniq.reject do |filename|
|
331
|
+
# This regex match does double duty of finding only files which match
|
332
|
+
# details (instead of just matching the prefix) and also filtering for
|
333
|
+
# case-insensitive file systems.
|
334
|
+
!regex.match?(filename) ||
|
335
|
+
File.directory?(filename)
|
336
|
+
end.sort_by do |filename|
|
337
|
+
# Because we scanned the directory, instead of checking for files
|
338
|
+
# one-by-one, they will be returned in an arbitrary order.
|
339
|
+
# We can use the matches found by the regex and sort by their index in
|
340
|
+
# details.
|
341
|
+
match = filename.match(regex)
|
342
|
+
EXTENSIONS.keys.reverse.map do |ext|
|
343
|
+
if ext == :variants && details[ext] == :any
|
344
|
+
match[ext].nil? ? 0 : 1
|
345
|
+
elsif match[ext].nil?
|
346
|
+
# No match should be last
|
347
|
+
details[ext].length
|
348
|
+
else
|
349
|
+
found = match[ext].to_sym
|
350
|
+
details[ext].index(found)
|
351
|
+
end
|
352
|
+
end
|
353
|
+
end
|
354
|
+
end
|
350
355
|
|
351
|
-
|
352
|
-
|
356
|
+
def build_regex(path, details)
|
357
|
+
query = Regexp.escape(File.join(@path, path))
|
358
|
+
exts = EXTENSIONS.map do |ext, prefix|
|
359
|
+
match =
|
360
|
+
if ext == :variants && details[ext] == :any
|
361
|
+
".*?"
|
362
|
+
else
|
363
|
+
details[ext].compact.uniq.map { |e| Regexp.escape(e) }.join("|")
|
364
|
+
end
|
365
|
+
prefix = Regexp.escape(prefix)
|
366
|
+
"(#{prefix}(?<#{ext}>#{match}))?"
|
367
|
+
end.join
|
368
|
+
|
369
|
+
%r{\A#{query}#{exts}\z}
|
370
|
+
end
|
353
371
|
end
|
354
372
|
|
355
373
|
# The same as FileSystemResolver but does not allow templates to store
|
356
374
|
# a virtual path since it is invalid for such resolvers.
|
357
375
|
class FallbackFileSystemResolver < FileSystemResolver #:nodoc:
|
376
|
+
private_class_method :new
|
377
|
+
|
358
378
|
def self.instances
|
359
379
|
[new(""), new("/")]
|
360
380
|
end
|
361
381
|
|
362
|
-
def
|
363
|
-
super
|
382
|
+
def build_unbound_template(template, _)
|
383
|
+
super(template, nil)
|
384
|
+
end
|
385
|
+
|
386
|
+
def reject_files_external_to_app(files)
|
387
|
+
files
|
364
388
|
end
|
365
389
|
end
|
366
390
|
end
|
@@ -1,22 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActionView #:nodoc:
|
2
4
|
# = Action View Text Template
|
3
|
-
class Template
|
5
|
+
class Template #:nodoc:
|
4
6
|
class Text #:nodoc:
|
5
7
|
attr_accessor :type
|
6
8
|
|
7
|
-
def initialize(string
|
9
|
+
def initialize(string)
|
8
10
|
@string = string.to_s
|
9
|
-
@type = Types[type] || type if type
|
10
|
-
@type ||= Types[:text]
|
11
11
|
end
|
12
12
|
|
13
13
|
def identifier
|
14
|
-
|
14
|
+
"text template"
|
15
15
|
end
|
16
16
|
|
17
|
-
|
18
|
-
'text template'
|
19
|
-
end
|
17
|
+
alias_method :inspect, :identifier
|
20
18
|
|
21
19
|
def to_str
|
22
20
|
@string
|
@@ -26,9 +24,12 @@ module ActionView #:nodoc:
|
|
26
24
|
to_str
|
27
25
|
end
|
28
26
|
|
29
|
-
def
|
30
|
-
|
27
|
+
def format
|
28
|
+
:text
|
31
29
|
end
|
30
|
+
|
31
|
+
def formats; Array(format); end
|
32
|
+
deprecate :formats
|
32
33
|
end
|
33
34
|
end
|
34
35
|
end
|