actionview 8.0.0.1 → 8.0.2

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9d33aa301544845fe17d4501a9a07972da170fb153d18721c24e48573b159a0f
4
- data.tar.gz: 2c4313ab6fba01543c67968f072143c6c7de15859f3b7291862515e9a574cd3f
3
+ metadata.gz: cc81c16e4d502ce1736e2ff17c85dbb50ed6209d6ded187af4f846d05540f551
4
+ data.tar.gz: faa7c34dff4cfc391389c56b52afd0cbbbdbaf5ee7805ae3e1d1fd78940f1195
5
5
  SHA512:
6
- metadata.gz: b8f2dfbfd90b90889e5609bea067be9af87189f3cce45aed52dc250ab20d9d89d195091426bc861d514c4f28cf94fc465a67441c7570257d405685731e364ce2
7
- data.tar.gz: c77d32aeb4c2f4fb9b0804e4e387df4102508417b08539fd0d485e1bef8135726d64ccd58cff1e892931ec5451d13cf1aefaa4cbb29a3b9ac133438cdedbf389
6
+ metadata.gz: 1e9c0c527c580719aea26c3a8499d1b1b82e4da5074271403c0060d0d96b604e23fe953ae8ede37d901ba7830fd3700a82297be2d29d574845eb79b559277d0f
7
+ data.tar.gz: 24a3fc73c41d00476204519044e278f8c545db961ac02e88d810325f6cc7d831d410e203e1691767b1cca6672c0ab2d0c5b19179276c47a672b8489827d0545d
data/CHANGELOG.md CHANGED
@@ -1,3 +1,57 @@
1
+ ## Rails 8.0.2 (March 12, 2025) ##
2
+
3
+ * No changes.
4
+
5
+
6
+ ## Rails 8.0.2 (March 12, 2025) ##
7
+
8
+ * Respect `html_options[:form]` when `collection_checkboxes` generates the
9
+ hidden `<input>`.
10
+
11
+ *Riccardo Odone*
12
+
13
+ * Layouts have access to local variables passed to `render`.
14
+
15
+ This fixes #31680 which was a regression in Rails 5.1.
16
+
17
+ *Mike Dalessio*
18
+
19
+ * Argument errors related to strict locals in templates now raise an
20
+ `ActionView::StrictLocalsError`, and all other argument errors are reraised as-is.
21
+
22
+ Previously, any `ArgumentError` raised during template rendering was swallowed during strict
23
+ local error handling, so that an `ArgumentError` unrelated to strict locals (e.g., a helper
24
+ method invoked with incorrect arguments) would be replaced by a similar `ArgumentError` with an
25
+ unrelated backtrace, making it difficult to debug templates.
26
+
27
+ Now, any `ArgumentError` unrelated to strict locals is reraised, preserving the original
28
+ backtrace for developers.
29
+
30
+ Also note that `ActionView::StrictLocalsError` is a subclass of `ArgumentError`, so any existing
31
+ code that rescues `ArgumentError` will continue to work.
32
+
33
+ Fixes #52227.
34
+
35
+ *Mike Dalessio*
36
+
37
+ * Fix stack overflow error in dependency tracker when dealing with circular dependencies
38
+
39
+ *Jean Boussier*
40
+
41
+ ## Rails 8.0.1 (December 13, 2024) ##
42
+
43
+ * Fix a crash in ERB template error highlighting when the error occurs on a
44
+ line in the compiled template that is past the end of the source template.
45
+
46
+ *Martin Emde*
47
+
48
+ * Improve reliability of ERB template error highlighting.
49
+ Fix infinite loops and crashes in highlighting and
50
+ improve tolerance for alternate ERB handlers.
51
+
52
+ *Martin Emde*
53
+
54
+
1
55
  ## Rails 8.0.0.1 (December 10, 2024) ##
2
56
 
3
57
  * No changes.
@@ -267,15 +267,12 @@ module ActionView # :nodoc:
267
267
  begin
268
268
  public_send(method, locals, buffer, **locals, &block)
269
269
  rescue ArgumentError => argument_error
270
- raise(
271
- ArgumentError,
272
- argument_error.
273
- message.
274
- gsub("unknown keyword:", "unknown local:").
275
- gsub("missing keyword:", "missing local:").
276
- gsub("no keywords accepted", "no locals accepted").
277
- concat(" for #{@current_template.short_identifier}")
278
- )
270
+ public_send_line = __LINE__ - 2
271
+ frame = argument_error.backtrace_locations[1]
272
+ if frame.path == __FILE__ && frame.lineno == public_send_line
273
+ raise StrictLocalsError.new(argument_error, @current_template)
274
+ end
275
+ raise
279
276
  end
