actionview 7.0.10 → 7.1.0.beta1

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.
Files changed (90) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +235 -404
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +1 -1
  5. data/app/assets/javascripts/rails-ujs.esm.js +668 -0
  6. data/app/assets/javascripts/rails-ujs.js +606 -0
  7. data/lib/action_view/base.rb +28 -7
  8. data/lib/action_view/buffers.rb +106 -8
  9. data/lib/action_view/cache_expiry.rb +40 -43
  10. data/lib/action_view/context.rb +1 -1
  11. data/lib/action_view/deprecator.rb +7 -0
  12. data/lib/action_view/digestor.rb +1 -1
  13. data/lib/action_view/gem_version.rb +4 -4
  14. data/lib/action_view/helpers/active_model_helper.rb +1 -1
  15. data/lib/action_view/helpers/asset_tag_helper.rb +134 -50
  16. data/lib/action_view/helpers/asset_url_helper.rb +6 -5
  17. data/lib/action_view/helpers/atom_feed_helper.rb +5 -5
  18. data/lib/action_view/helpers/cache_helper.rb +3 -9
  19. data/lib/action_view/helpers/capture_helper.rb +24 -10
  20. data/lib/action_view/helpers/content_exfiltration_prevention_helper.rb +70 -0
  21. data/lib/action_view/helpers/controller_helper.rb +6 -0
  22. data/lib/action_view/helpers/csp_helper.rb +2 -2
  23. data/lib/action_view/helpers/csrf_helper.rb +2 -2
  24. data/lib/action_view/helpers/date_helper.rb +17 -19
  25. data/lib/action_view/helpers/debug_helper.rb +3 -3
  26. data/lib/action_view/helpers/form_helper.rb +46 -25
  27. data/lib/action_view/helpers/form_options_helper.rb +2 -1
  28. data/lib/action_view/helpers/form_tag_helper.rb +43 -9
  29. data/lib/action_view/helpers/javascript_helper.rb +1 -0
  30. data/lib/action_view/helpers/number_helper.rb +331 -36
  31. data/lib/action_view/helpers/output_safety_helper.rb +2 -2
  32. data/lib/action_view/helpers/rendering_helper.rb +1 -1
  33. data/lib/action_view/helpers/sanitize_helper.rb +40 -32
  34. data/lib/action_view/helpers/tag_helper.rb +5 -27
  35. data/lib/action_view/helpers/tags/base.rb +11 -52
  36. data/lib/action_view/helpers/tags/collection_check_boxes.rb +1 -0
  37. data/lib/action_view/helpers/tags/collection_radio_buttons.rb +1 -0
  38. data/lib/action_view/helpers/tags/collection_select.rb +3 -0
  39. data/lib/action_view/helpers/tags/date_field.rb +1 -1
  40. data/lib/action_view/helpers/tags/date_select.rb +2 -0
  41. data/lib/action_view/helpers/tags/datetime_field.rb +14 -6
  42. data/lib/action_view/helpers/tags/datetime_local_field.rb +11 -2
  43. data/lib/action_view/helpers/tags/grouped_collection_select.rb +3 -0
  44. data/lib/action_view/helpers/tags/month_field.rb +1 -1
  45. data/lib/action_view/helpers/tags/select.rb +3 -0
  46. data/lib/action_view/helpers/tags/select_renderer.rb +56 -0
  47. data/lib/action_view/helpers/tags/time_field.rb +1 -1
  48. data/lib/action_view/helpers/tags/time_zone_select.rb +3 -0
  49. data/lib/action_view/helpers/tags/week_field.rb +1 -1
  50. data/lib/action_view/helpers/tags/weekday_select.rb +3 -0
  51. data/lib/action_view/helpers/tags.rb +2 -0
  52. data/lib/action_view/helpers/text_helper.rb +100 -138
  53. data/lib/action_view/helpers/translation_helper.rb +3 -3
  54. data/lib/action_view/helpers/url_helper.rb +41 -14
  55. data/lib/action_view/helpers.rb +2 -0
  56. data/lib/action_view/layouts.rb +6 -4
  57. data/lib/action_view/log_subscriber.rb +49 -32
  58. data/lib/action_view/lookup_context.rb +29 -13
  59. data/lib/action_view/path_registry.rb +57 -0
  60. data/lib/action_view/path_set.rb +13 -14
  61. data/lib/action_view/railtie.rb +26 -3
  62. data/lib/action_view/record_identifier.rb +15 -8
  63. data/lib/action_view/renderer/abstract_renderer.rb +1 -1
  64. data/lib/action_view/renderer/collection_renderer.rb +9 -1
  65. data/lib/action_view/renderer/partial_renderer/collection_caching.rb +2 -1
  66. data/lib/action_view/renderer/partial_renderer.rb +2 -1
  67. data/lib/action_view/renderer/renderer.rb +2 -0
  68. data/lib/action_view/renderer/streaming_template_renderer.rb +3 -2
  69. data/lib/action_view/renderer/template_renderer.rb +3 -2
  70. data/lib/action_view/rendering.rb +22 -4
  71. data/lib/action_view/ripper_ast_parser.rb +6 -6
  72. data/lib/action_view/template/error.rb +14 -1
  73. data/lib/action_view/template/handlers/builder.rb +4 -4
  74. data/lib/action_view/template/handlers/erb/erubi.rb +23 -27
  75. data/lib/action_view/template/handlers/erb.rb +73 -1
  76. data/lib/action_view/template/handlers.rb +1 -1
  77. data/lib/action_view/template/html.rb +1 -1
  78. data/lib/action_view/template/raw_file.rb +1 -1
  79. data/lib/action_view/template/renderable.rb +1 -1
  80. data/lib/action_view/template/resolver.rb +10 -2
  81. data/lib/action_view/template/text.rb +1 -1
  82. data/lib/action_view/template/types.rb +25 -34
  83. data/lib/action_view/template.rb +180 -53
  84. data/lib/action_view/template_path.rb +2 -0
  85. data/lib/action_view/test_case.rb +8 -5
  86. data/lib/action_view/unbound_template.rb +15 -5
  87. data/lib/action_view/version.rb +1 -1
  88. data/lib/action_view/view_paths.rb +15 -24
  89. data/lib/action_view.rb +4 -1
  90. metadata +29 -26
