actionview 7.2.2.1 → 7.2.3

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: 129f00c083e1a4443010be161169e51eea090543c6618289968ed2cdb1c65c76
4
- data.tar.gz: 568d88d12f7fc363958af2f8bf45308199dc0c221a05fafb9415a96b3eaddc8a
3
+ metadata.gz: 773338461dd6a54e8b6efa075c2be80d8f8c975ee46bd2167bc7e2fcd8e78f35
4
+ data.tar.gz: 22244120a030dfc49034d8d790fa86013b1de42b5ee7acbe75243580c9eec7c1
5
5
  SHA512:
6
- metadata.gz: de08bd40788b58e9ba6be2169a5ebbc39c6b4f1122d1564b64cf0a6af066f855d4a1ad2d1721ab849fde78ee87c496c61579afd59d1a3062dba6135b6652bee4
7
- data.tar.gz: cf2f5461be068f0d96d287bbdabe2daf9b3d76a4e80c9db31041d9242f77986b1d5d6774d6db95f5f2b068cbce2161b13335891bfd86d271994d49321bc88733
6
+ metadata.gz: 1c26e2052e3f599c7f28c19892948c6b3f8cdeef005a4dc54762b4e74309ac32ef794115b0e46d2364624b26debb85a6aaebc938813d08e69e9c670c3bf79ae6
7
+ data.tar.gz: 15bdc1f27280a327a1270ddf794b484d68f88af959c2d49361ef0e33c37e547b81166c8b92ef400f7e9a62c8192820bd58042733335ff4a9c3f1a77b660f775f
data/CHANGELOG.md CHANGED
@@ -1,3 +1,70 @@
1
+ ## Rails 7.2.3 (October 28, 2025) ##
2
+
3
+ * Fix `javascript_include_tag` `type` option to accept either strings and symbols.
4
+
5
+ ```ruby
6
+ javascript_include_tag "application", type: :module
7
+ javascript_include_tag "application", type: "module"
8
+ ```
9
+
10
+ Previously, only the string value was recoginized.
11
+
12
+ *Jean Boussier*
13
+
14
+ * Fix `excerpt` helper with non-whitespace separator.
15
+
16
+ *Jonathan Hefner*
17
+
18
+ * Respect `html_options[:form]` when `collection_checkboxes` generates the
19
+ hidden `<input>`.
20
+
21
+ *Riccardo Odone*
22
+
23
+ * Layouts have access to local variables passed to `render`.
24
+
25
+ This fixes #31680 which was a regression in Rails 5.1.
26
+
27
+ *Mike Dalessio*
28
+
29
+ * Argument errors related to strict locals in templates now raise an
30
+ `ActionView::StrictLocalsError`, and all other argument errors are reraised as-is.
31
+
32
+ Previously, any `ArgumentError` raised during template rendering was swallowed during strict
33
+ local error handling, so that an `ArgumentError` unrelated to strict locals (e.g., a helper
34
+ method invoked with incorrect arguments) would be replaced by a similar `ArgumentError` with an
35
+ unrelated backtrace, making it difficult to debug templates.
36
+
37
+ Now, any `ArgumentError` unrelated to strict locals is reraised, preserving the original
38
+ backtrace for developers.
39
+
40
+ Also note that `ActionView::StrictLocalsError` is a subclass of `ArgumentError`, so any existing
41
+ code that rescues `ArgumentError` will continue to work.
42
+
43
+ Fixes #52227.
44
+
45
+ *Mike Dalessio*
46
+
47
+ * Fix stack overflow error in dependency tracker when dealing with circular dependencies
48
+
49
+ *Jean Boussier*
50
+
51
+ * Fix a crash in ERB template error highlighting when the error occurs on a
52
+ line in the compiled template that is past the end of the source template.
53
+
54
+ *Martin Emde*
55
+
56
+ * Improve reliability of ERB template error highlighting.
57
+ Fix infinite loops and crashes in highlighting and
58
+ improve tolerance for alternate ERB handlers.
59
+
60
+ *Martin Emde*
61
+
62
+
63
+ ## Rails 7.2.2.2 (August 13, 2025) ##
64
+
65
+ * No changes.
66
+
67
+
1
68
  ## Rails 7.2.2.1 (December 10, 2024) ##