280
277
  else
281
278
  public_send(method, locals, buffer, &block)
@@ -107,8 +107,12 @@ module ActionView
107
107
  end.join("-")
108
108
  end
109
109
 
110
- def to_dep_map
111
- children.any? ? { name => children.map(&:to_dep_map) } : name
110
+ def to_dep_map(seen = Set.new.compare_by_identity)
111
+ if seen.add?(self)
112
+ children.any? ? { name => children.map { |c| c.to_dep_map(seen) } } : name
113
+ else # the tree has a cycle
114
+ name
115
+ end
112
116
  end
113
117
  end
114
118
 
@@ -9,8 +9,8 @@ module ActionView
9
9
  module VERSION
10
10
  MAJOR = 8
11
11
  MINOR = 0
12
- TINY = 0
13
- PRE = "1"
12
+ TINY = 2
13
+ PRE = nil
14
14
 
15
15
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
16
16
  end
@@ -197,7 +197,7 @@ module ActionView
197
197
  CachingRegistry.caching?
198
198
  end
199
199
 
200
- # Raises +UncacheableFragmentError+ when called from within a +cache+ block.
200
+ # Raises UncacheableFragmentError when called from within a +cache+ block.
201
201
  #
202
202
  # Useful to denote helper methods that can't participate in fragment caching:
203
203
  #
@@ -206,7 +206,7 @@ module ActionView
206
206
  # "#{project.name} - #{Time.now}"
207
207
  # end
208
208
  #
209
- # # Which will then raise if used within a +cache+ block:
209
+ # # Which will then raise if used within a `cache` block:
210
210
  # <% cache project do %>
211
211
  # <%= project_name_with_time(project) %>
212
212
  # <% end %>
@@ -31,7 +31,7 @@ module ActionView
31
31
  # of the resource should show the current values of those attributes.
32
32
  #
33
33
  # In \Rails, this is usually achieved by creating the form using either
34
- # +form_with+ or +form_for+ and a number of related helper methods. These
34
+ # #form_with or #form_for and a number of related helper methods. These
35
35
  # methods generate an appropriate <tt>form</tt> tag and yield a form
36
36
  # builder object that knows the model the form is about. Input fields are
37
37
  # created by calling methods defined on the form builder, which means they
@@ -42,7 +42,7 @@ module ActionView
42
42
  #
43
43
  # For example, to create a new person you typically set up a new instance of
44
44
  # +Person+ in the <tt>PeopleController#new</tt> action, <tt>@person</tt>, and
45
- # in the view template pass that object to +form_with+ or +form_for+:
45
+ # in the view template pass that object to #form_with or #form_for:
46
46
  #
47
47
  # <%= form_with model: @person do |f| %>
48
48
  # <%= f.label :first_name %>:
@@ -209,7 +209,7 @@ module ActionView
209
209
  # are designed to work with an object as base, like
210
210
  # FormOptionsHelper#collection_select and DateHelper#datetime_select.
211
211
  #
212
- # === #form_for with a model object
212
+ # === +form_for+ with a model object
213
213
  #
214
214
  # In the examples above, the object to be created or edited was
215
215
  # represented by a symbol passed to +form_for+, and we noted that
@@ -364,7 +364,7 @@ module ActionView
364
364
  #
365
365
  # === Removing hidden model id's
366
366
  #
367
- # The form_for method automatically includes the model id as a hidden field in the form.
367
+ # The +form_for+ method automatically includes the model id as a hidden field in the form.
368
368
  # This is used to maintain the correlation between the form data and its associated model.
369
369
  # Some ORM systems do not use IDs on nested models so in this case you want to be able
370
370
  # to disable the hidden id.
@@ -784,12 +784,12 @@ module ActionView
784
784
  end
785
785
  end
786
786
 
787
- # Creates a scope around a specific model object like +form_with+, but
787
+ # Creates a scope around a specific model object like #form_with, but
788
788
  # doesn't create the form tags themselves. This makes +fields_for+
789
789
  # suitable for specifying additional model objects in the same form.
790
790
  #
