actionview 5.2.4.rc1 → 6.0.0.rc2

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.

Files changed (70) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +182 -77
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +4 -2
  5. data/lib/action_view.rb +3 -2
  6. data/lib/action_view/base.rb +107 -10
  7. data/lib/action_view/buffers.rb +15 -0
  8. data/lib/action_view/cache_expiry.rb +54 -0
  9. data/lib/action_view/context.rb +5 -9
  10. data/lib/action_view/digestor.rb +8 -17
  11. data/lib/action_view/gem_version.rb +4 -4
  12. data/lib/action_view/helpers.rb +0 -2
  13. data/lib/action_view/helpers/asset_tag_helper.rb +7 -30
  14. data/lib/action_view/helpers/asset_url_helper.rb +4 -3
  15. data/lib/action_view/helpers/cache_helper.rb +18 -10
  16. data/lib/action_view/helpers/capture_helper.rb +4 -0
  17. data/lib/action_view/helpers/csp_helper.rb +4 -2
  18. data/lib/action_view/helpers/csrf_helper.rb +1 -1
  19. data/lib/action_view/helpers/date_helper.rb +69 -25
  20. data/lib/action_view/helpers/form_helper.rb +238 -6
  21. data/lib/action_view/helpers/form_options_helper.rb +23 -15
  22. data/lib/action_view/helpers/form_tag_helper.rb +12 -11
  23. data/lib/action_view/helpers/javascript_helper.rb +9 -8
  24. data/lib/action_view/helpers/number_helper.rb +5 -0
  25. data/lib/action_view/helpers/output_safety_helper.rb +1 -1
  26. data/lib/action_view/helpers/rendering_helper.rb +6 -4
  27. data/lib/action_view/helpers/sanitize_helper.rb +3 -3
  28. data/lib/action_view/helpers/tag_helper.rb +7 -6
  29. data/lib/action_view/helpers/tags/base.rb +9 -5
  30. data/lib/action_view/helpers/tags/color_field.rb +1 -1
  31. data/lib/action_view/helpers/tags/translator.rb +1 -6
  32. data/lib/action_view/helpers/text_helper.rb +3 -3
  33. data/lib/action_view/helpers/translation_helper.rb +16 -12
  34. data/lib/action_view/helpers/url_helper.rb +14 -14
  35. data/lib/action_view/layouts.rb +5 -5
  36. data/lib/action_view/log_subscriber.rb +6 -6
  37. data/lib/action_view/lookup_context.rb +73 -31
  38. data/lib/action_view/path_set.rb +5 -10
  39. data/lib/action_view/railtie.rb +24 -1
  40. data/lib/action_view/record_identifier.rb +2 -2
  41. data/lib/action_view/renderer/abstract_renderer.rb +56 -3
  42. data/lib/action_view/renderer/partial_renderer.rb +66 -52
  43. data/lib/action_view/renderer/partial_renderer/collection_caching.rb +62 -16
  44. data/lib/action_view/renderer/renderer.rb +16 -4
  45. data/lib/action_view/renderer/streaming_template_renderer.rb +5 -5
  46. data/lib/action_view/renderer/template_renderer.rb +24 -18
  47. data/lib/action_view/rendering.rb +51 -31
  48. data/lib/action_view/routing_url_for.rb +12 -11
  49. data/lib/action_view/template.rb +102 -70
  50. data/lib/action_view/template/error.rb +21 -1
  51. data/lib/action_view/template/handlers.rb +27 -1
  52. data/lib/action_view/template/handlers/builder.rb +2 -2
  53. data/lib/action_view/template/handlers/erb.rb +17 -7
  54. data/lib/action_view/template/handlers/erb/erubi.rb +7 -3
  55. data/lib/action_view/template/handlers/html.rb +1 -1
  56. data/lib/action_view/template/handlers/raw.rb +2 -2
  57. data/lib/action_view/template/html.rb +14 -5
  58. data/lib/action_view/template/inline.rb +22 -0
  59. data/lib/action_view/template/raw_file.rb +28 -0
  60. data/lib/action_view/template/resolver.rb +136 -133
  61. data/lib/action_view/template/sources.rb +13 -0
  62. data/lib/action_view/template/sources/file.rb +17 -0
  63. data/lib/action_view/template/text.rb +5 -3
  64. data/lib/action_view/test_case.rb +1 -1
  65. data/lib/action_view/testing/resolvers.rb +12 -11
  66. data/lib/action_view/unbound_template.rb +32 -0
  67. data/lib/action_view/view_paths.rb +25 -1
  68. data/lib/assets/compiled/rails-ujs.js +27 -4
  69. metadata +19 -14
  70. data/lib/action_view/helpers/record_tag_helper.rb +0 -23