@@ -4,18 +4,20 @@ require "thread"
4
4
  require "delegate"
5
5
 
6
6
  module ActionView
7
- # = Action View Template
7
+ # = Action View \Template
8
8
  class Template
9
9
  extend ActiveSupport::Autoload
10
10
 
11
+ STRICT_LOCALS_REGEX = /\#\s+locals:\s+\((.*)\)/
12
+
11
13
  # === Encodings in ActionView::Template
12
14
  #
13
15
  # ActionView::Template is one of a few sources of potential
14
- # encoding issues in Rails. This is because the source for
16
+ # encoding issues in \Rails. This is because the source for
15
17
  # templates are usually read from disk, and Ruby (like most
16
18
  # encoding-aware programming languages) assumes that the
17
19
  # String retrieved through File IO is encoded in the
18
- # <tt>default_external</tt> encoding. In Rails, the default
20
+ # <tt>default_external</tt> encoding. In \Rails, the default
19
21
  # <tt>default_external</tt> encoding is UTF-8.
20
22
  #
21
23
  # As a result, if a user saves their template as ISO-8859-1
@@ -34,13 +36,13 @@ module ActionView
34
36
  # to the problem.
35
37
  # 2. The user can specify the encoding using Ruby-style
36
38
  # encoding comments in any template engine. If such
37
- # a comment is supplied, Rails will apply that encoding
39
+ # a comment is supplied, \Rails will apply that encoding
38
40
  # to the resulting compiled source returned by the
39
41
  # template handler.
40
42
  # 3. In all cases, we transcode the resulting String to
41
43
  # the UTF-8.
42
44
  #