791
- # Although the usage and purpose of +fields_for+ is similar to +form_with+'s,
792
- # its method signature is slightly different. Like +form_with+, it yields
791
+ # Although the usage and purpose of +fields_for+ is similar to #form_with's,
792
+ # its method signature is slightly different. Like #form_with, it yields
793
793
  # a FormBuilder object associated with a particular model object to a block,
794
794
  # and within the block allows methods to be called on the builder to
795
795
  # generate fields associated with the model object. Fields may reflect
@@ -848,7 +848,7 @@ module ActionView
848
848
  # === Nested Attributes Examples
849
849
  #
850
850
  # When the object belonging to the current scope has a nested attribute
851
- # writer for a certain attribute, fields_for will yield a new scope
851
+ # writer for a certain attribute, +fields_for+ will yield a new scope
852
852
  # for that attribute. This allows you to create forms that set or change
853
853
  # the attributes of a parent object and its associations in one go.
854
854
  #
@@ -937,7 +937,7 @@ module ActionView
937
937
  # end
938
938
  #
939
939
  # Note that the <tt>projects_attributes=</tt> writer method is in fact
940
- # required for fields_for to correctly identify <tt>:projects</tt> as a
940
+ # required for +fields_for+ to correctly identify <tt>:projects</tt> as a
941
941
  # collection, and the correct indices to be set in the form markup.
942
942
  #
943
943
  # When projects is already an association on Person you can use
@@ -949,7 +949,7 @@ module ActionView
949
949
  # end
950
950
  #
951
951
  # This model can now be used with a nested fields_for. The block given to
952
- # the nested fields_for call will be repeated for each instance in the
952
+ # the nested +fields_for+ call will be repeated for each instance in the
953
953
  # collection:
954
954
  #
955
955
  # <%= form_with model: @person do |person_form| %>
@@ -1021,10 +1021,10 @@ module ActionView
1021
1021
  # ...
1022
1022
  # <% end %>
1023
1023
  #
1024
- # Note that fields_for will automatically generate a hidden field
1024
+ # Note that +fields_for+ will automatically generate a hidden field
1025
1025
  # to store the ID of the record if it responds to <tt>persisted?</tt>.
1026
1026
  # There are circumstances where this hidden field is not needed and you
1027
- # can pass <tt>include_id: false</tt> to prevent fields_for from
1027
+ # can pass <tt>include_id: false</tt> to prevent +fields_for+ from
1028
1028
  # rendering it automatically.
1029
1029
  def fields_for(record_name, record_object = nil, options = {}, &block)
1030
1030
  options = { model: record_object, allow_method_names_outside_object: false, skip_default_ids: false }.merge!(options)
@@ -1033,7 +1033,7 @@ module ActionView
1033
1033
  end
1034
1034
 
1035
1035
  # Scopes input fields with either an explicit scope or model.
1036
- # Like +form_with+ does with <tt>:scope</tt> or <tt>:model</tt>,
1036
+ # Like #form_with does with <tt>:scope</tt> or <tt>:model</tt>,
1037
1037
  # except it doesn't output the form tags.
1038
1038
  #
1039
1039
  # # Using a scope prefixes the input field names:
@@ -1048,7 +1048,7 @@ module ActionView
1048
1048
  # <% end %>
1049
1049
  # # => <input type="text" name="comment[body]" value="full bodied">
1050
1050
  #
1051
- # # Using +fields+ with +form_with+:
1051
+ # # Using `fields` with `form_with`:
1052
1052
  # <%= form_with model: @article do |form| %>
1053
1053
  # <%= form.text_field :title %>
1054
1054
  #
@@ -1057,13 +1057,13 @@ module ActionView
1057
1057
  # <% end %>
1058
1058
  # <% end %>
1059
1059
  #
1060
- # Much like +form_with+ a FormBuilder instance associated with the scope
1060
+ # Much like #form_with a FormBuilder instance associated with the scope
1061
1061
  # or model is yielded, so any generated field names are prefixed with
1062
1062
  # either the passed scope or the scope inferred from the <tt>:model</tt>.
1063
1063
  #
1064
1064
  # === Mixing with other form helpers
1065
1065
  #
1066
- # While +form_with+ uses a FormBuilder object it's possible to mix and
1066
+ # While #form_with uses a FormBuilder object it's possible to mix and
1067
1067
  # match the stand-alone FormHelper methods and methods
1068
1068
  # from FormTagHelper:
1069
1069
  #
