actionview 6.0.0.beta3 → 6.0.2.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 (38) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +76 -4
  3. data/README.rdoc +3 -1
  4. data/lib/action_view.rb +2 -1
  5. data/lib/action_view/base.rb +5 -5
  6. data/lib/action_view/cache_expiry.rb +54 -0
  7. data/lib/action_view/digestor.rb +5 -10
  8. data/lib/action_view/gem_version.rb +2 -2
  9. data/lib/action_view/helpers/form_helper.rb +2 -2
  10. data/lib/action_view/helpers/form_options_helper.rb +4 -3
  11. data/lib/action_view/helpers/form_tag_helper.rb +5 -2
  12. data/lib/action_view/helpers/output_safety_helper.rb +1 -1
  13. data/lib/action_view/helpers/sanitize_helper.rb +10 -16
  14. data/lib/action_view/helpers/tag_helper.rb +1 -1
  15. data/lib/action_view/helpers/tags/base.rb +1 -1
  16. data/lib/action_view/helpers/translation_helper.rb +3 -3
  17. data/lib/action_view/helpers/url_helper.rb +2 -2
  18. data/lib/action_view/lookup_context.rb +11 -4
  19. data/lib/action_view/path_set.rb +5 -10
  20. data/lib/action_view/railtie.rb +1 -1
  21. data/lib/action_view/renderer/partial_renderer.rb +0 -3
  22. data/lib/action_view/renderer/partial_renderer/collection_caching.rb +20 -13
  23. data/lib/action_view/renderer/streaming_template_renderer.rb +1 -1
  24. data/lib/action_view/renderer/template_renderer.rb +9 -3
  25. data/lib/action_view/rendering.rb +3 -2
  26. data/lib/action_view/template.rb +43 -50
  27. data/lib/action_view/template/error.rb +21 -1
  28. data/lib/action_view/template/handlers.rb +3 -3
  29. data/lib/action_view/template/handlers/erb/erubi.rb +2 -2
  30. data/lib/action_view/template/raw_file.rb +28 -0
  31. data/lib/action_view/template/resolver.rb +73 -117
  32. data/lib/action_view/template/sources.rb +13 -0
  33. data/lib/action_view/template/sources/file.rb +17 -0
  34. data/lib/action_view/testing/resolvers.rb +32 -21
  35. data/lib/action_view/unbound_template.rb +32 -0
  36. data/lib/assets/compiled/rails-ujs.js +21 -12
  37. metadata +24 -17
  38. data/lib/action_view/file_template.rb +0 -33
@@ -138,7 +138,7 @@ module ActionView
138
138
  end
139
139
 
140
140
  def sanitized_value(value)
141
- value.to_s.gsub(/[\s\.]/, "_").gsub(/[^-[[:word:]]]/, "").mb_chars.downcase.to_s
141
+ value.to_s.gsub(/[\s\.]/, "_").gsub(/[^-[[:word:]]]/, "").downcase
142
142
  end
143
143
 
144
144
  def select_content_tag(option_tags, options, html_options)
@@ -60,7 +60,7 @@ module ActionView
60
60
  def translate(key, options = {})
61
61
  options = options.dup
62
62
  if options.has_key?(:default)
63
- remaining_defaults = Array(options.delete(:default)).compact
63
+ remaining_defaults = Array.wrap(options.delete(:default)).compact
64
64
  options[:default] = remaining_defaults unless remaining_defaults.first.kind_of?(Symbol)
65
65
  end
66
66
 
@@ -114,7 +114,7 @@ module ActionView
114
114
 
115
115
  # Delegates to <tt>I18n.localize</tt> with no additional functionality.
116
116
  #
117
- # See http://rubydoc.info/github/svenfuchs/i18n/master/I18n/Backend/Base:localize
117
+ # See https://www.rubydoc.info/github/svenfuchs/i18n/master/I18n/Backend/Base:localize
118
118
  # for more information.
119
119
  def localize(*args)
120
120
  I18n.localize(*args)
@@ -138,7 +138,7 @@ module ActionView
138
138
  end
139
139
 
140
140
  def html_safe_translation_key?(key)
141
- /([_.]|\b)html\z/.match?(key.to_s)
141
+ /(?:_|\b)html\z/.match?(key.to_s)
142
142
  end
143
143
  end
144
144
  end
@@ -253,7 +253,7 @@ module ActionView
253
253
  # # <input value="New" type="submit" />
254
254
  # # </form>"
255
255
  #