@@ -2,13 +2,23 @@
2
2
 
3
3
  require "active_support/core_ext/object/try"
4
4
  require "active_support/core_ext/kernel/singleton_class"
5
+ require "active_support/deprecation"
5
6
  require "thread"
7
+ require "delegate"
6
8
 
7
9
  module ActionView
8
10
  # = Action View Template
9
11
  class Template
10
12
  extend ActiveSupport::Autoload
11
13
 
14
+ def self.finalize_compiled_template_methods
15
+ ActiveSupport::Deprecation.warn "ActionView::Template.finalize_compiled_template_methods is deprecated and has no effect"
16
+ end
17
+
18
+ def self.finalize_compiled_template_methods=(_)
19
+ ActiveSupport::Deprecation.warn "ActionView::Template.finalize_compiled_template_methods= is deprecated and has no effect"
20
+ end
21
+
12
22
  # === Encodings in ActionView::Template
13
23
  #
14
24
  # ActionView::Template is one of a few sources of potential
@@ -103,44 +113,60 @@ module ActionView
103
113
 
104
114
  eager_autoload do
105
115
  autoload :Error
116
+ autoload :RawFile
106
117
  autoload :Handlers
107
118
  autoload :HTML
119
+ autoload :Inline
120
+ autoload :Sources
108
121
  autoload :Text
109
122
  autoload :Types
110
123
  end
111
124
 
112
125
  extend Template::Handlers
113
126
 
114
- attr_accessor :locals, :formats, :variants, :virtual_path
127
+ attr_reader :identifier, :handler, :original_encoding, :updated_at
128
+ attr_reader :variable, :format, :variant, :locals, :virtual_path
115
129
 
116
- attr_reader :source, :identifier, :handler, :original_encoding, :updated_at
117
-
118
- # This finalizer is needed (and exactly with a proc inside another proc)
119
- # otherwise templates leak in development.
120
- Finalizer = proc do |method_name, mod| # :nodoc:
121
- proc do
122
- mod.module_eval do
123
- remove_possible_method method_name
124
- end
130
+ def initialize(source, identifier, handler, format: nil, variant: nil, locals: nil, virtual_path: nil, updated_at: nil)
131
+ unless locals
132
+ ActiveSupport::Deprecation.warn "ActionView::Template#initialize requires a locals parameter"
133
+ locals = []
125
134
  end
126
- end
127
-
128
- def initialize(source, identifier, handler, details)
129
- format = details[:format] || (handler.default_format if handler.respond_to?(:default_format))
130
135
 
131
136
  @source = source
132
137
  @identifier = identifier
133
138
  @handler = handler
134
139
  @compiled = false
135
- @original_encoding = nil
136
- @locals = details[:locals] || []
137
- @virtual_path = details[:virtual_path]
138
- @updated_at = details[:updated_at] || Time.now
139
- @formats = Array(format).map { |f| f.respond_to?(:ref) ? f.ref : f }
140
- @variants = [details[:variant]]
140
+ @locals = locals
141
+ @virtual_path = virtual_path
142
+
143
+ @variable = if @virtual_path
144
+ base = @virtual_path[-1] == "/" ? "" : ::File.basename(@virtual_path)
145
+ base =~ /\A_?(.*?)(?:\.\w+)*\z/
146
+ $1.to_sym
147
+ end
148
+
149
+ if updated_at
150
+ ActiveSupport::Deprecation.warn "ActionView::Template#updated_at is deprecated"
151
+ @updated_at = updated_at
152
+ else
153
+ @updated_at = Time.now
154
+ end
155
+ @format = format
156
+ @variant = variant
141
157
  @compile_mutex = Mutex.new