@@ -1221,7 +1221,7 @@ module ActionView
1221
1221
  # hash with +options+. These options will be tagged onto the HTML as an HTML element attribute as in the example
1222
1222
  # shown.
1223
1223
  #
1224
- # Using this method inside a +form_with+ block will set the enclosing form's encoding to <tt>multipart/form-data</tt>.
1224
+ # Using this method inside a #form_with block will set the enclosing form's encoding to <tt>multipart/form-data</tt>.
1225
1225
  #
1226
1226
  # ==== Options
1227
1227
  # * Creates standard HTML attributes for the tag.
@@ -1326,7 +1326,7 @@ module ActionView
1326
1326
  # the elements of the array. For each item with a checked check box you
1327
1327
  # get an extra ghost item with only that attribute, assigned to "0".
1328
1328
  #
1329
- # In that case it is preferable to either use +checkbox_tag+ or to use
1329
+ # In that case it is preferable to either use FormTagHelper#checkbox_tag or to use
1330
1330
  # hashes instead of arrays.
1331
1331
  #
1332
1332
  # ==== Examples
@@ -1632,7 +1632,7 @@ module ActionView
1632
1632
  #
1633
1633
  # A +FormBuilder+ object is associated with a particular model object and
1634
1634
  # allows you to generate fields associated with the model object. The
1635
- # +FormBuilder+ object is yielded when using +form_with+ or +fields_for+.
1635
+ # +FormBuilder+ object is yielded when using #form_with or #fields_for.
1636
1636
  # For example:
1637
1637
  #
1638
1638
  # <%= form_with model: @person do |person_form| %>
@@ -1770,7 +1770,7 @@ module ActionView
1770
1770
  # <% end %>
1771
1771
  #
1772
1772
  # In the example above, the <tt><input type="text"></tt> element built by
1773
- # the call to <tt>FormBuilder#text_field</tt> declares an
1773
+ # the call to #text_field declares an
1774
1774
  # <tt>aria-describedby</tt> attribute referencing the <tt><span></tt>
1775
1775
  # element, sharing a common <tt>id</tt> root (<tt>article_title</tt>, in this
1776
1776
  # case).
@@ -2033,12 +2033,12 @@ module ActionView
2033
2033
  end
2034
2034
  alias_method :text_area, :textarea
2035
2035
 
2036
- # Creates a scope around a specific model object like +form_with+, but
2036
+ # Creates a scope around a specific model object like #form_with, but
2037
2037
  # doesn't create the form tags themselves. This makes +fields_for+
2038
2038
  # suitable for specifying additional model objects in the same form.
2039
2039
  #
2040
- # Although the usage and purpose of +fields_for+ is similar to +form_with+'s,
2041
- # its method signature is slightly different. Like +form_with+, it yields
2040
+ # Although the usage and purpose of +fields_for+ is similar to #form_with's,
2041
+ # its method signature is slightly different. Like #form_with, it yields
2042
2042
  # a FormBuilder object associated with a particular model object to a block,
2043
2043
  # and within the block allows methods to be called on the builder to
2044
2044
  # generate fields associated with the model object. Fields may reflect
@@ -2109,7 +2109,7 @@ module ActionView
2109
2109
  # === Nested Attributes Examples
2110
2110
  #
2111
2111
  # When the object belonging to the current scope has a nested attribute
2112
- # writer for a certain attribute, fields_for will yield a new scope
2112
+ # writer for a certain attribute, +fields_for+ will yield a new scope
2113
2113
  # for that attribute. This allows you to create forms that set or change
2114
2114
  # the attributes of a parent object and its associations in one go.
2115
2115
  #
@@ -2140,7 +2140,7 @@ module ActionView
2140
2140
  # end
2141
2141
  # end
2142
2142
  #
2143
- # This model can now be used with a nested fields_for, like so:
2143
+ # This model can now be used with a nested +fields_for+, like so:
2144
2144
  #
2145
2145
  # <%= form_with model: @person do |person_form| %>
2146
2146
  # ...
@@ -2198,7 +2198,7 @@ module ActionView
2198
2198
  # end
2199
2199
  #
2200
2200
  # Note that the <tt>projects_attributes=</tt> writer method is in fact
2201
- # required for fields_for to correctly identify <tt>:projects</tt> as a
2201
+ # required for +fields_for+ to correctly identify <tt>:projects</tt> as a
2202
2202
  # collection, and the correct indices to be set in the form markup.
2203
2203
  #