256
- # <%= button_to "New", new_articles_path %>
256
+ # <%= button_to "New", new_article_path %>
257
257
  # # => "<form method="post" action="/articles/new" class="button_to">
258
258
  # # <input value="New" type="submit" />
259
259
  # # </form>"
@@ -553,7 +553,7 @@ module ActionView
553
553
  url_string = URI.parser.unescape(url_for(options)).force_encoding(Encoding::BINARY)
554
554
 
555
555
  # We ignore any extra parameters in the request_uri if the
556
- # submitted url doesn't have any either. This lets the function
556
+ # submitted URL doesn't have any either. This lets the function
557
557
  # work with things like ?order=asc
558
558
  # the behaviour can be disabled with check_parameters: true
559
559
  request_uri = url_string.index("?") || check_parameters ? request.fullpath : request.path
@@ -130,9 +130,8 @@ module ActionView
130
130
  end
131
131
  alias :find_template :find
132
132
 
133
- def find_file(name, prefixes = [], partial = false, keys = [], options = {})
134
- @view_paths.find_file(*args_for_lookup(name, prefixes, partial, keys, options))
135
- end
133
+ alias :find_file :find
134
+ deprecate :find_file
136
135
 
137
136
  def find_all(name, prefixes = [], partial = false, keys = [], options = {})
138
137
  @view_paths.find_all(*args_for_lookup(name, prefixes, partial, keys, options))
@@ -154,7 +153,7 @@ module ActionView
154
153
  view_paths = build_view_paths((@view_paths.paths + self.class.fallbacks).uniq)
155
154
 
156
155
  if block_given?
157
- ActiveSupport::Deprecation.warn <<~eowarn
156
+ ActiveSupport::Deprecation.warn <<~eowarn.squish
158
157
  Calling `with_fallbacks` with a block is deprecated. Call methods on
159
158
  the lookup context returned by `with_fallbacks` instead.
160
159
  eowarn
@@ -280,7 +279,15 @@ module ActionView
280
279
  # add :html as fallback to :js.
281
280
  def formats=(values)
282
281
  if values
282
+ values = values.dup
283
283
  values.concat(default_formats) if values.delete "*/*"
284
+ values.uniq!
285
+
286
+ invalid_values = (values - Template::Types.symbols)
287
+ unless invalid_values.empty?
288
+ raise ArgumentError, "Invalid formats: #{invalid_values.map(&:inspect).join(", ")}"
289
+ end
290
+
284
291
  if values == [:js]
285
292
  values << :html
286
293
  @html_fallback_for_js = true
@@ -48,12 +48,11 @@ module ActionView #:nodoc:
48
48
  find_all(*args).first || raise(MissingTemplate.new(self, *args))
49
49
  end
50
50
 
51
- def find_file(path, prefixes = [], *args)
52
- _find_all(path, prefixes, args, true).first || raise(MissingTemplate.new(self, path, prefixes, *args))
53
- end
51
+ alias :find_file :find
52
+ deprecate :find_file
54
53
 
55
54
  def find_all(path, prefixes = [], *args)
56
- _find_all path, prefixes, args, false
55
+ _find_all path, prefixes, args
57
56
  end
58
57
 
59
58
  def exists?(path, prefixes, *args)
@@ -71,15 +70,11 @@ module ActionView #:nodoc:
71
70
 
72
71
  private
73
72
 
74
- def _find_all(path, prefixes, args, outside_app)
73
+ def _find_all(path, prefixes, args)
75
74
  prefixes = [prefixes] if String === prefixes
76
75
  prefixes.each do |prefix|
77
76
  paths.each do |resolver|
78
- if outside_app
79
- templates = resolver.find_all_anywhere(path, prefix, *args)
80
- else
81
- templates = resolver.find_all(path, prefix, *args)
82
- end
77
+ templates = resolver.find_all(path, prefix, *args)
83
78
  return templates unless templates.empty?
84
79
  end
85
80
  end
@@ -81,7 +81,7 @@ module ActionView
81
81
  initializer "action_view.per_request_digest_cache" do |app|
82
82
  ActiveSupport.on_load(:action_view) do
83
83
  unless ActionView::Resolver.caching?
84
- app.executor.to_run ActionView::Digestor::PerExecutionDigestCacheExpiry
84
+ app.executor.to_run ActionView::CacheExpiry::Executor.new(watcher: app.config.file_watcher)
85
85
  end
86
86
  end
87
87
  end
@@ -105,9 +105,6 @@ module ActionView
105
105
  #
106
106
  # <%= render(partial: "ad", collection: @advertisements) || "There's no ad to be displayed" %>