142
158
  end
143
159
 
160
+ deprecate :original_encoding
161
+ deprecate :updated_at
162
+ deprecate def virtual_path=(_); end
163
+ deprecate def locals=(_); end
164
+ deprecate def formats=(_); end
165
+ deprecate def formats; Array(format); end
166
+ deprecate def variants=(_); end
167
+ deprecate def variants; [variant]; end
168
+ deprecate def refresh(_); self; end
169
+
144
170
  # Returns whether the underlying handler supports streaming. If so,
145
171
  # a streaming buffer *may* be passed when it starts rendering.
146
172
  def supports_streaming?
@@ -153,40 +179,29 @@ module ActionView
153
179
  # This method is instrumented as "!render_template.action_view". Notice that
154
180
  # we use a bang in this instrumentation because you don't want to
155
181
  # consume this in production. This is only slow if it's being listened to.
156
- def render(view, locals, buffer = nil, &block)
182
+ def render(view, locals, buffer = ActionView::OutputBuffer.new, &block)
157
183
  instrument_render_template do
158
184
  compile!(view)
159
- view.send(method_name, locals, buffer, &block)
185
+ view._run(method_name, self, locals, buffer, &block)
160
186
  end
161
187
  rescue => e
162
188
  handle_render_error(view, e)
163
189
  end
164
190
 
165
191
  def type
166
- @type ||= Types[@formats.first] if @formats.first
192
+ @type ||= Types[format]
167
193
  end
168
194
 
169
- # Receives a view object and return a template similar to self by using @virtual_path.
170
- #
171
- # This method is useful if you have a template object but it does not contain its source
172
- # anymore since it was already compiled. In such cases, all you need to do is to call
173
- # refresh passing in the view object.
174
- #
175
- # Notice this method raises an error if the template to be refreshed does not have a
176
- # virtual path set (true just for inline templates).
177
- def refresh(view)
178
- raise "A template needs to have a virtual path in order to be refreshed" unless @virtual_path
179
- lookup = view.lookup_context
180
- pieces = @virtual_path.split("/")
181
- name = pieces.pop
182
- partial = !!name.sub!(/^_/, "")
183
- lookup.disable_cache do
184
- lookup.find_template(name, [ pieces.join("/") ], partial, @locals)
185
- end
195
+ def short_identifier
196
+ @short_identifier ||= defined?(Rails.root) ? identifier.sub("#{Rails.root}/", "") : identifier
186
197
  end
187
198
 
188
199
  def inspect
189
- @inspect ||= defined?(Rails.root) ? identifier.sub("#{Rails.root}/", "".freeze) : identifier
200
+ "#<#{self.class.name} #{short_identifier} locals=#{@locals.inspect}>"
201
+ end
202
+
203
+ def source
204
+ @source.to_s
190
205
  end
191
206
 
192
207
  # This method is responsible for properly setting the encoding of the
@@ -200,7 +215,9 @@ module ActionView
200
215
  # before passing the source on to the template engine, leaving a
201
216
  # blank line in its stead.
202
217
  def encode!
203
- return unless source.encoding == Encoding::BINARY
218
+ source = self.source
219
+
220
+ return source unless source.encoding == Encoding::BINARY
204
221
 
205
222
  # Look for # encoding: *. If we find one, we'll encode the
206
223
  # String in that encoding, otherwise, we'll use the
@@ -233,6 +250,19 @@ module ActionView
233
250
  end
234
251
  end
235
252
 