2204
2204
  # When projects is already an association on Person you can use
@@ -2209,8 +2209,8 @@ module ActionView
2209
2209
  # accepts_nested_attributes_for :projects
2210
2210
  # end
2211
2211
  #
2212
- # This model can now be used with a nested fields_for. The block given to
2213
- # the nested fields_for call will be repeated for each instance in the
2212
+ # This model can now be used with a nested +fields_for+. The block given to
2213
+ # the nested +fields_for+ call will be repeated for each instance in the
2214
2214
  # collection:
2215
2215
  #
2216
2216
  # <%= form_with model: @person do |person_form| %>
@@ -2282,10 +2282,10 @@ module ActionView
2282
2282
  # ...
2283
2283
  # <% end %>
2284
2284
  #
2285
- # Note that fields_for will automatically generate a hidden field
2285
+ # Note that +fields_for+ will automatically generate a hidden field
2286
2286
  # to store the ID of the record. There are circumstances where this
2287
2287
  # hidden field is not needed and you can pass <tt>include_id: false</tt>
2288
- # to prevent fields_for from rendering it automatically.
2288
+ # to prevent +fields_for+ from rendering it automatically.
2289
2289
  def fields_for(record_name, record_object = nil, fields_options = nil, &block)
2290
2290
  fields_options, record_object = record_object, nil if fields_options.nil? && record_object.is_a?(Hash) && record_object.extractable_options?
2291
2291
  fields_options ||= {}
@@ -2451,7 +2451,7 @@ module ActionView
2451
2451
  # the elements of the array. For each item with a checked check box you
2452
2452
  # get an extra ghost item with only that attribute, assigned to "0".
2453
2453
  #
2454
- # In that case it is preferable to either use +checkbox_tag+ or to use
2454
+ # In that case it is preferable to either use FormTagHelper#checkbox_tag or to use
2455
2455
  # hashes instead of arrays.
2456
2456
  #
2457
2457
  # ==== Examples
@@ -2525,7 +2525,7 @@ module ActionView
2525
2525
  # hash with +options+. These options will be tagged onto the HTML as an HTML element attribute as in the example
2526
2526
  # shown.
2527
2527
  #
2528
- # Using this method inside a +form_with+ block will set the enclosing form's encoding to <tt>multipart/form-data</tt>.
2528
+ # Using this method inside a #form_with block will set the enclosing form's encoding to <tt>multipart/form-data</tt>.
2529
2529
  #
2530
2530
  # ==== Options
2531
2531
  # * Creates standard HTML attributes for the tag.
@@ -38,8 +38,7 @@ module ActionView # :nodoc:
38
38
 
39
39
  # Converts the array to a comma-separated sentence where the last element is
40
40
  # joined by the connector word. This is the html_safe-aware version of
41
- # ActiveSupport's {Array#to_sentence}[https://api.rubyonrails.org/classes/Array.html#method-i-to_sentence].
42
- #
41
+ # ActiveSupport's Array#to_sentence.
43
42
  def to_sentence(array, options = {})
44
43
  options.assert_valid_keys(:words_connector, :two_words_connector, :last_word_connector, :locale)
45
44
 
@@ -24,6 +24,12 @@ module ActionView
24
24
  #
25
25
  # Custom sanitization rules can also be provided.
26
26
  #
27
+ # <b>Warning</b>: Adding disallowed tags or attributes to the allowlists may introduce
28
+ # vulnerabilities into your application. Please rely on the default allowlists whenever
29
+ # possible, because they are curated to maintain security and safety. If you think that the
30
+ # default allowlists should be expanded, please {open an issue on the rails-html-sanitizer
31
+ # project}[https://github.com/rails/rails-html-sanitizer/issues].
32
+ #
27
33
  # Please note that sanitizing user-provided text does not guarantee that the
28
34
  # resulting markup is valid or even well-formed.
29
35
  #
@@ -106,7 +106,8 @@ module ActionView
106
106
 
107
107
  def hidden_field
108
108
  hidden_name = @html_options[:name] || hidden_field_name
109
- @template_object.hidden_field_tag(hidden_name, "", id: nil)
109
+ options = { id: nil, form: @html_options[:form] }
110
+ @template_object.hidden_field_tag(hidden_name, "", options)
110
111
  end
111
112
 
112
113
  def hidden_field_name
@@ -284,7 +284,7 @@ module ActionView
284
284
  silence_redefinition_of_method(:_layout)