107
107
  #
108
- # NOTE: Due to backwards compatibility concerns, the collection can't be one of hashes. Normally you'd also
109
- # just keep domain objects, like Active Records, in there.
110
- #
111
108
  # == \Rendering shared partials
112
109
  #
113
110
  # Two controllers can share a set of partials and render them like this:
@@ -17,13 +17,13 @@ module ActionView
17
17
  # Result is a hash with the key represents the
18
18
  # key used for cache lookup and the value is the item
19
19
  # on which the partial is being rendered
20
- keyed_collection = collection_by_cache_keys(view, template)
20
+ keyed_collection, ordered_keys = collection_by_cache_keys(view, template)
21
21
 
22
22
  # Pull all partials from cache
23
23
  # Result is a hash, key matches the entry in
24
24
  # `keyed_collection` where the cache was retrieved and the
25
25
  # value is the value that was present in the cache
26
- cached_partials = collection_cache.read_multi(*keyed_collection.keys)
26
+ cached_partials = collection_cache.read_multi(*keyed_collection.keys)
27
27
  instrumentation_payload[:cache_hits] = cached_partials.size
28
28
 
29
29
  # Extract the items for the keys that are not found
@@ -40,11 +40,15 @@ module ActionView
40
40
  rendered_partials = @collection.empty? ? [] : yield
41
41
 
42
42
  index = 0
43
- fetch_or_cache_partial(cached_partials, template, order_by: keyed_collection.each_key) do
43
+ keyed_partials = fetch_or_cache_partial(cached_partials, template, order_by: keyed_collection.each_key) do
44
44
  # This block is called once
45
45
  # for every cache miss while preserving order.
46
46
  rendered_partials[index].tap { index += 1 }
47
47
  end
48
+
49
+ ordered_keys.map do |key|
50
+ keyed_partials[key]
51
+ end
48
52
  end
49
53
 
50
54
  def callable_cache_key?
@@ -56,8 +60,10 @@ module ActionView
56
60
 
57
61
  digest_path = view.digest_path_from_template(template)
58
62
 
59
- @collection.each_with_object({}) do |item, hash|
60
- hash[expanded_cache_key(seed.call(item), view, template, digest_path)] = item
63
+ @collection.each_with_object([{}, []]) do |item, (hash, ordered_keys)|
64
+ key = expanded_cache_key(seed.call(item), view, template, digest_path)
65
+ ordered_keys << key
66
+ hash[key] = item
61
67
  end
62
68
  end
63
69
 
@@ -82,15 +88,16 @@ module ActionView
82
88
  # If the partial is not already cached it will also be
83
89
  # written back to the underlying cache store.
84
90
  def fetch_or_cache_partial(cached_partials, template, order_by:)
85
- order_by.map do |cache_key|
86
- if content = cached_partials[cache_key]
87
- build_rendered_template(content, template)
88
- else
89
- yield.tap do |rendered_partial|
90
- collection_cache.write(cache_key, rendered_partial.body)
91
- end
91
+ order_by.each_with_object({}) do |cache_key, hash|
92
+ hash[cache_key] =
93
+ if content = cached_partials[cache_key]
94
+ build_rendered_template(content, template)
95
+ else
96
+ yield.tap do |rendered_partial|
97
+ collection_cache.write(cache_key, rendered_partial.body)
98
+ end
99
+ end
92
100
  end
93
- end
94
101
  end
95
102
  end
96
103
  end
@@ -34,7 +34,7 @@ module ActionView
34
34
  return unless logger
35
35
 
36
36
  message = +"\n#{exception.class} (#{exception.message}):\n"
37
- message << exception.annoted_source_code.to_s if exception.respond_to?(:annoted_source_code)
37
+ message << exception.annotated_source_code.to_s if exception.respond_to?(:annotated_source_code)
38
38
  message << " " << exception.backtrace.join("\n ")
39
39
  logger.fatal("#{message}\n\n")
40
40
  end
@@ -26,7 +26,12 @@ module ActionView
26
26
  elsif options.key?(:html)
27
27
  Template::HTML.new(options[:html], formats.first)
28
28
  elsif options.key?(:file)
29
- @lookup_context.with_fallbacks.find_file(options[:file], nil, false, keys, @details)
29
+ if File.exist?(options[:file])
30
+ Template::RawFile.new(options[:file])
31
+ else
32
+ ActiveSupport::Deprecation.warn "render file: should be given the absolute path to a file"
33
+ @lookup_context.with_fallbacks.find_template(options[:file], nil, false, keys, @details)
34
+ end
30
35
  elsif options.key?(:inline)