253
+
254
+ # Exceptions are marshalled when using the parallel test runner with DRb, so we need
255
+ # to ensure that references to the template object can be marshalled as well. This means forgoing
256
+ # the marshalling of the compiler mutex and instantiating that again on unmarshalling.
257
+ def marshal_dump # :nodoc:
258
+ [ @source, @identifier, @handler, @compiled, @locals, @virtual_path, @updated_at, @format, @variant ]
259
+ end
260
+
261
+ def marshal_load(array) # :nodoc:
262
+ @source, @identifier, @handler, @compiled, @locals, @virtual_path, @updated_at, @format, @variant = *array
263
+ @compile_mutex = Mutex.new
264
+ end
265
+
236
266
  private
237
267
 
238
268
  # Compile a template. This method ensures a template is compiled
@@ -249,23 +279,25 @@ module ActionView
249
279
  # re-compilation
250
280
  return if @compiled
251
281
 
252
- if view.is_a?(ActionView::CompiledTemplates)
253
- mod = ActionView::CompiledTemplates
254
- else
255
- mod = view.singleton_class
256
- end
282
+ mod = view.compiled_method_container
257
283
 
258
284
  instrument("!compile_template") do
259
285
  compile(mod)
260
286
  end
261
287
 
262
- # Just discard the source if we have a virtual path. This
263
- # means we can get the template back.
264
- @source = nil if @virtual_path
265
288
  @compiled = true
266
289
  end
267
290
  end
268
291
 
292
+ class LegacyTemplate < DelegateClass(Template) # :nodoc:
293
+ attr_reader :source
294
+
295
+ def initialize(template, source)
296
+ super(template)
297
+ @source = source
298
+ end
299
+ end
300
+
269
301
  # Among other things, this method is responsible for properly setting
270
302
  # the encoding of the compiled template.
271
303
  #
@@ -279,16 +311,15 @@ module ActionView
279
311
  # In general, this means that templates will be UTF-8 inside of Rails,
280
312
  # regardless of the original source encoding.
281
313
  def compile(mod)
282
- encode!
283
- code = @handler.call(self)
314
+ source = encode!
315
+ code = @handler.call(self, source)
284
316
 
285
317
  # Make sure that the resulting String to be eval'd is in the
286
318
  # encoding of the code
287
- source = <<-end_src.dup
319
+ original_source = source
320
+ source = +<<-end_src
288
321
  def #{method_name}(local_assigns, output_buffer)
289
- _old_virtual_path, @virtual_path = @virtual_path, #{@virtual_path.inspect};_old_output_buffer = @output_buffer;#{locals_code};#{code}
290
- ensure
291
- @virtual_path, @output_buffer = _old_virtual_path, _old_output_buffer
322
+ @virtual_path = #{@virtual_path.inspect};#{locals_code};#{code}
292
323
  end
293
324
  end_src
294
325
 
@@ -303,11 +334,17 @@ module ActionView
303
334
  # handler is valid in the default_internal. This is for handlers
304
335
  # that handle encoding but screw up
305
336
  unless source.valid_encoding?
306
- raise WrongEncodingError.new(@source, Encoding.default_internal)
337
+ raise WrongEncodingError.new(source, Encoding.default_internal)
307
338
  end
308
339
 
309
- mod.module_eval(source, identifier, 0)
310
- ObjectSpace.define_finalizer(self, Finalizer[method_name, mod])
340
+ begin
341
+ mod.module_eval(source, identifier, 0)
342
+ rescue SyntaxError
343
+ # Account for when code in the template is not syntactically valid; e.g. if we're using
344
+ # ERB and the user writes <%= foo( %>, attempting to call a helper `foo` and interpolate
345
+ # the result into the template, but missing an end parenthesis.
346
+ raise SyntaxErrorInTemplate.new(self, original_source)
347
+ end
311
348
  end
312
349
 
313
350
  def handle_render_error(view, e)
@@ -315,12 +352,7 @@ module ActionView
315
352
  e.sub_template_of(self)
316
353
  raise e
317
354
  else