43
- # This means that other parts of Rails can always assume
45
+ # This means that other parts of \Rails can always assume
44
46
  # that templates are encoded in UTF-8, even if the original
45
47
  # source of the template was not UTF-8.
46
48
  #
@@ -51,7 +53,7 @@ module ActionView
51
53
  # === Instructions for template handlers
52
54
  #
53
55
  # The easiest thing for you to do is to simply ignore
54
- # encodings. Rails will hand you the template source
56
+ # encodings. \Rails will hand you the template source
55
57
  # as the default_internal (generally UTF-8), raising
56
58
  # an exception for the user before sending the template
57
59
  # to you if it could not determine the original encoding.
@@ -68,7 +70,7 @@ module ActionView
68
70
  # you may indicate that you will handle encodings yourself
69
71
  # by implementing <tt>handles_encoding?</tt> on your handler.
70
72
  #
71
- # If you do, Rails will not try to encode the String
73
+ # If you do, \Rails will not try to encode the String
72
74
  # into the default_internal, passing you the unaltered
73
75
  # bytes tagged with the assumed encoding (from
74
76
  # default_external).
@@ -107,6 +109,7 @@ module ActionView
107
109
  autoload :Handlers
108
110
  autoload :HTML
109
111
  autoload :Inline
112
+ autoload :Types
110
113
  autoload :Sources
111
114
  autoload :Text
112
115
  autoload :Types
@@ -117,11 +120,24 @@ module ActionView
117
120
  singleton_class.attr_accessor :frozen_string_literal
118
121
  @frozen_string_literal = false
119
122
 
123
+ class << self # :nodoc:
124
+ def mime_types_implementation=(implementation)
125
+ # This method isn't thread-safe, but it's not supposed
126
+ # to be called after initialization
127
+ if self::Types != implementation
128
+ remove_const(:Types)
129
+ const_set(:Types, implementation)
130
+ end
131
+ end
132
+ end
133
+
120
134
  attr_reader :identifier, :handler
121
- attr_reader :variable, :format, :variant, :locals, :virtual_path
135
+ attr_reader :variable, :format, :variant, :virtual_path
136
+
137
+ NONE = Object.new
122
138
 
123
139
  def initialize(source, identifier, handler, locals:, format: nil, variant: nil, virtual_path: nil)
124
- @source = source
140
+ @source = source.dup
125
141
  @identifier = identifier
126
142
  @handler = handler
127
143
  @compiled = false
@@ -137,6 +153,36 @@ module ActionView
137
153
  @format = format
138
154
  @variant = variant
139
155
  @compile_mutex = Mutex.new
156
+ @strict_locals = NONE
157
+ @type = nil
158
+ end
159
+
160
+ # The locals this template has been or will be compiled for, or nil if this
161
+ # is a strict locals template.
162
+ def locals
163
+ if strict_locals?
164
+ nil
165
+ else
166
+ @locals
167
+ end
168
+ end
169
+
170
+ def spot(location) # :nodoc:
171
+ ast = RubyVM::AbstractSyntaxTree.parse(compiled_source, keep_script_lines: true)
172
+ node_id = RubyVM::AbstractSyntaxTree.node_id_for_backtrace_location(location)
173
+ node = find_node_by_id(ast, node_id)
174
+
175
+ ErrorHighlight.spot(node)
176
+ end
177
+
178
+ # Translate an error location returned by ErrorHighlight to the correct
179
+ # source location inside the template.
180
+ def translate_location(backtrace_location, spot)
181
+ if handler.respond_to?(:translate_location)
182
+ handler.translate_location(spot, backtrace_location, encode!) || spot
183
+ else
184
+ spot
185
+ end
140
186
  end
141
187
 
142
188
  # Returns whether the underlying handler supports streaming. If so,
@@ -151,10 +197,15 @@ module ActionView
151
197
  # This method is instrumented as "!render_template.action_view". Notice that
152
198
  # we use a bang in this instrumentation because you don't want to
153
199
  # consume this in production. This is only slow if it's being listened to.