285
285
 
286
286
  prefixes = /\blayouts/.match?(_implied_layout_name) ? [] : ["layouts"]
287
- default_behavior = "lookup_context.find_all('#{_implied_layout_name}', #{prefixes.inspect}, false, [], { formats: formats }).first || super"
287
+ default_behavior = "lookup_context.find_all('#{_implied_layout_name}', #{prefixes.inspect}, false, keys, { formats: formats }).first || super"
288
288
  name_clause = if name
289
289
  default_behavior
290
290
  else
@@ -325,7 +325,7 @@ module ActionView
325
325
 
326
326
  class_eval <<-RUBY, __FILE__, __LINE__ + 1
327
327
  # frozen_string_literal: true
328
- def _layout(lookup_context, formats)
328
+ def _layout(lookup_context, formats, keys)
329
329
  if _conditional_layout?
330
330
  #{layout_definition}
331
331
  else
@@ -389,8 +389,8 @@ module ActionView
389
389
  case name
390
390
  when String then _normalize_layout(name)
391
391
  when Proc then name
392
- when true then Proc.new { |lookup_context, formats| _default_layout(lookup_context, formats, true) }
393
- when :default then Proc.new { |lookup_context, formats| _default_layout(lookup_context, formats, false) }
392
+ when true then Proc.new { |lookup_context, formats, keys| _default_layout(lookup_context, formats, keys, true) }
393
+ when :default then Proc.new { |lookup_context, formats, keys| _default_layout(lookup_context, formats, keys, false) }
394
394
  when false, nil then nil
395
395
  else
396
396
  raise ArgumentError,
@@ -412,9 +412,9 @@ module ActionView
412
412
  #
413
413
  # ==== Returns
414
414
  # * <tt>template</tt> - The template object for the default layout (or +nil+)
415
- def _default_layout(lookup_context, formats, require_layout = false)
415
+ def _default_layout(lookup_context, formats, keys, require_layout = false)
416
416
  begin
417
- value = _layout(lookup_context, formats) if action_has_layout?
417
+ value = _layout(lookup_context, formats, keys) if action_has_layout?
418
418
  rescue NameError => e
419
419
  raise e, "Could not render layout: #{e.message}"
420
420
  end
@@ -94,7 +94,7 @@ module ActionView
94
94
  # # <%= render partial: "accounts/account", locals: { account: @account} %>
95
95
  # <%= render partial: @account %>
96
96
  #
97
- # # @posts is an array of Post instances, so every post record returns 'posts/post' on +to_partial_path+,
97
+ # # @posts is an array of Post instances, so every post record returns 'posts/post' on #to_partial_path,
98
98
  # # that's why we can replace:
99
99
  # # <%= render partial: "posts/post", collection: @posts %>
100
100
  # <%= render partial: @posts %>
@@ -114,7 +114,7 @@ module ActionView
114
114
  # # <%= render partial: "accounts/account", locals: { account: @account} %>
115
115
  # <%= render @account %>
116
116
  #
117
- # # @posts is an array of Post instances, so every post record returns 'posts/post' on +to_partial_path+,
117
+ # # @posts is an array of Post instances, so every post record returns 'posts/post' on #to_partial_path,
118
118
  # # that's why we can replace:
119
119
  # # <%= render partial: "posts/post", collection: @posts %>
120
120
  # <%= render @posts %>
@@ -99,14 +99,14 @@ module ActionView
99
99
  if layout.start_with?("/")
100
100
  raise ArgumentError, "Rendering layouts from an absolute path is not supported."
101
101
  else
102
- @lookup_context.find_template(layout, nil, false, [], details)
102
+ @lookup_context.find_template(layout, nil, false, keys, details)
103
103
  end
104
104
  rescue ActionView::MissingTemplate
105
105
  all_details = @details.merge(formats: @lookup_context.default_formats)
106
- raise unless template_exists?(layout, nil, false, [], **all_details)
106
+ raise unless template_exists?(layout, nil, false, keys, **all_details)
107
107
  end
108
108
  when Proc
109
- resolve_layout(layout.call(@lookup_context, formats), keys, formats)
109
+ resolve_layout(layout.call(@lookup_context, formats, keys), keys, formats)
110
110
  else
111
111
  layout
112
112
  end
@@ -27,6 +27,17 @@ module ActionView
27
27
  end
28
28
  end
29
29
 