318
- template = self
319
- unless template.source
320
- template = refresh(view)
321
- template.encode!
322
- end
323
- raise Template::Error.new(template)
355
+ raise Template::Error.new(self)
324
356
  end
325
357
  end
326
358
 
@@ -331,19 +363,19 @@ module ActionView
331
363
  locals = locals.grep(/\A@?(?![A-Z0-9])(?:[[:alnum:]_]|[^\0-\177])+\z/)
332
364
 
333
365
  # Assign for the same variable is to suppress unused variable warning
334
- locals.each_with_object("".dup) { |key, code| code << "#{key} = local_assigns[:#{key}]; #{key} = #{key};" }
366
+ locals.each_with_object(+"") { |key, code| code << "#{key} = local_assigns[:#{key}]; #{key} = #{key};" }
335
367
  end
336
368
 
337
369
  def method_name
338
370
  @method_name ||= begin
339
- m = "_#{identifier_method_name}__#{@identifier.hash}_#{__id__}".dup
340
- m.tr!("-".freeze, "_".freeze)
371
+ m = +"_#{identifier_method_name}__#{@identifier.hash}_#{__id__}"
372
+ m.tr!("-", "_")
341
373
  m
342
374
  end
343
375
  end
344
376
 
345
377
  def identifier_method_name
346
- inspect.tr("^a-z_".freeze, "_".freeze)
378
+ short_identifier.tr("^a-z_", "_")
347
379
  end
348
380
 
349
381
  def instrument(action, &block) # :doc:
@@ -351,7 +383,7 @@ module ActionView
351
383
  end
352
384
 
353
385
  def instrument_render_template(&block)
354
- ActiveSupport::Notifications.instrument("!render_template.action_view".freeze, instrument_payload, &block)
386
+ ActiveSupport::Notifications.instrument("!render_template.action_view", instrument_payload, &block)
355
387
  end
356
388
 
357
389
  def instrument_payload
@@ -109,7 +109,7 @@ module ActionView
109
109
  end
110
110
  end
111
111
 
112
- def annoted_source_code
112
+ def annotated_source_code
113
113
  source_extract(4)
114
114
  end
115
115
 
@@ -138,4 +138,24 @@ module ActionView
138
138
  end
139
139
 
140
140
  TemplateError = Template::Error
141
+
142
+ class SyntaxErrorInTemplate < TemplateError #:nodoc
143
+ def initialize(template, offending_code_string)
144
+ @offending_code_string = offending_code_string
145
+ super(template)
146
+ end
147
+
148
+ def message
149
+ <<~MESSAGE
150
+ Encountered a syntax error while rendering template: check #{@offending_code_string}
151
+ MESSAGE
152
+ end
153
+
154
+ def annotated_source_code
155
+ @offending_code_string.split("\n").map.with_index(1) { |line, index|
156
+ indentation = " " * 4
157
+ "#{index}:#{indentation}#{line}"
158
+ }
159
+ end
160
+ end
141
161
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_support/deprecation"
4
+
3
5
  module ActionView #:nodoc:
4
6
  # = Action View Template Handlers
5
7
  class Template #:nodoc:
@@ -14,7 +16,7 @@ module ActionView #:nodoc:
14
16
  base.register_template_handler :erb, ERB.new
15
17
  base.register_template_handler :html, Html.new
16
18
  base.register_template_handler :builder, Builder.new
17
- base.register_template_handler :ruby, :source.to_proc
19
+ base.register_template_handler :ruby, lambda { |_, source| source }
18
20
  end
19
21
 
20
22
  @@template_handlers = {}
@@ -24,11 +26,35 @@ module ActionView #:nodoc:
24
26
  @@template_extensions ||= @@template_handlers.keys
25
27
  end
26
28
 
29
+ class LegacyHandlerWrapper < SimpleDelegator # :nodoc:
30
+ def call(view, source)
31
+ __getobj__.call(ActionView::Template::LegacyTemplate.new(view, source))
32
+ end
33
+ end
34
+
27
35
  # Register an object that knows how to handle template files with the given