154
- def render(view, locals, buffer = ActionView::OutputBuffer.new, add_to_stack: true, &block)
200
+ def render(view, locals, buffer = nil, add_to_stack: true, &block)
155
201
  instrument_render_template do
156
202
  compile!(view)
157
- view._run(method_name, self, locals, buffer, add_to_stack: add_to_stack, &block)
203
+ if buffer
204
+ view._run(method_name, self, locals, buffer, add_to_stack: add_to_stack, has_strict_locals: strict_locals?, &block)
205
+ nil
206
+ else
207
+ view._run(method_name, self, locals, OutputBuffer.new, add_to_stack: add_to_stack, has_strict_locals: strict_locals?, &block)&.to_s
208
+ end
158
209
  end
159
210
  rescue => e
160
211
  handle_render_error(view, e)
@@ -169,20 +220,23 @@ module ActionView
169
220
  end
170
221
 
171
222
  def inspect
172
- "#<#{self.class.name} #{short_identifier} locals=#{@locals.inspect}>"
223
+ "#<#{self.class.name} #{short_identifier} locals=#{locals.inspect}>"
173
224
  end
174
225
 
175
226
  def source
176
227
  @source.to_s
177
228
  end
178
229
 
230
+ LEADING_ENCODING_REGEXP = /\A#{ENCODING_FLAG}/
231
+ private_constant :LEADING_ENCODING_REGEXP
232
+
179
233
  # This method is responsible for properly setting the encoding of the
180
234
  # source. Until this point, we assume that the source is BINARY data.
181
235
  # If no additional information is supplied, we assume the encoding is
182
236
  # the same as <tt>Encoding.default_external</tt>.
183
237
  #
184
238
  # The user can also specify the encoding via a comment on the first
185
- # line of the template (<tt># encoding: NAME-OF-ENCODING</tt>). This will work
239
+ # line of the template (# encoding: NAME-OF-ENCODING). This will work
186
240
  # with any template engine, as we process out the encoding comment
187
241
  # before passing the source on to the template engine, leaving a
188
242
  # blank line in its stead.
@@ -194,7 +248,7 @@ module ActionView
194
248
  # Look for # encoding: *. If we find one, we'll encode the
195
249
  # String in that encoding, otherwise, we'll use the
196
250
  # default external encoding.