30
+ class StrictLocalsError < ArgumentError # :nodoc:
31
+ def initialize(argument_error, template)
32
+ message = argument_error.message.
33
+ gsub("unknown keyword:", "unknown local:").
34
+ gsub("missing keyword:", "missing local:").
35
+ gsub("no keywords accepted", "no locals accepted").
36
+ concat(" for #{template.short_identifier}")
37
+ super(message)
38
+ end
39
+ end
40
+
30
41
  class MissingTemplate < ActionViewError # :nodoc:
31
42
  attr_reader :path, :paths, :prefixes, :partial
32
43
 
@@ -42,7 +42,9 @@ module ActionView
42
42
  # source location inside the template.
43
43
  def translate_location(spot, backtrace_location, source)
44
44
  # Tokenize the source line
45
- tokens = ::ERB::Util.tokenize(source.lines[backtrace_location.lineno - 1])
45
+ source_lines = source.lines
46
+ return nil if source_lines.size < backtrace_location.lineno
47
+ tokens = ::ERB::Util.tokenize(source_lines[backtrace_location.lineno - 1])
46
48
  new_first_column = find_offset(spot[:snippet], tokens, spot[:first_column])
47
49
  lineno_delta = spot[:first_lineno] - backtrace_location.lineno
48
50
  spot[:first_lineno] -= lineno_delta
@@ -51,7 +53,7 @@ module ActionView
51
53
  column_delta = spot[:first_column] - new_first_column
52
54
  spot[:first_column] -= column_delta
53
55
  spot[:last_column] -= column_delta
54
- spot[:script_lines] = source.lines
56
+ spot[:script_lines] = source_lines
55
57
 
56
58
  spot
57
59
  rescue NotImplementedError, LocationParsingError
@@ -105,51 +107,57 @@ module ActionView
105
107
  raise WrongEncodingError.new(string, string.encoding)
106
108
  end
107
109
 
110
+ # Find which token in the source template spans the byte range that
111
+ # contains the error_column, then return the offset compared to the
112
+ # original source template.
113
+ #
114
+ # Iterate consecutive pairs of CODE or TEXT tokens, requiring
115
+ # a match of the first token before matching either token.
116
+ #
117
+ # For example, if we want to find tokens A, B, C, we do the following:
118
+ # 1. Find a match for A: test error_column or advance scanner.
119
+ # 2. Find a match for B or A:
120
+ # a. If B: start over with next token set (B, C).
121
+ # b. If A: test error_column or advance scanner.
122
+ # c. Otherwise: Advance 1 byte
123
+ #
124
+ # Prioritize matching the next token over the current token once
125
+ # a match for the current token has been found. This is to prevent
126
+ # the current token from looping past the next token if they both
127
+ # match (i.e. if the current token is a single space character).
108
128
  def find_offset(compiled, source_tokens, error_column)
109
129
  compiled = StringScanner.new(compiled)
130
+ offset_source_tokens(source_tokens).each_cons(2) do |(name, str, offset), (_, next_str, _)|
131
+ matched_str = false
110
132
 
111
- passed_tokens = []
133
+ until compiled.eos?
134
+ if matched_str && next_str && compiled.match?(next_str)
135
+ break
136
+ elsif compiled.match?(str)
137
+ matched_str = true
112
138
 
113
- while tok = source_tokens.shift
114
- tok_name, str = *tok
115
- case tok_name
116
- when :TEXT
117
- loop do
118
- break if compiled.match?(str)
119
- compiled.getch
120
- end
121
- raise LocationParsingError unless compiled.scan(str)
122
- when :CODE
123
- if compiled.pos > error_column
124
- raise LocationParsingError, "We went too far"
125
- end
139
+ if name == :CODE && compiled.pos <= error_column && compiled.pos + str.bytesize >= error_column
140
+ return error_column - compiled.pos + offset
141
+ end
126
142
 
127
- if compiled.pos + str.bytesize >= error_column
128
- offset = error_column - compiled.pos
129
- return passed_tokens.map(&:last).join.bytesize + offset
143
+ compiled.pos += str.bytesize
130
144
  else
131
- unless compiled.scan(str)
132
- raise LocationParsingError, "Couldn't find code snippet"
133
- end
134
- end
135
- when :OPEN
136
- next_tok = source_tokens.first.last
137
- loop do
138
- break if compiled.match?(next_tok)
139
- compiled.getch
145
+ compiled.pos += 1
140
146
  end
