Move convert_to_model call from form_for to form_with

Ensure models passed to `form_with` attempt to call `to_model`.

Now that `form_for` is implemented in terms of `form_with`, this commit
also removes the `convert_to_model` call from the `form_for` implementation.

To exercise this behavior, change existing `form_with` test coverage.

Prior to this change, a call to `form_with` made with a `model:` argument
that responds to `to_model` would not incorporate the instance's persistence
state into the form's HTTP verb. After this change, the persistence state
inferred from the `model:` argument's `to_model` call is incorporated into
the `<form>` element's `[method]` attribute.

This is a separate follow-up change proposed in [rails/rails#44328][].
The original change to restore old behavior _deliberately_ excluded
applying the same logic to `form_with`, since it would be a breaking
change from how `form_with` behaved previously.

This commit proposed making that breaking change.

[rails/rails#44328]: https://github.com/rails/rails/pull/44328#discussion_r808475585
This commit is contained in:
Sean Doyle 2022-02-17 15:53:09 -05:00
parent b5d12eaee8
commit 980de46f54
3 changed files with 10 additions and 3 deletions

@ -1,3 +1,10 @@
* Move `convert_to_model` call from `form_for` into `form_with`
Now that `form_for` is implemented in terms of `form_with`, remove the
`convert_to_model` call from `form_for`.
*Sean Doyle*
* Fix and add protections for XSS in `ActionView::Helpers` and `ERB::Util`.
Escape dangerous characters in names of tags and names of attributes in the

@ -438,7 +438,7 @@ def form_for(record, options = {}, &block)
model = nil
object_name = record
else
model = convert_to_model(record)
model = record
object = _object_for_form_builder(record)
raise ArgumentError, "First argument in form cannot contain nil or be empty" unless object
object_name = options[:as] || model_name_from_record_or_class(object).param_key
@ -763,7 +763,7 @@ def form_with(model: nil, scope: nil, url: nil, format: nil, **options, &block)
end
end
model = _object_for_form_builder(model)
model = convert_to_model(_object_for_form_builder(model))
scope ||= model_name_from_record_or_class(model).param_key
end

@ -387,7 +387,7 @@ def test_form_with_with_persisted_to_model
form_with(model: post_form) { }
expected = whole_form("/posts/123", method: :post) { "" }
expected = whole_form("/posts/123", method: :patch) { "" }
assert_dom_equal expected, output_buffer
end