actionview 4.2.11.1 → 7.0.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of actionview might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +229 -215
- data/MIT-LICENSE +1 -1
- data/README.rdoc +9 -8
- data/lib/action_view/base.rb +116 -43
- data/lib/action_view/buffers.rb +20 -3
- data/lib/action_view/cache_expiry.rb +66 -0
- data/lib/action_view/context.rb +8 -12
- data/lib/action_view/dependency_tracker/erb_tracker.rb +154 -0
- data/lib/action_view/dependency_tracker/ripper_tracker.rb +59 -0
- data/lib/action_view/dependency_tracker.rb +21 -122
- data/lib/action_view/digestor.rb +92 -85
- data/lib/action_view/flows.rb +15 -16
- data/lib/action_view/gem_version.rb +6 -4
- data/lib/action_view/helpers/active_model_helper.rb +17 -12
- data/lib/action_view/helpers/asset_tag_helper.rb +356 -101
- data/lib/action_view/helpers/asset_url_helper.rb +180 -74
- data/lib/action_view/helpers/atom_feed_helper.rb +21 -19
- data/lib/action_view/helpers/cache_helper.rb +156 -43
- data/lib/action_view/helpers/capture_helper.rb +21 -14
- data/lib/action_view/helpers/controller_helper.rb +16 -5
- 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 +288 -132
- data/lib/action_view/helpers/debug_helper.rb +9 -6
- data/lib/action_view/helpers/form_helper.rb +956 -173
- data/lib/action_view/helpers/form_options_helper.rb +178 -97
- data/lib/action_view/helpers/form_tag_helper.rb +220 -101
- data/lib/action_view/helpers/javascript_helper.rb +33 -19
- data/lib/action_view/helpers/number_helper.rb +88 -63
- data/lib/action_view/helpers/output_safety_helper.rb +38 -6
- data/lib/action_view/helpers/rendering_helper.rb +21 -10
- data/lib/action_view/helpers/sanitize_helper.rb +31 -32
- data/lib/action_view/helpers/tag_helper.rb +332 -71
- data/lib/action_view/helpers/tags/base.rb +123 -99
- data/lib/action_view/helpers/tags/check_box.rb +21 -20
- 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 +5 -3
- data/lib/action_view/helpers/tags/color_field.rb +4 -3
- data/lib/action_view/helpers/tags/date_field.rb +3 -2
- data/lib/action_view/helpers/tags/date_select.rb +38 -37
- data/lib/action_view/helpers/tags/datetime_field.rb +4 -3
- data/lib/action_view/helpers/tags/datetime_local_field.rb +3 -2
- 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 +18 -0
- data/lib/action_view/helpers/tags/grouped_collection_select.rb +4 -2
- data/lib/action_view/helpers/tags/hidden_field.rb +6 -0
- data/lib/action_view/helpers/tags/label.rb +7 -2
- data/lib/action_view/helpers/tags/month_field.rb +3 -2
- 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 +12 -2
- 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 +3 -2
- data/lib/action_view/helpers/tags/weekday_select.rb +28 -0
- data/lib/action_view/helpers/tags.rb +5 -2
- data/lib/action_view/helpers/text_helper.rb +80 -51
- data/lib/action_view/helpers/translation_helper.rb +120 -69
- data/lib/action_view/helpers/url_helper.rb +398 -171
- data/lib/action_view/helpers.rb +29 -27
- data/lib/action_view/layouts.rb +68 -63
- data/lib/action_view/log_subscriber.rb +77 -10
- data/lib/action_view/lookup_context.rb +137 -113
- data/lib/action_view/model_naming.rb +4 -2
- data/lib/action_view/path_set.rb +28 -32
- data/lib/action_view/railtie.rb +74 -13
- data/lib/action_view/record_identifier.rb +53 -26
- data/lib/action_view/render_parser.rb +188 -0
- data/lib/action_view/renderer/abstract_renderer.rb +152 -15
- 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 +102 -0
- data/lib/action_view/renderer/partial_renderer.rb +51 -333
- data/lib/action_view/renderer/renderer.rb +68 -11
- data/lib/action_view/renderer/streaming_template_renderer.rb +60 -56
- data/lib/action_view/renderer/template_renderer.rb +87 -74
- data/lib/action_view/rendering.rb +73 -47
- data/lib/action_view/ripper_ast_parser.rb +198 -0
- data/lib/action_view/routing_url_for.rb +35 -24
- data/lib/action_view/tasks/cache_digests.rake +25 -0
- data/lib/action_view/template/error.rb +151 -41
- data/lib/action_view/template/handlers/builder.rb +12 -13
- data/lib/action_view/template/handlers/erb/erubi.rb +89 -0
- data/lib/action_view/template/handlers/erb.rb +29 -89
- 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 +14 -10
- data/lib/action_view/template/html.rb +12 -13
- 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 +139 -300
- 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 +10 -12
- data/lib/action_view/template/types.rb +28 -26
- data/lib/action_view/template.rb +123 -91
- data/lib/action_view/template_details.rb +66 -0
- data/lib/action_view/template_path.rb +64 -0
- data/lib/action_view/test_case.rb +70 -53
- data/lib/action_view/testing/resolvers.rb +25 -35
- data/lib/action_view/unbound_template.rb +57 -0
- data/lib/action_view/version.rb +3 -1
- data/lib/action_view/view_paths.rb +73 -58
- data/lib/action_view.rb +16 -11
- data/lib/assets/compiled/rails-ujs.js +746 -0
- metadata +52 -32
- data/lib/action_view/helpers/record_tag_helper.rb +0 -108
- data/lib/action_view/tasks/dependencies.rake +0 -23
@@ -1,366 +1,205 @@
|
|
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
|
11
12
|
class Resolver
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
NAME_BLOCK = lambda {|cache, name| cache[name] = SmallCache.new(&PREFIX_BLOCK)}
|
49
|
-
KEY_BLOCK = lambda {|cache, key| cache[key] = SmallCache.new(&NAME_BLOCK)}
|
50
|
-
|
51
|
-
# usually a majority of template look ups return nothing, use this canonical preallocated array to save memory
|
52
|
-
NO_TEMPLATES = [].freeze
|
53
|
-
|
54
|
-
def initialize
|
55
|
-
@data = SmallCache.new(&KEY_BLOCK)
|
56
|
-
end
|
57
|
-
|
58
|
-
# Cache the templates returned by the block
|
59
|
-
def cache(key, name, prefix, partial, locals)
|
60
|
-
if Resolver.caching?
|
61
|
-
@data[key][name][prefix][partial][locals] ||= canonical_no_templates(yield)
|
62
|
-
else
|
63
|
-
fresh_templates = yield
|
64
|
-
cached_templates = @data[key][name][prefix][partial][locals]
|
65
|
-
|
66
|
-
if templates_have_changed?(cached_templates, fresh_templates)
|
67
|
-
@data[key][name][prefix][partial][locals] = canonical_no_templates(fresh_templates)
|
68
|
-
else
|
69
|
-
cached_templates || NO_TEMPLATES
|
70
|
-
end
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
def clear
|
75
|
-
@data.clear
|
76
|
-
end
|
77
|
-
|
78
|
-
private
|
79
|
-
|
80
|
-
def canonical_no_templates(templates)
|
81
|
-
templates.empty? ? NO_TEMPLATES : templates
|
82
|
-
end
|
83
|
-
|
84
|
-
def templates_have_changed?(cached_templates, fresh_templates)
|
85
|
-
# if either the old or new template list is empty, we don't need to (and can't)
|
86
|
-
# compare modification times, and instead just check whether the lists are different
|
87
|
-
if cached_templates.blank? || fresh_templates.blank?
|
88
|
-
return fresh_templates.blank? != cached_templates.blank?
|
89
|
-
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 }
|
13
|
+
Path = ActionView::TemplatePath
|
14
|
+
deprecate_constant :Path
|
15
|
+
|
16
|
+
class PathParser # :nodoc:
|
17
|
+
ParsedPath = Struct.new(:path, :details)
|
18
|
+
|
19
|
+
def build_path_regex
|
20
|
+
handlers = Template::Handlers.extensions.map { |x| Regexp.escape(x) }.join("|")
|
21
|
+
formats = Template::Types.symbols.map { |x| Regexp.escape(x) }.join("|")
|
22
|
+
locales = "[a-z]{2}(?:-[A-Z]{2})?"
|
23
|
+
variants = "[^.]*"
|
24
|
+
|
25
|
+
%r{
|
26
|
+
\A
|
27
|
+
(?:(?<prefix>.*)/)?
|
28
|
+
(?<partial>_)?
|
29
|
+
(?<action>.*?)
|
30
|
+
(?:\.(?<locale>#{locales}))??
|
31
|
+
(?:\.(?<format>#{formats}))??
|
32
|
+
(?:\+(?<variant>#{variants}))??
|
33
|
+
(?:\.(?<handler>#{handlers}))?
|
34
|
+
\z
|
35
|
+
}x
|
36
|
+
end
|
37
|
+
|
38
|
+
def parse(path)
|
39
|
+
@regex ||= build_path_regex
|
40
|
+
match = @regex.match(path)
|
41
|
+
path = TemplatePath.build(match[:action], match[:prefix] || "", !!match[:partial])
|
42
|
+
details = TemplateDetails.new(
|
43
|
+
match[:locale]&.to_sym,
|
44
|
+
match[:handler]&.to_sym,
|
45
|
+
match[:format]&.to_sym,
|
46
|
+
match[:variant]&.to_sym
|
47
|
+
)
|
48
|
+
ParsedPath.new(path, details)
|
95
49
|
end
|
96
50
|
end
|
97
51
|
|
98
|
-
cattr_accessor :caching
|
99
|
-
self.caching = true
|
52
|
+
cattr_accessor :caching, default: true
|
100
53
|
|
101
54
|
class << self
|
102
55
|
alias :caching? :caching
|
103
56
|
end
|
104
57
|
|
105
|
-
def initialize
|
106
|
-
@cache = Cache.new
|
107
|
-
end
|
108
|
-
|
109
58
|
def clear_cache
|
110
|
-
@cache.clear
|
111
59
|
end
|
112
60
|
|
113
61
|
# Normalizes the arguments and passes it on to find_templates.
|
114
|
-
def find_all(name, prefix=nil, partial=false, details={}, key=nil, locals=[])
|
115
|
-
|
116
|
-
find_templates(name, prefix, partial, details, false)
|
117
|
-
end
|
62
|
+
def find_all(name, prefix = nil, partial = false, details = {}, key = nil, locals = [])
|
63
|
+
_find_all(name, prefix, partial, details, key, locals)
|
118
64
|
end
|
119
65
|
|
120
|
-
def
|
121
|
-
|
122
|
-
|
123
|
-
end
|
66
|
+
def all_template_paths # :nodoc:
|
67
|
+
# Not implemented by default
|
68
|
+
[]
|
124
69
|
end
|
125
70
|
|
126
71
|
private
|
72
|
+
def _find_all(name, prefix, partial, details, key, locals)
|
73
|
+
find_templates(name, prefix, partial, details, locals)
|
74
|
+
end
|
127
75
|
|
128
76
|
delegate :caching?, to: :class
|
129
77
|
|
130
78
|
# This is what child classes implement. No defaults are needed
|
131
79
|
# because Resolver guarantees that the arguments are present and
|
132
80
|
# 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)
|
140
|
-
end
|
141
|
-
|
142
|
-
# Handles templates caching. If a key is given and caching is on
|
143
|
-
# always check the cache before hitting the resolver. Otherwise,
|
144
|
-
# it always hits the resolver but if the key is present, check if the
|
145
|
-
# resolver is fresher before returning it.
|
146
|
-
def cached(key, path_info, details, locals) #:nodoc:
|
147
|
-
name, prefix, partial = path_info
|
148
|
-
locals = locals.map { |x| x.to_s }.sort!
|
149
|
-
|
150
|
-
if key
|
151
|
-
@cache.cache(key, name, prefix, partial, locals) do
|
152
|
-
decorate(yield, path_info, details, locals)
|
153
|
-
end
|
154
|
-
else
|
155
|
-
decorate(yield, path_info, details, locals)
|
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))
|
167
|
-
end
|
81
|
+
def find_templates(name, prefix, partial, details, locals = [])
|
82
|
+
raise NotImplementedError, "Subclasses must implement a find_templates(name, prefix, partial, details, locals = []) method"
|
168
83
|
end
|
169
84
|
end
|
170
85
|
|
171
|
-
#
|
172
|
-
class
|
173
|
-
|
174
|
-
DEFAULT_PATTERN = ":prefix/:action{.:locale,}{.:formats,}{+:variants,}{.:handlers,}"
|
86
|
+
# A resolver that loads files from the filesystem.
|
87
|
+
class FileSystemResolver < Resolver
|
88
|
+
attr_reader :path
|
175
89
|
|
176
|
-
def initialize(
|
177
|
-
|
90
|
+
def initialize(path)
|
91
|
+
raise ArgumentError, "path already is a Resolver class" if path.is_a?(Resolver)
|
92
|
+
@unbound_templates = Concurrent::Map.new
|
93
|
+
@path_parser = PathParser.new
|
94
|
+
@path = File.expand_path(path)
|
178
95
|
super()
|
179
96
|
end
|
180
97
|
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
query(path, details, details[:formats], outside_app_allowed)
|
98
|
+
def clear_cache
|
99
|
+
@unbound_templates.clear
|
100
|
+
@path_parser = PathParser.new
|
101
|
+
super
|
186
102
|
end
|
187
103
|
|
188
|
-
def
|
189
|
-
|
190
|
-
|
191
|
-
template_paths = find_template_paths query
|
192
|
-
template_paths = reject_files_external_to_app(template_paths) unless outside_app_allowed
|
193
|
-
|
194
|
-
template_paths.map { |template|
|
195
|
-
handler, format, variant = extract_handler_and_format_and_variant(template, formats)
|
196
|
-
contents = File.binread(template)
|
197
|
-
|
198
|
-
Template.new(contents, File.expand_path(template), handler,
|
199
|
-
:virtual_path => path.virtual,
|
200
|
-
:format => format,
|
201
|
-
:variant => variant,
|
202
|
-
:updated_at => mtime(template)
|
203
|
-
)
|
204
|
-
}
|
104
|
+
def to_s
|
105
|
+
@path.to_s
|
205
106
|
end
|
107
|
+
alias :to_path :to_s
|
206
108
|
|
207
|
-
def
|
208
|
-
|
109
|
+
def eql?(resolver)
|
110
|
+
self.class.equal?(resolver.class) && to_path == resolver.to_path
|
209
111
|
end
|
112
|
+
alias :== :eql?
|
210
113
|
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
}
|
114
|
+
def all_template_paths # :nodoc:
|
115
|
+
paths = template_glob("**/*")
|
116
|
+
paths.map do |filename|
|
117
|
+
filename.from(@path.size + 1).remove(/\.[^\/]*\z/)
|
118
|
+
end.uniq.map do |filename|
|
119
|
+
TemplatePath.parse(filename)
|
218
120
|
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
|
-
|
224
|
-
Dir[query].reject { |filename|
|
225
|
-
File.directory?(filename) ||
|
226
|
-
!sanitizer[File.dirname(filename)].include?(filename)
|
227
|
-
}
|
228
|
-
end
|
229
|
-
end
|
230
|
-
|
231
|
-
def inside_path?(path, filename)
|
232
|
-
filename = File.expand_path(filename)
|
233
|
-
path = File.join(path, '')
|
234
|
-
filename.start_with?(path)
|
235
121
|
end
|
236
122
|
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
123
|
+
private
|
124
|
+
def _find_all(name, prefix, partial, details, key, locals)
|
125
|
+
requested_details = key || TemplateDetails::Requested.new(**details)
|
126
|
+
cache = key ? @unbound_templates : Concurrent::Map.new
|
127
|
+
|
128
|
+
unbound_templates =
|
129
|
+
cache.compute_if_absent(TemplatePath.virtual(name, prefix, partial)) do
|
130
|
+
path = TemplatePath.build(name, prefix, partial)
|
131
|
+
unbound_templates_from_path(path)
|
132
|
+
end
|
246
133
|
|
247
|
-
|
248
|
-
|
134
|
+
filter_and_sort_by_details(unbound_templates, requested_details).map do |unbound_template|
|
135
|
+
unbound_template.bind_locals(locals)
|
136
|
+
end
|
249
137
|
end
|
250
138
|
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
def escape_entry(entry)
|
255
|
-
entry.gsub(/[*?{}\[\]]/, '\\\\\\&')
|
256
|
-
end
|
139
|
+
def source_for_template(template)
|
140
|
+
Template::Sources::File.new(template)
|
141
|
+
end
|
257
142
|
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
143
|
+
def build_unbound_template(template)
|
144
|
+
parsed = @path_parser.parse(template.from(@path.size + 1))
|
145
|
+
details = parsed.details
|
146
|
+
source = source_for_template(template)
|
262
147
|
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
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
|
148
|
+
UnboundTemplate.new(
|
149
|
+
source,
|
150
|
+
template,
|
151
|
+
details: details,
|
152
|
+
virtual_path: parsed.path.virtual,
|
153
|
+
)
|
276
154
|
end
|
277
155
|
|
278
|
-
|
279
|
-
|
280
|
-
|
156
|
+
def unbound_templates_from_path(path)
|
157
|
+
if path.name.include?(".")
|
158
|
+
return []
|
159
|
+
end
|
281
160
|
|
282
|
-
|
283
|
-
|
284
|
-
|
161
|
+
# Instead of checking for every possible path, as our other globs would
|
162
|
+
# do, scan the directory for files with the right prefix.
|
163
|
+
paths = template_glob("#{escape_entry(path.to_s)}*")
|
285
164
|
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
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
|
-
#
|
324
|
-
class FileSystemResolver < PathResolver
|
325
|
-
def initialize(path, pattern=nil)
|
326
|
-
raise ArgumentError, "path already is a Resolver class" if path.is_a?(Resolver)
|
327
|
-
super(pattern)
|
328
|
-
@path = File.expand_path(path)
|
329
|
-
end
|
165
|
+
paths.map do |path|
|
166
|
+
build_unbound_template(path)
|
167
|
+
end.select do |template|
|
168
|
+
# Select for exact virtual path match, including case sensitivity
|
169
|
+
template.virtual_path == path.virtual
|
170
|
+
end
|
171
|
+
end
|
330
172
|
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
173
|
+
def filter_and_sort_by_details(templates, requested_details)
|
174
|
+
filtered_templates = templates.select do |template|
|
175
|
+
template.details.matches?(requested_details)
|
176
|
+
end
|
335
177
|
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
178
|
+
if filtered_templates.count > 1
|
179
|
+
filtered_templates.sort_by! do |template|
|
180
|
+
template.details.sort_key_for(requested_details)
|
181
|
+
end
|
182
|
+
end
|
341
183
|
|
342
|
-
|
343
|
-
|
344
|
-
def build_query(path, details)
|
345
|
-
query = escape_entry(File.join(@path, path))
|
184
|
+
filtered_templates
|
185
|
+
end
|
346
186
|
|
347
|
-
|
348
|
-
|
349
|
-
|
187
|
+
# Safe glob within @path
|
188
|
+
def template_glob(glob)
|
189
|
+
query = File.join(escape_entry(@path), glob)
|
190
|
+
path_with_slash = File.join(@path, "")
|
350
191
|
|
351
|
-
|
352
|
-
|
353
|
-
|
192
|
+
Dir.glob(query).filter_map do |filename|
|
193
|
+
filename = File.expand_path(filename)
|
194
|
+
next if File.directory?(filename)
|
195
|
+
next unless filename.start_with?(path_with_slash)
|
354
196
|
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
def self.instances
|
359
|
-
[new(""), new("/")]
|
360
|
-
end
|
197
|
+
filename
|
198
|
+
end
|
199
|
+
end
|
361
200
|
|
362
|
-
|
363
|
-
|
364
|
-
|
201
|
+
def escape_entry(entry)
|
202
|
+
entry.gsub(/[*?{}\[\]]/, '\\\\\\&')
|
203
|
+
end
|
365
204
|
end
|
366
205
|
end
|
@@ -1,22 +1,20 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActionView # :nodoc:
|
2
4
|
# = Action View Text Template
|
3
|
-
class Template
|
4
|
-
class Text
|
5
|
+
class Template # :nodoc:
|
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,8 +24,8 @@ module ActionView #:nodoc:
|
|
26
24
|
to_str
|
27
25
|
end
|
28
26
|
|
29
|
-
def
|
30
|
-
|
27
|
+
def format
|
28
|
+
:text
|
31
29
|
end
|
32
30
|
end
|
33
31
|
end
|
@@ -1,23 +1,17 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/core_ext/module/attribute_accessors"
|
3
4
|
|
4
5
|
module ActionView
|
5
|
-
class Template
|
6
|
-
|
6
|
+
class Template # :nodoc:
|
7
|
+
module Types
|
7
8
|
class Type
|
8
|
-
|
9
|
-
self.types = Set.new
|
10
|
-
|
11
|
-
def self.register(*t)
|
12
|
-
types.merge(t.map { |type| type.to_s })
|
13
|
-
end
|
14
|
-
|
15
|
-
register :html, :text, :js, :css, :xml, :json
|
9
|
+
SET = Struct.new(:symbols).new([ :html, :text, :js, :css, :xml, :json ])
|
16
10
|
|
17
11
|
def self.[](type)
|
18
|
-
|
19
|
-
|
20
|
-
|
12
|
+
if type.is_a?(self)
|
13
|
+
type
|
14
|
+
else
|
21
15
|
new(type)
|
22
16
|
end
|
23
17
|
end
|
@@ -28,30 +22,38 @@ module ActionView
|
|
28
22
|
@symbol = symbol.to_sym
|
29
23
|
end
|
30
24
|
|
31
|
-
|
25
|
+
def to_s
|
26
|
+
@symbol.to_s
|
27
|
+
end
|
32
28
|
alias to_str to_s
|
33
29
|
|
34
30
|
def ref
|
35
|
-
|
31
|
+
@symbol
|
36
32
|
end
|
33
|
+
alias to_sym ref
|
37
34
|
|
38
35
|
def ==(type)
|
39
|
-
|
40
|
-
symbol.to_sym == type.to_sym
|
36
|
+
@symbol == type.to_sym unless type.blank?
|
41
37
|
end
|
42
38
|
end
|
43
39
|
|
44
|
-
|
40
|
+
class << self
|
41
|
+
attr_accessor :type_klass
|
45
42
|
|
46
|
-
|
47
|
-
|
48
|
-
|
43
|
+
def delegate_to(klass)
|
44
|
+
self.type_klass = klass
|
45
|
+
end
|
49
46
|
|
50
|
-
|
47
|
+
def [](type)
|
48
|
+
type_klass[type]
|
49
|
+
end
|
51
50
|
|
52
|
-
|
53
|
-
|
51
|
+
def symbols
|
52
|
+
type_klass::SET.symbols
|
53
|
+
end
|
54
54
|
end
|
55
|
+
|
56
|
+
delegate_to Type
|
55
57
|
end
|
56
58
|
end
|
57
59
|
end
|