197
- if source.sub!(/\A#{ENCODING_FLAG}/, "")
251
+ if source.sub!(LEADING_ENCODING_REGEXP, "")
198
252
  encoding = magic_encoding = $1
199
253
  else
200
254
  encoding = Encoding.default_external
@@ -222,6 +276,32 @@ module ActionView
222
276
  end
223
277
  end
224
278
 
279
+ # This method is responsible for marking a template as having strict locals
280
+ # which means the template can only accept the locals defined in a magic
281
+ # comment. For example, if your template acceps the locals +title+ and
282
+ # +comment_count+, add the following to your template file:
283
+ #
284
+ # <%# locals: (title: "Default title", comment_count: 0) %>
285
+ #
286
+ # Strict locals are useful for validating template arguments and for
287
+ # specifying defaults.
288
+ def strict_locals!
289
+ if @strict_locals == NONE
290
+ self.source.sub!(STRICT_LOCALS_REGEX, "")
291
+ @strict_locals = $1
292
+
293
+ return if @strict_locals.nil? # Magic comment not found
294
+
295
+ @strict_locals = "**nil" if @strict_locals.blank?
296
+ end
297
+
298
+ @strict_locals
299
+ end
300
+
301
+ # Returns whether a template is using strict locals.
302
+ def strict_locals?
303
+ strict_locals!
304
+ end
225
305
 
226
306
  # Exceptions are marshalled when using the parallel test runner with DRb, so we need
227
307
  # to ensure that references to the template object can be marshalled as well. This means forgoing
@@ -235,7 +315,26 @@ module ActionView
235
315
  @compile_mutex = Mutex.new
236
316
  end
237
317
 
318
+ def method_name # :nodoc:
319
+ @method_name ||= begin
320
+ m = +"_#{identifier_method_name}__#{@identifier.hash}_#{__id__}"
321
+ m.tr!("-", "_")
322
+ m
323
+ end
324
+ end
325
+
238
326
  private
327
+ def find_node_by_id(node, node_id)
328
+ return node if node.node_id == node_id
329
+
330
+ node.children.grep(node.class).each do |child|
331
+ found = find_node_by_id(child, node_id)
332
+ return found if found
333
+ end
334
+
335
+ false
336
+ end
337
+
239
338
  # Compile a template. This method ensures a template is compiled
240
339
  # just once and removes the source after it is compiled.
241
340
  def compile!(view)
@@ -260,27 +359,25 @@ module ActionView
260
359
  end
261
360
  end
262
361
 
263
- # Among other things, this method is responsible for properly setting
264
- # the encoding of the compiled template.
265
- #
266
- # If the template engine handles encodings, we send the encoded
267
- # String to the engine without further processing. This allows
268
- # the template engine to support additional mechanisms for
269
- # specifying the encoding. For instance, ERB supports <%# encoding: %>
270
- #
271
- # Otherwise, after we figure out the correct encoding, we then
272
- # encode the source into <tt>Encoding.default_internal</tt>.
273
- # In general, this means that templates will be UTF-8 inside of Rails,
274
- # regardless of the original source encoding.
275
- def compile(mod)
362
+ # This method compiles the source of the template. The compilation of templates
363
+ # involves setting strict_locals! if applicable, encoding the template, and setting
364
+ # frozen string literal.
365
+ def compiled_source
366
+ set_strict_locals = strict_locals!
276
367
  source = encode!
277
368
  code = @handler.call(self, source)
278
369
 
370
+ method_arguments =
371
+ if set_strict_locals
372
+ "output_buffer, #{set_strict_locals}"
373
+ else
374
+ "local_assigns, output_buffer"
375
+ end
376
+
279
377
  # Make sure that the resulting String to be eval'd is in the
280
378
  # encoding of the code
281
- original_source = source
282
379
  source = +<<-end_src
283
- def #{method_name}(local_assigns, output_buffer, &_)
380
+ def #{method_name}(#{method_arguments})
284
381
  @virtual_path = #{@virtual_path.inspect};#{locals_code};#{code}
285
382
  end
286
383
  end_src
@@ -299,17 +396,61 @@ module ActionView
299
396
  raise WrongEncodingError.new(source, Encoding.default_internal)
300
397
  end
301
398
 
399
+ if Template.frozen_string_literal
400
+ "# frozen_string_literal: true\n#{source}"
401
+ else
402
+ source
403
+ end
404
+ end
405
+
406
+ # Among other things, this method is responsible for properly setting
407
+ # the encoding of the compiled template.
408
+ #
409
+ # If the template engine handles encodings, we send the encoded
410
+ # String to the engine without further processing. This allows
411
+ # the template engine to support additional mechanisms for
412
+ # specifying the encoding. For instance, ERB supports <%# encoding: %>
413
+ #
414
+ # Otherwise, after we figure out the correct encoding, we then
415
+ # encode the source into <tt>Encoding.default_internal</tt>.
416
+ # In general, this means that templates will be UTF-8 inside of Rails,
417
+ # regardless of the original source encoding.
418
+ def compile(mod)
302
419
  begin
303
- if Template.frozen_string_literal
304
- mod.module_eval("# frozen_string_literal: true\n#{source}", identifier, -1)
305
- else
306
- mod.module_eval(source, identifier, 0)
307
- end
420
+ mod.module_eval(compiled_source, identifier, offset)
308
421
  rescue SyntaxError
309
422
  # Account for when code in the template is not syntactically valid; e.g. if we're using
310
423
  # ERB and the user writes <%= foo( %>, attempting to call a helper `foo` and interpolate
311
424
  # the result into the template, but missing an end parenthesis.
312
- raise SyntaxErrorInTemplate.new(self, original_source)
425
+ raise SyntaxErrorInTemplate.new(self, encode!)
426
+ end
427
+
428
+ return unless strict_locals?
429
+
430
+ # Check compiled method parameters to ensure that only kwargs
431
+ # were provided as strict locals, preventing `locals: (foo, *foo)` etc
432
+ # and allowing `locals: (foo:)`.
433
+
434
+ non_kwarg_parameters =
435
+ (mod.instance_method(method_name).parameters - [[:req, :output_buffer]]).
436
+ select { |parameter| ![:keyreq, :key, :keyrest, :nokey].include?(parameter[0]) }
437
+
438
+ return unless non_kwarg_parameters.any?
439
+
440
+ mod.undef_method(method_name)
441
+
442
+ raise ArgumentError.new(
443
+ "#{non_kwarg_parameters.map { |_, name| "`#{name}`" }.to_sentence} set as non-keyword " \
444
+ "#{'argument'.pluralize(non_kwarg_parameters.length)} for #{short_identifier}. " \
445
+ "Locals can only be set as keyword arguments."
446
+ )
447
+ end
448
+
449
+ def offset
450
+ if Template.frozen_string_literal
451
+ -1
452
+ else
453
+ 0
313
454
  end
314
455
  end
315
456
 
@@ -323,32 +464,18 @@ module ActionView
323
464
  end
324
465
 
325
466
  def locals_code
467
+ return "" if strict_locals?
468
+
326
469
  # Only locals with valid variable names get set directly. Others will
327
470
  # still be available in local_assigns.
328
471
  locals = @locals - Module::RUBY_RESERVED_KEYWORDS
329
- deprecated_locals = locals.grep(/\A@+/)
330
- if deprecated_locals.any?
331
- ActiveSupport::Deprecation.warn(<<~MSG)
332
- Passing instance variables to `render` is deprecated.
333
- In Rails 7.1, #{deprecated_locals.to_sentence} will be ignored.
334
- MSG
335
- locals = locals.grep(/\A@?(?![A-Z0-9])(?:[[:alnum:]_]|[^\0-\177])+\z/)
336
- else
337
- locals = locals.grep(/\A(?![A-Z0-9])(?:[[:alnum:]_]|[^\0-\177])+\z/)
338
- end
472
+
473
+ locals = locals.grep(/\A(?![A-Z0-9])(?:[[:alnum:]_]|[^\0-\177])+\z/)
339
474
 
340
475
  # Assign for the same variable is to suppress unused variable warning
341
476
  locals.each_with_object(+"") { |key, code| code << "#{key} = local_assigns[:#{key}]; #{key} = #{key};" }
342
477
  end
343
478
 
344
- def method_name
345
- @method_name ||= begin
346
- m = +"_#{identifier_method_name}__#{@identifier.hash}_#{__id__}"
347
- m.tr!("-", "_")
348
- m
349
- end
350
- end
351
-
352
479
  def identifier_method_name
353
480
  short_identifier.tr("^a-z_", "_")
354
481
  end
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActionView
4
+ # = Action View \TemplatePath
5
+ #
4
6
  # Represents a template path within ActionView's lookup and rendering system,
5
7
  # like "users/show"
6
8
  #
@@ -110,9 +110,7 @@ module ActionView
110
110
  @controller = controller_class.new
111
111
  @request = @controller.request
112
112
  @view_flow = ActionView::OutputFlow.new
113
- # empty string ensures buffer has UTF-8 encoding as
114
- # new without arguments returns ASCII-8BIT encoded buffer like String#new
115
- @output_buffer = ActiveSupport::SafeBuffer.new ""
113
+ @output_buffer = ActionView::OutputBuffer.new
116
114
  @rendered = +""
117
115
 
118
116
  test_case_instance = self
@@ -181,7 +179,7 @@ module ActionView
181
179
  private
182
180
  # Need to experiment if this priority is the best one: rendered => output_buffer
183
181
  def document_root_element
184
- Nokogiri::HTML::Document.parse(@rendered.blank? ? @output_buffer : @rendered).root
182
+ Rails::Dom::Testing.html_document.parse(@rendered.blank? ? @output_buffer.to_str : @rendered).root
185
183
  end
186
184
 
187
185
  module Locals
@@ -227,6 +225,10 @@ module ActionView
227
225
  :@_result,
228
226
  :@_routes,
229
227
  :@controller,
228
+ :@_controller,
229
+ :@_request,
230
+ :@_config,
231
+ :@_default_form_builder,
230
232
  :@_layouts,
231
233
  :@_files,
232
234
  :@_rendered_views,
@@ -245,7 +247,7 @@ module ActionView
245
247
  :@view_context_class,
246
248
  :@view_flow,
247
249
  :@_subscribers,
248
- :@html_document
250
+ :@html_document,
249
251
  ]