31
36
  handler = Template.handler_for_extension(options[:type] || "erb")
32
37
  format = if handler.respond_to?(:default_format)
@@ -49,14 +54,14 @@ module ActionView
49
54
  # Renders the given template. A string representing the layout can be
50
55
  # supplied as well.
51
56
  def render_template(view, template, layout_name, locals)
52
- render_with_layout(view, layout_name, template, locals) do |layout|
57
+ render_with_layout(view, template, layout_name, locals) do |layout|
53
58
  instrument(:template, identifier: template.identifier, layout: layout.try(:virtual_path)) do
54
59
  template.render(view, locals) { |*name| view._layout_for(*name) }
55
60
  end
56
61
  end
57
62
  end
58
63
 
59
- def render_with_layout(view, path, template, locals)
64
+ def render_with_layout(view, template, path, locals)
60
65
  layout = path && find_layout(path, locals.keys, [formats.first])
61
66
  content = yield(layout)
62
67
 
@@ -84,6 +89,7 @@ module ActionView
84
89
  when String
85
90
  begin
86
91
  if layout.start_with?("/")
92
+ ActiveSupport::Deprecation.warn "Rendering layouts from an absolute path is deprecated."
87
93
  @lookup_context.with_fallbacks.find_template(layout, nil, false, [], details)
88
94
  else
89
95
  @lookup_context.find_template(layout, nil, false, [], details)
@@ -118,7 +118,8 @@ module ActionView
118
118
  renderer.render_to_object(context, options)
119
119
  end
120
120
 
121
- @rendered_format = Template::Types[rendered_template.format]
121
+ rendered_format = rendered_template.format || lookup_context.formats.first
122
+ @rendered_format = Template::Types[rendered_format]
122
123
 
123
124
  rendered_template.body
124
125
  end
@@ -126,7 +127,7 @@ module ActionView
126
127
  # Assign the rendered format to look up context.
127
128
  def _process_format(format)
128
129
  super
129
- lookup_context.formats = [format.to_sym]
130
+ lookup_context.formats = [format.to_sym] if format.to_sym
130
131
  end
131
132
 
132
133
  # Normalize args by converting render "foo" to render :action => "foo" and
@@ -113,51 +113,59 @@ module ActionView
113
113
 
114
114
  eager_autoload do
115
115
  autoload :Error
116
+ autoload :RawFile
116
117
  autoload :Handlers
117
118
  autoload :HTML
118
119
  autoload :Inline
120
+ autoload :Sources
119
121
  autoload :Text
120
122
  autoload :Types
121
123
  end
122
124
 
123
125
  extend Template::Handlers
124
126
 
125
- attr_accessor :locals, :variants, :virtual_path
127
+ attr_reader :identifier, :handler, :original_encoding, :updated_at
128
+ attr_reader :variable, :format, :variant, :locals, :virtual_path
126
129
 
127
- attr_reader :source, :identifier, :handler, :original_encoding, :updated_at
128
- attr_reader :variable, :format
129
-
130
- def initialize(source, identifier, handler, format: nil, **details)
131
- unless format
132
- ActiveSupport::Deprecation.warn "ActionView::Template#initialize requires a format parameter"
133
- format = :html
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 = []
134
134
  end
135
135
 
136
136
  @source = source
137
137
  @identifier = identifier
138
138
  @handler = handler
139
139
  @compiled = false
140
- @original_encoding = nil
141
- @locals = details[:locals] || []
142
- @virtual_path = details[:virtual_path]
140
+ @locals = locals
141
+ @virtual_path = virtual_path
143
142
 
144
143
  @variable = if @virtual_path
145
- base = @virtual_path[-1] == "/" ? "" : File.basename(@virtual_path)
144
+ base = @virtual_path[-1] == "/" ? "" : ::File.basename(@virtual_path)
146
145
  base =~ /\A_?(.*?)(?:\.\w+)*\z/
147
146
  $1.to_sym
148
147
  end
149
148
 
150
- @updated_at = details[:updated_at] || Time.now
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
151
155
  @format = format
152
- @variants = [details[:variant]]
156
+ @variant = variant
153
157
  @compile_mutex = Mutex.new
154
158
  end
155
159
 
156
- def formats=(_)
157
- end
158
- deprecate :formats=
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
160
165
  deprecate def formats; Array(format); end
166
+ deprecate def variants=(_); end
167
+ deprecate def variants; [variant]; end
168
+ deprecate def refresh(_); self; end
161
169
 
