actionview 6.0.4 → 6.1.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 +152 -258
- data/MIT-LICENSE +1 -1
- data/README.rdoc +1 -1
- data/lib/action_view.rb +4 -1
- data/lib/action_view/base.rb +21 -52
- data/lib/action_view/cache_expiry.rb +1 -2
- data/lib/action_view/context.rb +0 -1
- data/lib/action_view/dependency_tracker.rb +10 -4
- data/lib/action_view/digestor.rb +3 -2
- data/lib/action_view/gem_version.rb +3 -3
- data/lib/action_view/helpers/asset_tag_helper.rb +40 -15
- 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 +10 -16
- data/lib/action_view/helpers/date_helper.rb +4 -4
- data/lib/action_view/helpers/form_helper.rb +59 -17
- data/lib/action_view/helpers/form_options_helper.rb +7 -16
- data/lib/action_view/helpers/form_tag_helper.rb +8 -6
- data/lib/action_view/helpers/javascript_helper.rb +3 -3
- data/lib/action_view/helpers/number_helper.rb +6 -6
- data/lib/action_view/helpers/rendering_helper.rb +11 -3
- data/lib/action_view/helpers/sanitize_helper.rb +2 -2
- data/lib/action_view/helpers/tag_helper.rb +92 -17
- data/lib/action_view/helpers/tags/base.rb +9 -5
- data/lib/action_view/helpers/tags/date_field.rb +1 -1
- data/lib/action_view/helpers/tags/date_select.rb +2 -2
- data/lib/action_view/helpers/tags/datetime_local_field.rb +1 -1
- data/lib/action_view/helpers/tags/label.rb +4 -0
- data/lib/action_view/helpers/tags/month_field.rb +1 -1
- data/lib/action_view/helpers/tags/select.rb +1 -1
- data/lib/action_view/helpers/tags/time_field.rb +1 -1
- data/lib/action_view/helpers/tags/week_field.rb +1 -1
- data/lib/action_view/helpers/text_helper.rb +1 -1
- data/lib/action_view/helpers/translation_helper.rb +88 -53
- data/lib/action_view/helpers/url_helper.rb +107 -13
- data/lib/action_view/layouts.rb +3 -2
- data/lib/action_view/log_subscriber.rb +26 -10
- data/lib/action_view/lookup_context.rb +3 -18
- data/lib/action_view/path_set.rb +0 -3
- data/lib/action_view/railtie.rb +35 -46
- data/lib/action_view/renderer/abstract_renderer.rb +93 -14
- data/lib/action_view/renderer/collection_renderer.rb +192 -0
- data/lib/action_view/renderer/object_renderer.rb +34 -0
- data/lib/action_view/renderer/partial_renderer.rb +20 -282
- data/lib/action_view/renderer/partial_renderer/collection_caching.rb +25 -26
- data/lib/action_view/renderer/renderer.rb +44 -1
- data/lib/action_view/renderer/streaming_template_renderer.rb +5 -1
- data/lib/action_view/renderer/template_renderer.rb +15 -12
- data/lib/action_view/rendering.rb +3 -1
- data/lib/action_view/routing_url_for.rb +1 -1
- data/lib/action_view/template.rb +9 -49
- data/lib/action_view/template/handlers.rb +0 -26
- data/lib/action_view/template/handlers/erb.rb +10 -14
- data/lib/action_view/template/handlers/erb/erubi.rb +9 -7
- data/lib/action_view/template/html.rb +1 -11
- data/lib/action_view/template/raw_file.rb +0 -3
- data/lib/action_view/template/renderable.rb +24 -0
- data/lib/action_view/template/resolver.rb +82 -40
- data/lib/action_view/template/text.rb +0 -3
- data/lib/action_view/test_case.rb +18 -25
- data/lib/action_view/testing/resolvers.rb +10 -31
- data/lib/action_view/unbound_template.rb +3 -3
- data/lib/action_view/view_paths.rb +34 -36
- data/lib/assets/compiled/rails-ujs.js +1 -1
- metadata +17 -14
@@ -13,9 +13,11 @@ module ActionView
|
|
13
13
|
|
14
14
|
# Dup properties so that we don't modify argument
|
15
15
|
properties = Hash[properties]
|
16
|
-
|
17
|
-
properties[:
|
18
|
-
properties[:
|
16
|
+
|
17
|
+
properties[:bufvar] ||= "@output_buffer"
|
18
|
+
properties[:preamble] ||= ""
|
19
|
+
properties[:postamble] ||= "#{properties[:bufvar]}.to_s"
|
20
|
+
|
19
21
|
properties[:escapefunc] = ""
|
20
22
|
|
21
23
|
super
|
@@ -37,7 +39,7 @@ module ActionView
|
|
37
39
|
if text == "\n"
|
38
40
|
@newline_pending += 1
|
39
41
|
else
|
40
|
-
src << "
|
42
|
+
src << bufvar << ".safe_append='"
|
41
43
|
src << "\n" * @newline_pending if @newline_pending > 0
|
42
44
|
src << text.gsub(/['\\]/, '\\\\\&')
|
43
45
|
src << "'.freeze;"
|
@@ -52,9 +54,9 @@ module ActionView
|
|
52
54
|
flush_newline_if_pending(src)
|
53
55
|
|
54
56
|
if (indicator == "==") || @escape
|
55
|
-
src << "
|
57
|
+
src << bufvar << ".safe_expr_append="
|
56
58
|
else
|
57
|
-
src << "
|
59
|
+
src << bufvar << ".append="
|
58
60
|
end
|
59
61
|
|
60
62
|
if BLOCK_EXPR.match?(code)
|
@@ -76,7 +78,7 @@ module ActionView
|
|
76
78
|
|
77
79
|
def flush_newline_if_pending(src)
|
78
80
|
if @newline_pending > 0
|
79
|
-
src << "
|
81
|
+
src << bufvar << ".safe_append='#{"\n" * @newline_pending}'.freeze;"
|
80
82
|
@newline_pending = 0
|
81
83
|
end
|
82
84
|
end
|
@@ -1,19 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "active_support/deprecation"
|
4
|
-
|
5
3
|
module ActionView #:nodoc:
|
6
4
|
# = Action View HTML Template
|
7
5
|
class Template #:nodoc:
|
8
6
|
class HTML #:nodoc:
|
9
7
|
attr_reader :type
|
10
8
|
|
11
|
-
def initialize(string, type
|
12
|
-
unless type
|
13
|
-
ActiveSupport::Deprecation.warn "ActionView::Template::HTML#initialize requires a type parameter"
|
14
|
-
type = :html
|
15
|
-
end
|
16
|
-
|
9
|
+
def initialize(string, type)
|
17
10
|
@string = string.to_s
|
18
11
|
@type = type
|
19
12
|
end
|
@@ -35,9 +28,6 @@ module ActionView #:nodoc:
|
|
35
28
|
def format
|
36
29
|
@type
|
37
30
|
end
|
38
|
-
|
39
|
-
def formats; Array(format); end
|
40
|
-
deprecate :formats
|
41
31
|
end
|
42
32
|
end
|
43
33
|
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,7 +93,7 @@ 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
|
@@ -75,7 +110,7 @@ module ActionView
|
|
75
110
|
@query_cache.clear
|
76
111
|
end
|
77
112
|
|
78
|
-
# Get the cache size.
|
113
|
+
# Get the cache size. Do not call this
|
79
114
|
# method. This method is not guaranteed to be here ever.
|
80
115
|
def size # :nodoc:
|
81
116
|
size = 0
|
@@ -121,9 +156,6 @@ module ActionView
|
|
121
156
|
end
|
122
157
|
end
|
123
158
|
|
124
|
-
alias :find_all_anywhere :find_all
|
125
|
-
deprecate :find_all_anywhere
|
126
|
-
|
127
159
|
def find_all_with_query(query) # :nodoc:
|
128
160
|
@cache.cache_query(query) { find_template_paths(File.join(@path, query)) }
|
129
161
|
end
|
@@ -164,20 +196,17 @@ module ActionView
|
|
164
196
|
EXTENSIONS = { locale: ".", formats: ".", variants: "+", handlers: "." }
|
165
197
|
DEFAULT_PATTERN = ":prefix/:action{.:locale,}{.:formats,}{+:variants,}{.:handlers,}"
|
166
198
|
|
167
|
-
def initialize
|
168
|
-
|
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
|
199
|
+
def initialize
|
200
|
+
@pattern = DEFAULT_PATTERN
|
174
201
|
@unbound_templates = Concurrent::Map.new
|
175
|
-
|
202
|
+
@path_parser = PathParser.new
|
203
|
+
super
|
176
204
|
end
|
177
205
|
|
178
206
|
def clear_cache
|
179
207
|
@unbound_templates.clear
|
180
|
-
|
208
|
+
@path_parser = PathParser.new
|
209
|
+
super
|
181
210
|
end
|
182
211
|
|
183
212
|
private
|
@@ -204,9 +233,13 @@ module ActionView
|
|
204
233
|
end
|
205
234
|
end
|
206
235
|
|
236
|
+
def source_for_template(template)
|
237
|
+
Template::Sources::File.new(template)
|
238
|
+
end
|
239
|
+
|
207
240
|
def build_unbound_template(template, virtual_path)
|
208
241
|
handler, format, variant = extract_handler_and_format_and_variant(template)
|
209
|
-
source =
|
242
|
+
source = source_for_template(template)
|
210
243
|
|
211
244
|
UnboundTemplate.new(
|
212
245
|
source,
|
@@ -223,6 +256,10 @@ module ActionView
|
|
223
256
|
end
|
224
257
|
|
225
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
|
+
|
226
263
|
query = build_query(path, details)
|
227
264
|
find_template_paths(query)
|
228
265
|
end
|
@@ -249,7 +286,7 @@ module ActionView
|
|
249
286
|
query.gsub!(/:prefix(\/)?/, prefix)
|
250
287
|
|
251
288
|
partial = escape_entry(path.partial? ? "_#{path.name}" : path.name)
|
252
|
-
query.gsub!(
|
289
|
+
query.gsub!(":action", partial)
|
253
290
|
|
254
291
|
details.each do |ext, candidates|
|
255
292
|
if ext == :variants && candidates == :any
|
@@ -270,22 +307,11 @@ module ActionView
|
|
270
307
|
# from the path, or the handler, we should return the array of formats given
|
271
308
|
# to the resolver.
|
272
309
|
def extract_handler_and_format_and_variant(path)
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
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
|
310
|
+
details = @path_parser.parse(path)
|
311
|
+
|
312
|
+
handler = Template.handler_for_extension(details[:handler])
|
313
|
+
format = details[:format] || handler.try(:default_format)
|
314
|
+
variant = details[:variant]
|
289
315
|
|
290
316
|
# Template::Types[format] and handler.default_format can return nil
|
291
317
|
[handler, format, variant]
|
@@ -296,9 +322,9 @@ module ActionView
|
|
296
322
|
class FileSystemResolver < PathResolver
|
297
323
|
attr_reader :path
|
298
324
|
|
299
|
-
def initialize(path
|
325
|
+
def initialize(path)
|
300
326
|
raise ArgumentError, "path already is a Resolver class" if path.is_a?(Resolver)
|
301
|
-
super(
|
327
|
+
super()
|
302
328
|
@path = File.expand_path(path)
|
303
329
|
end
|
304
330
|
|
@@ -320,14 +346,27 @@ module ActionView
|
|
320
346
|
end
|
321
347
|
|
322
348
|
private
|
323
|
-
def
|
349
|
+
def find_candidate_template_paths(path)
|
324
350
|
# Instead of checking for every possible path, as our other globs would
|
325
351
|
# do, scan the directory for files with the right prefix.
|
326
352
|
query = "#{escape_entry(File.join(@path, path))}*"
|
327
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
|
+
|
328
367
|
regex = build_regex(path, details)
|
329
368
|
|
330
|
-
|
369
|
+
candidates.uniq.reject do |filename|
|
331
370
|
# This regex match does double duty of finding only files which match
|
332
371
|
# details (instead of just matching the prefix) and also filtering for
|
333
372
|
# case-insensitive file systems.
|
@@ -339,7 +378,7 @@ module ActionView
|
|
339
378
|
# We can use the matches found by the regex and sort by their index in
|
340
379
|
# details.
|
341
380
|
match = filename.match(regex)
|
342
|
-
EXTENSIONS.keys.
|
381
|
+
EXTENSIONS.keys.map do |ext|
|
343
382
|
if ext == :variants && details[ext] == :any
|
344
383
|
match[ext].nil? ? 0 : 1
|
345
384
|
elsif match[ext].nil?
|
@@ -360,7 +399,10 @@ module ActionView
|
|
360
399
|
if ext == :variants && details[ext] == :any
|
361
400
|
".*?"
|
362
401
|
else
|
363
|
-
details[ext].compact
|
402
|
+
arr = details[ext].compact
|
403
|
+
arr.uniq!
|
404
|
+
arr.map! { |e| Regexp.escape(e) }
|
405
|
+
arr.join("|")
|
364
406
|
end
|
365
407
|
prefix = Regexp.escape(prefix)
|
366
408
|
"(#{prefix}(?<#{ext}>#{match}))?"
|
@@ -16,11 +16,12 @@ module ActionView
|
|
16
16
|
attr_accessor :request, :response, :params
|
17
17
|
|
18
18
|
class << self
|
19
|
-
|
19
|
+
# Overrides AbstractController::Base#controller_path
|
20
|
+
attr_accessor :controller_path
|
20
21
|
end
|
21
22
|
|
22
23
|
def controller_path=(path)
|
23
|
-
self.class.controller_path =
|
24
|
+
self.class.controller_path = path
|
24
25
|
end
|
25
26
|
|
26
27
|
def initialize
|
@@ -73,7 +74,7 @@ module ActionView
|
|
73
74
|
def helper_method(*methods)
|
74
75
|
# Almost a duplicate from ActionController::Helpers
|
75
76
|
methods.flatten.each do |method|
|
76
|
-
|
77
|
+
_helpers_for_modification.module_eval <<-end_eval, __FILE__, __LINE__ + 1
|
77
78
|
def #{method}(*args, &block) # def current_user(*args, &block)
|
78
79
|
_test_case.send(:'#{method}', *args, &block) # _test_case.send(:'current_user', *args, &block)
|
79
80
|
end # end
|
@@ -101,7 +102,8 @@ module ActionView
|
|
101
102
|
end
|
102
103
|
|
103
104
|
def setup_with_controller
|
104
|
-
|
105
|
+
controller_class = Class.new(ActionView::TestCase::TestController)
|
106
|
+
@controller = controller_class.new
|
105
107
|
@request = @controller.request
|
106
108
|
@view_flow = ActionView::OutputFlow.new
|
107
109
|
# empty string ensures buffer has UTF-8 encoding as
|
@@ -109,8 +111,8 @@ module ActionView
|
|
109
111
|
@output_buffer = ActiveSupport::SafeBuffer.new ""
|
110
112
|
@rendered = +""
|
111
113
|
|
112
|
-
|
113
|
-
|
114
|
+
test_case_instance = self
|
115
|
+
controller_class.define_method(:_test_case) { test_case_instance }
|
114
116
|
end
|
115
117
|
|
116
118
|
def config
|
@@ -160,33 +162,24 @@ module ActionView
|
|
160
162
|
included do
|
161
163
|
setup :setup_with_controller
|
162
164
|
ActiveSupport.run_load_hooks(:action_view_test_case, self)
|
163
|
-
end
|
164
165
|
|
165
|
-
|
166
|
-
# Need to experiment if this priority is the best one: rendered => output_buffer
|
167
|
-
def document_root_element
|
168
|
-
Nokogiri::HTML::Document.parse(@rendered.blank? ? @output_buffer : @rendered).root
|
169
|
-
end
|
170
|
-
|
171
|
-
def say_no_to_protect_against_forgery!
|
172
|
-
_helpers.module_eval do
|
173
|
-
silence_redefinition_of_method :protect_against_forgery?
|
166
|
+
helper do
|
174
167
|
def protect_against_forgery?
|
175
168
|
false
|
176
169
|
end
|
177
|
-
end
|
178
|
-
end
|
179
170
|
|
180
|
-
|
181
|
-
|
182
|
-
_helpers.module_eval do
|
183
|
-
unless private_method_defined?(:_test_case)
|
184
|
-
define_method(:_test_case) { test_case_instance }
|
185
|
-
private :_test_case
|
171
|
+
def _test_case
|
172
|
+
controller._test_case
|
186
173
|
end
|
187
174
|
end
|
188
175
|
end
|
189
176
|
|
177
|
+
private
|
178
|
+
# Need to experiment if this priority is the best one: rendered => output_buffer
|
179
|
+
def document_root_element
|
180
|
+
Nokogiri::HTML::Document.parse(@rendered.blank? ? @output_buffer : @rendered).root
|
181
|
+
end
|
182
|
+
|
190
183
|
module Locals
|
191
184
|
attr_accessor :rendered_views
|
192
185
|
|
@@ -283,7 +276,7 @@ module ActionView
|
|
283
276
|
|
284
277
|
def respond_to_missing?(name, include_private = false)
|
285
278
|
begin
|
286
|
-
routes = @controller.respond_to?(:_routes) && @controller._routes
|
279
|
+
routes = defined?(@controller) && @controller.respond_to?(:_routes) && @controller._routes
|
287
280
|
rescue
|
288
281
|
# Don't call routes, if there is an error on _routes call
|
289
282
|
end
|