28
36
  # extensions. This can be used to implement new template types.
29
37
  # The handler must respond to +:call+, which will be passed the template
30
38
  # and should return the rendered template as a String.
31
39
  def register_template_handler(*extensions, handler)
40
+ params = if handler.is_a?(Proc)
41
+ handler.parameters
42
+ else
43
+ handler.method(:call).parameters
44
+ end
45
+
46
+ unless params.find_all { |type, _| type == :req || type == :opt }.length >= 2
47
+ ActiveSupport::Deprecation.warn <<~eowarn
48
+ Single arity template handlers are deprecated. Template handlers must
49
+ now accept two parameters, the view object and the source for the view object.
50
+ Change:
51
+ >> #{handler}.call(#{params.map(&:last).join(", ")})
52
+ To:
53
+ >> #{handler}.call(#{params.map(&:last).join(", ")}, source)
54
+ eowarn
55
+ handler = LegacyHandlerWrapper.new(handler)
56
+ end
57
+
32
58
  raise(ArgumentError, "Extension is required") if extensions.empty?
33
59
  extensions.each do |extension|
34
60
  @@template_handlers[extension.to_sym] = handler
@@ -5,11 +5,11 @@ module ActionView
5
5
  class Builder
6
6
  class_attribute :default_format, default: :xml
7
7
 
8
- def call(template)
8
+ def call(template, source)
9
9
  require_engine
10
10
  "xml = ::Builder::XmlMarkup.new(:indent => 2);" \
11
11
  "self.output_buffer = xml.target!;" +
12
- template.source +
12
+ source +
13
13
  ";xml.target!;"
14
14
  end
15
15
 
@@ -14,12 +14,22 @@ module ActionView
14
14
  class_attribute :erb_implementation, default: Erubi
15
15
 
16
16
  # Do not escape templates of these mime types.
17
- class_attribute :escape_whitelist, default: ["text/plain"]
17
+ class_attribute :escape_ignore_list, default: ["text/plain"]
18
+
19
+ [self, singleton_class].each do |base|
20
+ base.alias_method :escape_whitelist, :escape_ignore_list
21
+ base.alias_method :escape_whitelist=, :escape_ignore_list=
22
+
23
+ base.deprecate(
24
+ escape_whitelist: "use #escape_ignore_list instead",
25
+ :escape_whitelist= => "use #escape_ignore_list= instead"
26
+ )
27
+ end
18
28
 
19
29
  ENCODING_TAG = Regexp.new("\\A(<%#{ENCODING_FLAG}-?%>)[ \\t]*")
20
30
 
21
- def self.call(template)
22
- new.call(template)
31
+ def self.call(template, source)
32
+ new.call(template, source)
23
33
  end
24
34
 
25
35
  def supports_streaming?
@@ -30,24 +40,24 @@ module ActionView
30
40
  true
31
41
  end
32
42
 
33
- def call(template)
43
+ def call(template, source)
34
44
  # First, convert to BINARY, so in case the encoding is
35
45
  # wrong, we can still find an encoding tag
36
46
  # (<%# encoding %>) inside the String using a regular
37
47
  # expression
38
- template_source = template.source.dup.force_encoding(Encoding::ASCII_8BIT)
48
+ template_source = source.dup.force_encoding(Encoding::ASCII_8BIT)
39
49
 
40
50
  erb = template_source.gsub(ENCODING_TAG, "")
41
51
  encoding = $2
42
52
 
43
- erb.force_encoding valid_encoding(template.source.dup, encoding)
53
+ erb.force_encoding valid_encoding(source.dup, encoding)
44
54
 
45
55
  # Always make sure we return a String in the default_internal
46
56
  erb.encode!
47
57
 
48
58
  self.class.erb_implementation.new(
49
59
  erb,
50
- escape: (self.class.escape_whitelist.include? template.type),
60
+ escape: (self.class.escape_ignore_list.include? template.type),
51
61
  trim: (self.class.erb_trim_mode == "-")
52
62
  ).src
53
63
  end