2
69
 
3
70
  * No changes.
data/README.rdoc CHANGED
@@ -35,6 +35,6 @@ Bug reports for the Ruby on \Rails project can be filed here:
35
35
 
36
36
  * https://github.com/rails/rails/issues
37
37
 
38
- Feature requests should be discussed on the rails-core mailing list here:
38
+ Feature requests should be discussed on the rubyonrails-core forum here:
39
39
 
40
40
  * https://discuss.rubyonrails.org/c/rubyonrails-core
@@ -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 = 7
11
11
  MINOR = 2
12
- TINY = 2
13
- PRE = "1"
12
+ TINY = 3
13
+ PRE = nil
14
14
 
15
15
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
16
16
  end
@@ -115,11 +115,11 @@ module ActionView
115
115
  path_options = options.extract!("protocol", "extname", "host", "skip_pipeline").symbolize_keys
116
116
  preload_links = []
117
117
  use_preload_links_header = options["preload_links_header"].nil? ? preload_links_header : options.delete("preload_links_header")
118
- nopush = options["nopush"].nil? ? true : options.delete("nopush")
118
+ nopush = options["nopush"].nil? || options.delete("nopush")
119
119
  crossorigin = options.delete("crossorigin")
120
120
  crossorigin = "anonymous" if crossorigin == true
121
121
  integrity = options["integrity"]
122
- rel = options["type"] == "module" ? "modulepreload" : "preload"
122
+ rel = options["type"] == "module" || options["type"] == :module ? "modulepreload" : "preload"
123
123
 