250
252
 
251
253
  def _user_defined_ivars
@@ -277,6 +279,7 @@ module ActionView
277
279
  super
278
280
  end
279
281
  end
282
+ ruby2_keywords(:method_missing)
280
283
 
281
284
  def respond_to_missing?(name, include_private = false)
282
285
  begin
@@ -18,21 +18,31 @@ module ActionView
18
18
  end
19
19
 
20
20
  def bind_locals(locals)
21
- if template = @templates[locals]
22
- template
23
- else
21
+ unless template = @templates[locals]
24
22
  @write_lock.synchronize do
25
23
  normalized_locals = normalize_locals(locals)
26
24
 
27
25
  # We need ||=, both to dedup on the normalized locals and to check
28
26
  # while holding the lock.
29
- @templates[normalized_locals] ||= build_template(normalized_locals)
27
+ template = (@templates[normalized_locals] ||= build_template(normalized_locals))
30
28
 
31
29
  # This may have already been assigned, but we've already de-dup'd so
32
30
  # reassignment is fine.
33
- @templates[locals.dup] = @templates[normalized_locals]
31
+ @templates[locals.dup] = template
32
+
33
+ if template.strict_locals?
34
+ # Under strict locals, we only need one template.
35
+ # This replaces the @templates Concurrent::Map with a hash which
36
+ # returns this template for every key.
37
+ @templates = Hash.new(template).freeze
38
+ end
34
39
  end
