In previous versions of Rails, a dynamic regex was built to find templates.
After that, PathParser started to be used to both match and sort templates.
With the dynamic regex, templates with lowdash locales (es_AR) were
found properly. But the PathParser regex does not match locales with this
format, only allowing dash (es-AR) or no dash (es). Templates with lowdash
locales have a wrong virtual path and get filtered.
In this commit the PathParser regex is extended to support the lowdash.
The generated view context classes tend to be fairly complex and use a
lot of memory. Similar to how we only generate new helper classes when
necessary (see https://github.com/rails/rails/pull/40204) we should be
doing the same for view context classes.
Follow-up to [rails/rails#43411][] (merged in [15f6113][])
By default, when generating a `<button>` element through a Form Builder
instance, the element's `[name]` attribute is populated by calling the
`FormBuilder#field_name` method. This commit assigns a matching default
`[id]` attribute generated by `FormBuilder#field_id`.
Additionally, it adds test coverage to ensure that calls that provide
their own `name:` and `id:` options are not overridden by the default
values.
[rails/rails#43411]: https://github.com/rails/rails/pull/43411
[15f6113]: 15f6113622
The background
---
Configuration for replacing a collection was introduced in
[rails/rails#36716][].
However, since [rails/rails#42596][] has been merged, Rails 7.1 and
beyond will default to _replacing_ an Active Storage `has_many_attached`
relationship, as opposed to _appending to it_.
The problem
---
With replacement as the established precedent, it's currently a
challenge to replace an existing collection with an empty one.
The solution
---
This commit makes two changes.
The first is to Action View and its form building helpers. The change
draws inspiration from how an `<input type="checkbox">` field (or
collection of fields) is paired with an `<input type="hidden">` field to
represent the unchecked value. The change pairs any `<input type="file"
multiple="multiple">` elements with an `<input type="hidden">` element
to represent an empty collection. Like the [check_box][] form builder
method, the `file_field` method accepts an `include_hidden:` option to
skip the creation of the hidden element.
The second is to how Active Storage generates attribute assignment
methods through `has_many_attached`. With the possibility of an `<input
type="file">` field being paired with an `<input type="hidden"
value="">` field, the backing models need to be able to coerce an
"empty-ish" value into an empty list. For example:
```ruby
@user.highlights = [""]
@user.highlights # => []
```
When combined, these changes enable consumer applications to submit
"empty" collections to blank out existing attachments.
Support is configured through the
`config.active_storage.multiple_file_field_include_hidden` configuration
value, which defaults to `false`.
[check_box]: https://edgeapi.rubyonrails.org/classes/ActionView/Helpers/FormBuilder.html#method-i-check_box
[rails/rails#36716]: https://github.com/rails/rails/pull/36716
[rails/rails#42596]: https://github.com/rails/rails/pull/42596
When constructing the field's `[id]` attribute, the current
`FormBuilder#field_id` implementation (introduced in [59ca21c][])
ignores the `namespace:` option.
This commit incorporates any namespace by prepending it to the
`@object_name`.
[59ca21c]: 59ca21c011
Re-use template.field_id
---
Thread options[:namespace] down through the FormBuilder instance to the
`Tags::Base#tag_id` and `#add_default_name_and_id` methods
Squashed commits:
[49cb03a3ce] Fix missing return from ActionView::Helpers::NumberHelper#parse_float, fixes#43853
Add test case for number helpers not raising exception when `raise: true` is passed and input is valid
Ruby 3.1 introduced an optimization to string interpolation for some
core classes in b08dacfea3.
But since we override `to_s` in some of those core classes to add behavior
like `to_s(:db)`, all Rails applications will not be able to take advantage
of that improvement.
Since we can use the `to_formatted_s` alias for the Rails specific behavior
it is best for us to deprecate the `to_s` core extension and allow Rails
applications to get the proformace improvement.
This commit starts removing all the `to_s(:db)` calls inside the framework
so we can deprecate the core extension in the next commit.
This can save a significant amount of string allocation in some scenarios
and is more consistent with modern Ruby code where `frozen_string_literal`
is enabled most of the time.
This module has been soft deprecated for a long time, but since
it was used internally it wasn't throwing deprecation warnings.
Now we can throw a deprecation warning.
Improve the parity between `form_for` and `form_with` by implementing
`form_for` in terms of `form_with`.
Replaces `html_options` transformations with coercion of data into the
shape it needs to be in order to delegate to `form_with`.
In the same spirit, this commit also implements `fields_for` in terms of
`fields`.
Since `<button>` elements translate their `[name]` and `[value]`
attributes to the resulting `<form>` element submission, and are encoded
into the resulting `URLSearchParams` or `FormData` instance, Action View
`FormBuilder` instances should support encoding a method name the same
way it does for other fields.
For instance, consider this HTML:
```html
<button>Publish</button>
<button name="post[draft]" value="true">Save as draft</button>
```
Clicking the "Publish" button would submit the form without encoding any
additional `[name]` and `[value]` pairs.
Clicking the "Save as draft" button would submit the form and encode
`post[draft]=true` into the submission.
This commit changes the `FormBuilder#button` method to interpret a
`Symbol` as the first argument as a method name argument, and encodes
its value based on the form's `model:` or `scope:` value:
```erb
<%= form.button :draft, value: true do %>
Save as draft
<% end %>
end
<%# => <button name="post[draft]" value="true" type="submit"> %>
<%# Save as draft %>
<%# </button> %>
```
Co-authored-by: Rafael Mendonça França <rafael@rubyonrails.org>
The `tag.attributes` helper introduced in [#40657][] is implemented on a
`:nodoc:`-private `TagBuilder` class, so the documentation for the
method is omitted when generating the API Guides HTML pages.
This commit extends the `ActionView::Helpers::TagHelper` module
documentation comment to mention the new attribute building
capabilities, along with some examples.
[#40657]: https://github.com/rails/rails/pull/40657
Some background
---
By default, when a `<form>` is declared without an `[action]` attribute,
browsers will encode a `<form>`'s fields into the _current_ URL.
This can be useful for a `<form method="get">` that operates on the
current page. For example, when filtering search results, a form that
sorts:
```html
<form method="get">
<button name="sort" value="desc">Most to least</button>
<button name="sort" value="asc">Least to most</button>
</form>
```
can operate on a page that is filtered in another way by merging the
`?sort=asc` or `?sort=desc` values _into_ the existing page, which might
have the `?q=...` string set elsewhere.
The problem
---
Prior to this commit, none of the `<form>` construction variations
supported declaring a `<form>` without an `[action]` attribute.
`form_with`, `form_for`, and `form_tag` all default to `url_for({})`
when a `url:` or `action:` option is omitted.
The solution
---
Treat `url: false`, or `action: false` as an escape hatch to signal to
Action View that we don't need to transform the `model:` option or
argument into a Rails route.
Similarly, when calling `button_to` with `false` as the URL options
arguments will construct a `<form>` element without an `[action]`
attribute.
Instead of treating it as an anonymous block, execute the
`ActionView::Base.field_error_proc` within the context of the
`ActionView::Base` instance.
This enables consumer applications to continue to override the proc as
they see fit, but frees them from declaring templating logic within a
`config/initializers/*.rb`, `config/environments/*.rb` or
`config/application.rb` file.
This makes it possible to replace something like:
```ruby
config.action_view.field_error_proc = proc do |html_tag, instance|
<<~HTML.html_safe
#{html_tag}
<span class="errors">#{instance.error_message.to_sentence}</span>
HTML
end
```
With inline calls to Action View helpers like:
```ruby
config.action_view.field_error_proc = proc do |html_tag, instance|
safe_join [ html_tag, tag.span(instance.error_message.to_sentence, class: "errors") ]
end
```
Or with a view partial rendering, like:
```ruby
config.action_view.field_error_proc = proc do |html_tag, instance|
render partial: "application/field_with_errors", locals: { html_tag: html_tag, instance: instance }
end
```
Then, elsewhere in `app/views/application/field_with_errors.html.erb`:
```erb
<%= html_tag %>
<span class="errors"><%= instance.error_message.to_sentence %></span>
```
Infer HTTP verb `[method]` from a model or Array with model as the first
argument to `button_to` when combined with a block:
```ruby
button_to(Workshop.find(1)){ "Update" }
#=> <form method="post" action="/workshops/1" class="button_to">
#=> <input type="hidden" name="_method" value="patch" autocomplete="off" />
#=> <button type="submit">Update</button>
#=> </form>
button_to([ Workshop.find(1), Session.find(1) ]) { "Update" }
#=> <form method="post" action="/workshops/1/sessions/1" class="button_to">
#=> <input type="hidden" name="_method" value="patch" autocomplete="off" />
#=> <button type="submit">Update</button>
#=> </form>
```
Prior to this change, the constructed `<form>` was always submitted with
a `[method="post"]` and _always_ omitted the `<input type="hidden"
name="_method" value="...">` field, regardless of the return value of
the "model" argument's `#persisted?` predicate.