141
- when :CLOSE
142
- next_tok = source_tokens.first.last
143
- loop do
144
- break if compiled.match?(next_tok)
145
- compiled.getch
146
- end
147
- else
148
- raise LocationParsingError, "Not implemented: #{tok.first}"
149
147
  end
148
+ end
149
+
150
+ raise LocationParsingError, "Couldn't find code snippet"
151
+ end
150
152
 
151
- passed_tokens << tok
153
+ def offset_source_tokens(source_tokens)
154
+ source_offset = 0
155
+ with_offset = source_tokens.filter_map do |(name, str)|
156
+ result = [name, str, source_offset] if name == :CODE || name == :TEXT
157
+ source_offset += str.bytesize
158
+ result
152
159
  end
160
+ with_offset << [:EOS, nil, source_offset]
153
161
  end
154
162
  end
155
163
  end
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: actionview
3
3
  version: !ruby/object:Gem::Version
4
- version: 8.0.0.1
4
+ version: 8.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Heinemeier Hansson
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2024-12-10 00:00:00.000000000 Z
10
+ date: 2025-03-12 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: activesupport
@@ -16,14 +15,14 @@ dependencies:
16
15
  requirements:
17
16
  - - '='
18
17
  - !ruby/object:Gem::Version
19
- version: 8.0.0.1
18
+ version: 8.0.2
20
19
  type: :runtime
21
20
  prerelease: false
22
21
  version_requirements: !ruby/object:Gem::Requirement
23
22
  requirements:
24
23
  - - '='
25
24
  - !ruby/object:Gem::Version
26
- version: 8.0.0.1
25
+ version: 8.0.2
27
26
  - !ruby/object:Gem::Dependency
28
27
  name: builder
29
28
  requirement: !ruby/object:Gem::Requirement
@@ -86,28 +85,28 @@ dependencies:
86
85
  requirements:
87
86
  - - '='
88
87
  - !ruby/object:Gem::Version
89
- version: 8.0.0.1
88
+ version: 8.0.2
90
89
  type: :development
91
90
  prerelease: false
92
91
  version_requirements: !ruby/object:Gem::Requirement
93
92
  requirements:
94
93
  - - '='
95
94
  - !ruby/object:Gem::Version
96
- version: 8.0.0.1
95
+ version: 8.0.2
97
96
  - !ruby/object:Gem::Dependency
98
97
  name: activemodel
99
98
  requirement: !ruby/object:Gem::Requirement
100
99
  requirements:
101
100
  - - '='
102
101
  - !ruby/object:Gem::Version
103
- version: 8.0.0.1
102
+ version: 8.0.2
104
103
  type: :development
105
104
  prerelease: false
106
105
  version_requirements: !ruby/object:Gem::Requirement
107
106
  requirements:
108
107
  - - '='
109
108
  - !ruby/object:Gem::Version
110
- version: 8.0.0.1
109
+ version: 8.0.2
111
110
  description: Simple, battle-tested conventions and helpers for building web pages.
112
111
  email: david@loudthinking.com
113
112
  executables: []
@@ -247,12 +246,11 @@ licenses:
247
246
  - MIT
248
247
  metadata:
249
248
  bug_tracker_uri: https://github.com/rails/rails/issues
250
- changelog_uri: https://github.com/rails/rails/blob/v8.0.0.1/actionview/CHANGELOG.md
251
- documentation_uri: https://api.rubyonrails.org/v8.0.0.1/
249
+ changelog_uri: https://github.com/rails/rails/blob/v8.0.2/actionview/CHANGELOG.md
250
+ documentation_uri: https://api.rubyonrails.org/v8.0.2/
252
251
  mailing_list_uri: https://discuss.rubyonrails.org/c/rubyonrails-talk
253
- source_code_uri: https://github.com/rails/rails/tree/v8.0.0.1/actionview
252
+ source_code_uri: https://github.com/rails/rails/tree/v8.0.2/actionview
254
253
  rubygems_mfa_required: 'true'
255
- post_install_message:
256
254
  rdoc_options: []
257
255
  require_paths:
258
256
  - lib
@@ -268,8 +266,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
268
266
  version: '0'
269
267
  requirements:
270
268
  - none
271
- rubygems_version: 3.5.22
272
- signing_key:
269
+ rubygems_version: 3.6.2
273
270
  specification_version: 4
274
271
  summary: Rendering framework putting the V in MVC (part of Rails).
275
272
  test_files: []