35
40
  end
41
+ template
42
+ end
43
+
44
+ def built_templates # :nodoc:
45
+ @templates.values
36
46
  end
37
47
 
38
48
  private
@@ -3,7 +3,7 @@
3
3
  require_relative "gem_version"
4
4
 
5
5
  module ActionView
6
- # Returns the currently loaded version of Action View as a <tt>Gem::Version</tt>.
6
+ # Returns the currently loaded version of Action View as a +Gem::Version+.
7
7
  def self.version
8
8
  gem_version
9
9
  end
@@ -5,7 +5,7 @@ module ActionView
5
5
  extend ActiveSupport::Concern
6
6
 
7
7
  included do
8
- ViewPaths.set_view_paths(self, ActionView::PathSet.new.freeze)
8
+ ActionView::PathRegistry.set_view_paths(self, ActionView::PathSet.new.freeze)
9
9
  end
10
10
 
11
11
  delegate :template_exists?, :any_templates?, :view_paths, :formats, :formats=,
@@ -13,11 +13,11 @@ module ActionView
13
13
 
14
14
  module ClassMethods
15
15
  def _view_paths
16
- ViewPaths.get_view_paths(self)
16
+ ActionView::PathRegistry.get_view_paths(self)
17
17
  end
18
18
 
19
19
  def _view_paths=(paths)
20
- ViewPaths.set_view_paths(self, paths)
20
+ ActionView::PathRegistry.set_view_paths(self, paths)
21
21
  end
22
22
 
23
23
  def _prefixes # :nodoc:
@@ -28,6 +28,13 @@ module ActionView
28
28
  end
29
29
  end
30
30
 
31
+ def _build_view_paths(paths) # :nodoc:
32
+ return paths if ActionView::PathSet === paths
33
+
34
+ paths = ActionView::PathRegistry.cast_file_system_resolvers(paths)
35
+ ActionView::PathSet.new(paths)
36
+ end
37
+
31
38
  # Append a path to the list of view paths for this controller.