124
124
  sources_tags = sources.uniq.map { |source|
125
125
  href = path_to_javascript(source, path_options)
@@ -206,7 +206,7 @@ module ActionView
206
206
  preload_links = []
207
207
  crossorigin = options.delete("crossorigin")
208
208
  crossorigin = "anonymous" if crossorigin == true
209
- nopush = options["nopush"].nil? ? true : options.delete("nopush")
209
+ nopush = options["nopush"].nil? || options.delete("nopush")
210
210
  integrity = options["integrity"]
211
211
 
212
212
  sources_tags = sources.uniq.map { |source|
@@ -361,7 +361,7 @@ module ActionView
361
361
  crossorigin = "anonymous" if crossorigin == true || (crossorigin.blank? && as_type == "font")
362
362
  integrity = options[:integrity]
363
363
  nopush = options.delete(:nopush) || false
364
- rel = mime_type == "module" ? "modulepreload" : "preload"
364
+ rel = mime_type == "module" || mime_type == :module ? "modulepreload" : "preload"
365
365
 
366
366
  link_tag = tag.link(
367
367
  rel: rel,
@@ -172,7 +172,7 @@ module ActionView
172
172
 
173
173
  # Creates an entry tag for a specific record and prefills the id using class and id.
174
174
  #
175
- # Options:
175
+ # ==== Options
176
176
  #
177
177
  # * <tt>:published</tt>: Time first published. Defaults to the created_at attribute on the record if one such exists.
178
178
  # * <tt>:updated</tt>: Time of update. Defaults to the updated_at attribute on the record if one such exists.
@@ -189,7 +189,7 @@ module ActionView
189
189
  CachingRegistry.caching?
190
190
  end
191
191
 
192
- # Raises +UncacheableFragmentError+ when called from within a +cache+ block.
192
+ # Raises UncacheableFragmentError when called from within a +cache+ block.
193
193
  #
194
194
  # Useful to denote helper methods that can't participate in fragment caching:
195
195
  #
@@ -198,7 +198,7 @@ module ActionView
198
198
  # "#{project.name} - #{Time.now}"
199
199
  # end
200
200
  #
201
- # # Which will then raise if used within a +cache+ block:
201
+ # # Which will then raise if used within a `cache` block:
202
202
  # <% cache project do %>
203
203
  # <%= project_name_with_time(project) %>
204
204
  # <% end %>
@@ -136,8 +136,15 @@ module ActionView
136
136
  from_year += 1 if from_time.month >= 3
137
137
  to_year = to_time.year
138
138
  to_year -= 1 if to_time.month < 3
139
- leap_years = (from_year > to_year) ? 0 : (from_year..to_year).count { |x| Date.leap?(x) }
139
+
140
+ leap_years = if from_year > to_year
141
+ 0
142
+ else
143
+ fyear = from_year - 1
144
+ (to_year / 4 - to_year / 100 + to_year / 400) - (fyear / 4 - fyear / 100 + fyear / 400)
145
+ end
140
146
  minute_offset_for_leap_year = leap_years * 1440
147
+
141
148
  # Discount the leap year days when calculating year distance.
142
149
  # e.g. if there are 20 leap year days between 2 dates having the same day
143
150
  # and month then based on 365 days calculation
@@ -30,8 +30,8 @@ module ActionView
30
30
  # when the form is initially displayed, input fields corresponding to attributes
31
31
  # of the resource should show the current values of those attributes.
32
32
  #
33
- # In \Rails, this is usually achieved by creating the form using +form_for+ and
34
- # a number of related helper methods. +form_for+ generates an appropriate <tt>form</tt>
33
+ # In \Rails, this is usually achieved by creating the form using #form_for and
34
+ # a number of related helper methods. #form_for generates an appropriate <tt>form</tt>
35
35
  # tag and yields a form builder object that knows the model the form is about.
36
36
  # Input fields are created by calling methods defined on the form builder, which
37
37
  # means they are able to generate the appropriate names and default values
@@ -41,7 +41,7 @@ module ActionView
41
41
  #
42
42
  # For example, to create a new person you typically set up a new instance of
43
43
  # +Person+ in the <tt>PeopleController#new</tt> action, <tt>@person</tt>, and
44
- # in the view template pass that object to +form_for+:
44
+ # in the view template pass that object to #form_for:
45
45
  #
46
46
  # <%= form_for @person do |f| %>
47
47
  # <%= f.label :first_name %>:
@@ -208,7 +208,7 @@ module ActionView
208
208
  # are designed to work with an object as base, like
209
209
  # FormOptionsHelper#collection_select and DateHelper#datetime_select.
210
210
  #
211
- # === #form_for with a model object
211
+ # === +form_for+ with a model object
212
212
  #
213
213
  # In the examples above, the object to be created or edited was
214
214
  # represented by a symbol passed to +form_for+, and we noted that
@@ -363,7 +363,7 @@ module ActionView
363
363
  #
364
364
  # === Removing hidden model id's
365
365
  #
366
- # The form_for method automatically includes the model id as a hidden field in the form.
366
+ # The +form_for+ method automatically includes the model id as a hidden field in the form.
367
367
  # This is used to maintain the correlation between the form data and its associated model.
368
368
  # Some ORM systems do not use IDs on nested models so in this case you want to be able
369
369
  # to disable the hidden id.
@@ -783,12 +783,12 @@ module ActionView
783
783
  end
784
784
  end
785
785
 
786
- # Creates a scope around a specific model object like form_with, but
786
+ # Creates a scope around a specific model object like #form_with, but
787
787
  # doesn't create the form tags themselves. This makes fields_for suitable
788
788
  # for specifying additional model objects in the same form.
789
789
  #
790
- # Although the usage and purpose of +fields_for+ is similar to +form_with+'s,
791
- # its method signature is slightly different. Like +form_with+, it yields
790
+ # Although the usage and purpose of +fields_for+ is similar to #form_with's,
791
+ # its method signature is slightly different. Like #form_with, it yields
792
792
  # a FormBuilder object associated with a particular model object to a block,
793
793
  # and within the block allows methods to be called on the builder to
794
794
  # generate fields associated with the model object. Fields may reflect
@@ -847,7 +847,7 @@ module ActionView
847
847
  # === Nested Attributes Examples
848
848
  #
849
849
  # When the object belonging to the current scope has a nested attribute
850
- # writer for a certain attribute, fields_for will yield a new scope
850
+ # writer for a certain attribute, +fields_for+ will yield a new scope
851
851
  # for that attribute. This allows you to create forms that set or change
852
852
  # the attributes of a parent object and its associations in one go.
853
853
  #
@@ -936,7 +936,7 @@ module ActionView
936
936
  # end
937
937
  #
938
938
  # Note that the <tt>projects_attributes=</tt> writer method is in fact
939
- # required for fields_for to correctly identify <tt>:projects</tt> as a
939
+ # required for +fields_for+ to correctly identify <tt>:projects</tt> as a
940
940
  # collection, and the correct indices to be set in the form markup.
941
941
  #
942
942
  # When projects is already an association on Person you can use
@@ -948,7 +948,7 @@ module ActionView
948
948
  # end
949
949
  #
950
950
  # This model can now be used with a nested fields_for. The block given to
951
- # the nested fields_for call will be repeated for each instance in the
951
+ # the nested +fields_for+ call will be repeated for each instance in the
952
952
  # collection:
953
953
  #
954
954
  # <%= form_with model: @person do |person_form| %>
@@ -1020,10 +1020,10 @@ module ActionView
1020
1020
  # ...
1021
1021
  # <% end %>
1022
1022
  #
1023
- # Note that fields_for will automatically generate a hidden field
1023
+ # Note that +fields_for+ will automatically generate a hidden field
1024
1024
  # to store the ID of the record if it responds to <tt>persisted?</tt>.
1025
1025
  # There are circumstances where this hidden field is not needed and you
1026
- # can pass <tt>include_id: false</tt> to prevent fields_for from
1026
+ # can pass <tt>include_id: false</tt> to prevent +fields_for+ from
1027
1027
  # rendering it automatically.
1028
1028
  def fields_for(record_name, record_object = nil, options = {}, &block)
1029
1029
  options = { model: record_object, allow_method_names_outside_object: false, skip_default_ids: false }.merge!(options)
@@ -1032,7 +1032,7 @@ module ActionView
1032
1032
  end
1033
1033
 
1034
1034
  # Scopes input fields with either an explicit scope or model.
1035
- # Like +form_with+ does with <tt>:scope</tt> or <tt>:model</tt>,
1035
+ # Like #form_with does with <tt>:scope</tt> or <tt>:model</tt>,
1036
1036
  # except it doesn't output the form tags.
1037
1037
  #
1038
1038
  # # Using a scope prefixes the input field names:
@@ -1047,7 +1047,7 @@ module ActionView
1047
1047
  # <% end %>
1048
1048
  # # => <input type="text" name="comment[body]" value="full bodied">
1049
1049
  #
1050
- # # Using +fields+ with +form_with+:
1050
+ # # Using `fields` with `form_with`:
1051
1051
  # <%= form_with model: @article do |form| %>
1052
1052
  # <%= form.text_field :title %>
1053
1053
  #
@@ -1056,13 +1056,13 @@ module ActionView
1056
1056
  # <% end %>
1057
1057
  # <% end %>
1058
1058
  #
1059
- # Much like +form_with+ a FormBuilder instance associated with the scope
1059
+ # Much like #form_with a FormBuilder instance associated with the scope
1060
1060
  # or model is yielded, so any generated field names are prefixed with
1061
1061
  # either the passed scope or the scope inferred from the <tt>:model</tt>.
1062
1062
  #
1063
1063
  # === Mixing with other form helpers
1064
1064
  #
1065
- # While +form_with+ uses a FormBuilder object it's possible to mix and
1065
+ # While #form_with uses a FormBuilder object it's possible to mix and
1066
1066
  # match the stand-alone FormHelper methods and methods
1067
1067
  # from FormTagHelper:
1068
1068
  #
@@ -1220,7 +1220,7 @@ module ActionView
1220
1220
  # hash with +options+. These options will be tagged onto the HTML as an HTML element attribute as in the example
1221
1221
  # shown.
1222
1222
  #
1223
- # Using this method inside a +form_with+ block will set the enclosing form's encoding to <tt>multipart/form-data</tt>.
1223
+ # Using this method inside a #form_with block will set the enclosing form's encoding to <tt>multipart/form-data</tt>.
1224
1224
  #
1225
1225
  # ==== Options
1226
1226
  # * Creates standard HTML attributes for the tag.
@@ -1324,7 +1324,7 @@ module ActionView
1324
1324
  # the elements of the array. For each item with a checked check box you
1325
1325
  # get an extra ghost item with only that attribute, assigned to "0".
1326
1326
  #
1327
- # In that case it is preferable to either use +check_box_tag+ or to use
1327
+ # In that case it is preferable to either use FormTagHelper#check_box_tag or to use
1328
1328
  # hashes instead of arrays.
1329
1329
  #
1330
1330
  # ==== Examples
@@ -1629,7 +1629,7 @@ module ActionView
1629
1629
  #
1630
1630
  # A +FormBuilder+ object is associated with a particular model object and
1631
1631
  # allows you to generate fields associated with the model object. The
1632
- # +FormBuilder+ object is yielded when using +form_with+ or +fields_for+.
1632
+ # +FormBuilder+ object is yielded when using #form_with or #fields_for.
1633
1633
  # For example:
1634
1634
  #
1635
1635
  # <%= form_with model: @person do |person_form| %>
@@ -1767,7 +1767,7 @@ module ActionView
1767
1767
  # <% end %>
1768
1768
  #
1769
1769
  # In the example above, the <tt><input type="text"></tt> element built by
1770
- # the call to <tt>FormBuilder#text_field</tt> declares an
1770
+ # the call to #text_field declares an
1771
1771
  # <tt>aria-describedby</tt> attribute referencing the <tt><span></tt>
1772
1772
  # element, sharing a common <tt>id</tt> root (<tt>article_title</tt>, in this
1773
1773
  # case).
@@ -2034,12 +2034,12 @@ module ActionView
2034
2034
  end
2035
2035
  end
2036
2036
 
2037
- # Creates a scope around a specific model object like form_with, but
2037
+ # Creates a scope around a specific model object like #form_with, but
2038
2038
  # doesn't create the form tags themselves. This makes fields_for suitable
2039
2039
  # for specifying additional model objects in the same form.
2040
2040
  #
2041
- # Although the usage and purpose of +fields_for+ is similar to +form_with+'s,
2042
- # its method signature is slightly different. Like +form_with+, it yields
2041
+ # Although the usage and purpose of +fields_for+ is similar to #form_with's,
2042
+ # its method signature is slightly different. Like #form_with, it yields
2043
2043
  # a FormBuilder object associated with a particular model object to a block,
2044
2044
  # and within the block allows methods to be called on the builder to
2045
2045
  # generate fields associated with the model object. Fields may reflect
@@ -2110,7 +2110,7 @@ module ActionView
2110
2110
  # === Nested Attributes Examples
2111
2111
  #
2112
2112
  # When the object belonging to the current scope has a nested attribute
2113
- # writer for a certain attribute, fields_for will yield a new scope
2113
+ # writer for a certain attribute, +fields_for+ will yield a new scope
2114
2114
  # for that attribute. This allows you to create forms that set or change
2115
2115
  # the attributes of a parent object and its associations in one go.
2116
2116
  #
@@ -2141,7 +2141,7 @@ module ActionView
2141
2141
  # end
2142
2142
  # end
2143
2143
  #
2144
- # This model can now be used with a nested fields_for, like so:
2144
+ # This model can now be used with a nested +fields_for+, like so:
2145
2145
  #
2146
2146
  # <%= form_with model: @person do |person_form| %>
2147
2147
  # ...
@@ -2199,7 +2199,7 @@ module ActionView
2199
2199
  # end
2200
2200
  #
2201
2201
  # Note that the <tt>projects_attributes=</tt> writer method is in fact
2202
- # required for fields_for to correctly identify <tt>:projects</tt> as a
2202
+ # required for +fields_for+ to correctly identify <tt>:projects</tt> as a
2203
2203
  # collection, and the correct indices to be set in the form markup.
2204
2204
  #
2205
2205
  # When projects is already an association on Person you can use
@@ -2210,8 +2210,8 @@ module ActionView
2210
2210
  # accepts_nested_attributes_for :projects
2211
2211
  # end
2212
2212
  #
2213
- # This model can now be used with a nested fields_for. The block given to
2214
- # the nested fields_for call will be repeated for each instance in the
2213
+ # This model can now be used with a nested +fields_for+. The block given to
2214
+ # the nested +fields_for+ call will be repeated for each instance in the
2215
2215
  # collection:
2216
2216
  #
2217
2217
  # <%= form_with model: @person do |person_form| %>
@@ -2283,10 +2283,10 @@ module ActionView
2283
2283
  # ...
2284
2284
  # <% end %>
2285
2285
  #
2286
- # Note that fields_for will automatically generate a hidden field
2286
+ # Note that +fields_for+ will automatically generate a hidden field
2287
2287
  # to store the ID of the record. There are circumstances where this
2288
2288
  # hidden field is not needed and you can pass <tt>include_id: false</tt>
2289
- # to prevent fields_for from rendering it automatically.
2289
+ # to prevent +fields_for+ from rendering it automatically.
2290
2290
  def fields_for(record_name, record_object = nil, fields_options = nil, &block)
2291
2291
  fields_options, record_object = record_object, nil if fields_options.nil? && record_object.is_a?(Hash) && record_object.extractable_options?
2292
2292
  fields_options ||= {}
@@ -2452,7 +2452,7 @@ module ActionView
2452
2452
  # the elements of the array. For each item with a checked check box you
2453
2453
  # get an extra ghost item with only that attribute, assigned to "0".
2454
2454
  #
2455
- # In that case it is preferable to either use +check_box_tag+ or to use
2455
+ # In that case it is preferable to either use FormTagHelper#check_box_tag or to use
2456
2456
  # hashes instead of arrays.
2457
2457
  #
2458
2458
  # ==== 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.
@@ -496,7 +496,8 @@ module ActionView
496
496
  # <option value="France">France</option>
497
497
  # </optgroup>
498
498
  #
499
- # Parameters:
499
+ # ==== Parameters
500
+ #
500
501
  # * +grouped_options+ - Accepts a nested array or hash of strings. The first value serves as the
501
502
  # <tt><optgroup></tt> label while the second value must be an array of options. The second value can be a
502
503
  # nested array of text-value pairs. See <tt>options_for_select</tt> for more info.
@@ -507,7 +508,8 @@ module ActionView
507
508
  # which will have the +selected+ attribute set. Note: It is possible for this value to match multiple options
508
509
  # as you might have the same option in multiple groups. Each will then get <tt>selected="selected"</tt>.
509
510
  #
510
- # Options:
511
+ # ==== Options
512
+ #
511
513
  # * <tt>:prompt</tt> - set to true or a prompt string. When the select element doesn't have a value yet, this
512
514
  # prepends an option with a generic prompt - "Please select" - or the given prompt string.
513
515
  # * <tt>:divider</tt> - the divider for the options groups.
@@ -599,7 +601,8 @@ module ActionView
599
601
 
600
602
  # Returns a string of option tags for the days of the week.
601
603
  #
602
- # Options:
604
+ # ====Options
605
+ #
603
606
  # * <tt>:index_as_value</tt> - Defaults to false, set to true to use the indexes from
604
607
  # <tt>I18n.translate("date.day_names")</tt> as the values. By default, Sunday is always 0.
605
608
  # * <tt>:day_format</tt> - The I18n key of the array to use for the weekday options.
@@ -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
 
@@ -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
@@ -260,7 +260,14 @@ module ActionView
260
260
  prefix, first_part = cut_excerpt_part(:first, first_part, separator, options)
261
261
  postfix, second_part = cut_excerpt_part(:second, second_part, separator, options)
262
262
 
263
- affix = [first_part, separator, phrase, separator, second_part].join.strip
263
+ affix = [
264
+ first_part,
265
+ !first_part.empty? ? separator : "",
266
+ phrase,
267
+ !second_part.empty? ? separator : "",
268
+ second_part
269
+ ].join.strip
270
+
264
271
  [prefix, affix, postfix].join
265
272
  end
266
273
 
@@ -271,7 +278,7 @@ module ActionView
271
278
  #
272
279
  # The word will be pluralized using rules defined for the locale
273
280
  # (you must define your own inflection rules for languages other than English).
274
- # See ActiveSupport::Inflector.pluralize
281
+ # See ActiveSupport::Inflector.pluralize.
275
282
  #
276
283
  # pluralize(1, 'person')
277
284
  # # => "1 person"
@@ -346,7 +353,7 @@ module ActionView
346
353
  # ==== Options
347
354
  # * <tt>:sanitize</tt> - If +false+, does not sanitize +text+.
348
355
  # * <tt>:sanitize_options</tt> - Any extra options you want appended to the sanitize.
349
- # * <tt>:wrapper_tag</tt> - String representing the wrapper tag, defaults to <tt>"p"</tt>
356
+ # * <tt>:wrapper_tag</tt> - String representing the wrapper tag, defaults to <tt>"p"</tt>.
350
357
  #
351
358
  # ==== Examples
352
359
  # my_text = "Here is some basic text...\n...with a line break."
@@ -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
@@ -301,7 +301,6 @@ module ActionView
301
301
  class RenderedViewContent < String # :nodoc:
302
302
  end
303
303
 
304
- # Need to experiment if this priority is the best one: rendered => output_buffer
305
304
  class RenderedViewsCollection
306
305
  def initialize
307
306
  @rendered_views ||= Hash.new { |hash, key| hash[key] = [] }
data/lib/action_view.rb CHANGED
@@ -81,6 +81,7 @@ module ActionView
81
81
  autoload :MissingTemplate
82
82
  autoload :ActionViewError
83
83
  autoload :EncodingError
84
+ autoload :StrictLocalsError
84
85
  autoload :TemplateError
85
86
  autoload :SyntaxErrorInTemplate
86
87
  autoload :WrongEncodingError
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: 7.2.2.1
4
+ version: 7.2.3
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: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: activesupport
@@ -16,14 +15,28 @@ dependencies:
16
15
  requirements:
17
16
  - - '='
18
17
  - !ruby/object:Gem::Version
19
- version: 7.2.2.1
18
+ version: 7.2.3
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: 7.2.2.1
25
+ version: 7.2.3
26
+ - !ruby/object:Gem::Dependency
27
+ name: cgi
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
27
40
  - !ruby/object:Gem::Dependency
28
41
  name: builder
29
42
  requirement: !ruby/object:Gem::Requirement
@@ -86,28 +99,28 @@ dependencies:
86
99
  requirements:
87
100
  - - '='
88
101
  - !ruby/object:Gem::Version
89
- version: 7.2.2.1
102
+ version: 7.2.3
90
103
  type: :development
91
104
  prerelease: false
92
105
  version_requirements: !ruby/object:Gem::Requirement
93
106
  requirements:
94
107
  - - '='
95
108
  - !ruby/object:Gem::Version
96
- version: 7.2.2.1
109
+ version: 7.2.3
97
110
  - !ruby/object:Gem::Dependency
98
111
  name: activemodel
99
112
  requirement: !ruby/object:Gem::Requirement
100
113
  requirements:
101
114
  - - '='
102
115
  - !ruby/object:Gem::Version
103
- version: 7.2.2.1
116
+ version: 7.2.3
104
117
  type: :development
105
118
  prerelease: false
106
119
  version_requirements: !ruby/object:Gem::Requirement
107
120
  requirements:
108
121
  - - '='
109
122
  - !ruby/object:Gem::Version
110
- version: 7.2.2.1
123
+ version: 7.2.3
111
124
  description: Simple, battle-tested conventions and helpers for building web pages.
112
125
  email: david@loudthinking.com
113
126
  executables: []
@@ -246,12 +259,11 @@ licenses:
246
259
  - MIT
247
260
  metadata:
248
261
  bug_tracker_uri: https://github.com/rails/rails/issues
249
- changelog_uri: https://github.com/rails/rails/blob/v7.2.2.1/actionview/CHANGELOG.md
250
- documentation_uri: https://api.rubyonrails.org/v7.2.2.1/
262
+ changelog_uri: https://github.com/rails/rails/blob/v7.2.3/actionview/CHANGELOG.md
263
+ documentation_uri: https://api.rubyonrails.org/v7.2.3/
251
264
  mailing_list_uri: https://discuss.rubyonrails.org/c/rubyonrails-talk
252
- source_code_uri: https://github.com/rails/rails/tree/v7.2.2.1/actionview
265
+ source_code_uri: https://github.com/rails/rails/tree/v7.2.3/actionview
253
266
  rubygems_mfa_required: 'true'
254
- post_install_message:
255
267
  rdoc_options: []
256
268
  require_paths:
257
269
  - lib
@@ -267,8 +279,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
267
279
  version: '0'
268
280
  requirements:
269
281
  - none
270
- rubygems_version: 3.5.22
271
- signing_key:
282
+ rubygems_version: 3.6.9
272
283
  specification_version: 4
273
284
  summary: Rendering framework putting the V in MVC (part of Rails).
274
285
  test_files: []