162
170
  # Returns whether the underlying handler supports streaming. If so,
163
171
  # a streaming buffer *may* be passed when it starts rendering.
@@ -174,7 +182,7 @@ module ActionView
174
182
  def render(view, locals, buffer = ActionView::OutputBuffer.new, &block)
175
183
  instrument_render_template do
176
184
  compile!(view)
177
- view.run(method_name, self, locals, buffer, &block)
185
+ view._run(method_name, self, locals, buffer, &block)
178
186
  end
179
187
  rescue => e
180
188
  handle_render_error(view, e)
@@ -184,25 +192,6 @@ module ActionView
184
192
  @type ||= Types[format]
185
193
  end
186
194
 
187
- # Receives a view object and return a template similar to self by using @virtual_path.
188
- #
189
- # This method is useful if you have a template object but it does not contain its source
190
- # anymore since it was already compiled. In such cases, all you need to do is to call
191
- # refresh passing in the view object.
192
- #
193
- # Notice this method raises an error if the template to be refreshed does not have a
194
- # virtual path set (true just for inline templates).
195
- def refresh(view)
196
- raise "A template needs to have a virtual path in order to be refreshed" unless @virtual_path
197
- lookup = view.lookup_context
198
- pieces = @virtual_path.split("/")
199
- name = pieces.pop
200
- partial = !!name.sub!(/^_/, "")
201
- lookup.disable_cache do
202
- lookup.find_template(name, [ pieces.join("/") ], partial, @locals)
203
- end
204
- end
205
-
206
195
  def short_identifier
207
196
  @short_identifier ||= defined?(Rails.root) ? identifier.sub("#{Rails.root}/", "") : identifier
208
197
  end
@@ -211,6 +200,10 @@ module ActionView
211
200
  "#<#{self.class.name} #{short_identifier} locals=#{@locals.inspect}>"
212
201
  end
213
202
 
203
+ def source
204
+ @source.to_s
205
+ end
206
+
214
207
  # This method is responsible for properly setting the encoding of the
215
208
  # source. Until this point, we assume that the source is BINARY data.
216
209
  # If no additional information is supplied, we assume the encoding is
@@ -262,11 +255,11 @@ module ActionView
262
255
  # to ensure that references to the template object can be marshalled as well. This means forgoing
263
256
  # the marshalling of the compiler mutex and instantiating that again on unmarshalling.
264
257
  def marshal_dump # :nodoc:
265
- [ @source, @identifier, @handler, @compiled, @original_encoding, @locals, @virtual_path, @updated_at, @format, @variants ]
258
+ [ @source, @identifier, @handler, @compiled, @locals, @virtual_path, @updated_at, @format, @variant ]
266
259
  end
267
260
 
268
261
  def marshal_load(array) # :nodoc:
269
- @source, @identifier, @handler, @compiled, @original_encoding, @locals, @virtual_path, @updated_at, @format, @variants = *array
262
+ @source, @identifier, @handler, @compiled, @locals, @virtual_path, @updated_at, @format, @variant = *array
270
263
  @compile_mutex = Mutex.new
271
264
  end
272
265
 
@@ -292,9 +285,6 @@ module ActionView
292
285
  compile(mod)
293
286
  end
294
287
 
295
- # Just discard the source if we have a virtual path. This
296
- # means we can get the template back.
297
- @source = nil if @virtual_path
298
288
  @compiled = true
299
289
  end
300
290
  end
@@ -326,6 +316,7 @@ module ActionView
326
316
 
327
317
  # Make sure that the resulting String to be eval'd is in the
328
318
  # encoding of the code
319
+ original_source = source
329
320
  source = +<<-end_src
330
321
  def #{method_name}(local_assigns, output_buffer)
331
322
  @virtual_path = #{@virtual_path.inspect};#{locals_code};#{code}
@@ -346,7 +337,14 @@ module ActionView
346
337
  raise WrongEncodingError.new(source, Encoding.default_internal)
347
338
  end
348
339
 
349
- mod.module_eval(source, identifier, 0)
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
350
348
  end
351
349
 
352
350
  def handle_render_error(view, e)
@@ -354,12 +352,7 @@ module ActionView
354
352
  e.sub_template_of(self)
355
353
  raise e
356
354
  else
357
- template = self
358
- unless template.source
359
- template = refresh(view)
360
- template.encode!
361
- end
362
- raise Template::Error.new(template)
355
+ raise Template::Error.new(self)
363
356
  end
364
357
  end
365
358