32
39
  #
33
40
  # ==== Parameters
@@ -35,7 +42,7 @@ module ActionView
35
42
  # the default view path. You may also provide a custom view path
36
43
  # (see ActionView::PathSet for more information)
37
44
  def append_view_path(path)
38
- self._view_paths = view_paths + Array(path)
45
+ self._view_paths = view_paths + _build_view_paths(path)
39
46
  end
40
47
 
41
48
  # Prepend a path to the list of view paths for this controller.
@@ -45,7 +52,7 @@ module ActionView
45
52
  # the default view path. You may also provide a custom view path
46
53
  # (see ActionView::PathSet for more information)
47
54
  def prepend_view_path(path)
48
- self._view_paths = ActionView::PathSet.new(Array(path) + view_paths)
55
+ self._view_paths = _build_view_paths(path) + view_paths
49
56
  end
50
57
 
51
58
  # A list of all of the default view paths for this controller.
@@ -59,7 +66,7 @@ module ActionView
59
66
  # * <tt>paths</tt> - If a PathSet is provided, use that;
60
67
  # otherwise, process the parameter into a PathSet.
61
68
  def view_paths=(paths)
62
- self._view_paths = ActionView::PathSet.new(Array(paths))
69
+ self._view_paths = _build_view_paths(paths)
63
70
  end
64
71
 
65
72
  private
@@ -70,22 +77,6 @@ module ActionView
70
77
  end
71
78
  end
72
79
 
73
- # :stopdoc:
74
- @all_view_paths = {}
75
-
76
- def self.get_view_paths(klass)
77
- @all_view_paths[klass] || get_view_paths(klass.superclass)
78
- end
79
-
80
- def self.set_view_paths(klass, paths)
81
- @all_view_paths[klass] = paths
82
- end
83
-
84
- def self.all_view_paths
85
- @all_view_paths.values.uniq
86
- end
87
- # :startdoc:
88
-
89
80
  # The prefixes used in render "foo" shortcuts.
90
81
  def _prefixes # :nodoc:
91
82
  self.class._prefixes
@@ -110,7 +101,7 @@ module ActionView
110
101
  # the default view path. You may also provide a custom view path
111
102
  # (see ActionView::PathSet for more information)
112
103
  def append_view_path(path)
113
- lookup_context.view_paths.push(*path)
104
+ lookup_context.append_view_paths(self.class._build_view_paths(path))
114
105
  end
115
106
 
116
107
  # Prepend a path to the list of view paths for the current LookupContext.
@@ -120,7 +111,7 @@ module ActionView
120
111
  # the default view path. You may also provide a custom view path
121
112
  # (see ActionView::PathSet for more information)
122
113
  def prepend_view_path(path)
123
- lookup_context.view_paths.unshift(*path)
114
+ lookup_context.prepend_view_paths(self.class._build_view_paths(path))
124
115
  end
125
116
  end
126
117
  end
data/lib/action_view.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  #--
4
- # Copyright (c) 2004-2022 David Heinemeier Hansson
4
+ # Copyright (c) David Heinemeier Hansson
5
5
  #
6
6
  # Permission is hereby granted, free of charge, to any person obtaining
7
7
  # a copy of this software and associated documentation files (the
@@ -26,7 +26,9 @@
26
26
  require "active_support"
27
27
  require "active_support/rails"
28
28
  require "action_view/version"
29
+ require "action_view/deprecator"
29
30
 
31
+ # :include: actionview/README.rdoc
30
32
  module ActionView
31
33
  extend ActiveSupport::Autoload
32
34
 
@@ -39,6 +41,7 @@ module ActionView
39
41
  autoload :Helpers
40
42
  autoload :LookupContext
41
43
  autoload :Layouts
44
+ autoload :PathRegistry
42
45
  autoload :PathSet
43
46
  autoload :RecordIdentifier
44
47
  autoload :Rendering