Make `button_to` more model-aware

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.
This commit is contained in:
Sean Doyle 2021-10-08 14:50:02 -04:00
parent 9f980664fc
commit 7d2be2e011
3 changed files with 91 additions and 2 deletions

View File

@ -1,3 +1,22 @@
* 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>
```
*Sean Doyle*
* Add `:day_format` option to `date_select`
date_select("article", "written_on", day_format: ->(day) { day.ordinalize })

View File

@ -332,7 +332,8 @@ module ActionView
remote = html_options.delete("remote")
params = html_options.delete("params")
method = html_options.delete("method").to_s
method = (html_options.delete("method").presence || method_for_options(options)).to_s
method_tag = BUTTON_TAG_METHOD_VERBS.include?(method) ? method_tag(method) : "".html_safe
form_method = method == "get" ? "get" : "post"
@ -753,6 +754,16 @@ module ActionView
html_options["data-method"] = method
end
def method_for_options(options)
if options.is_a?(Array)
method_for_options(options.last)
elsif options.respond_to?(:persisted?)
options.persisted? ? :patch : :post
elsif options.respond_to?(:to_model)
method_for_options(options.to_model)
end
end
STRINGIFIED_COMMON_METHODS = {
get: "get",
delete: "delete",

View File

@ -35,7 +35,10 @@ class UrlHelperTest < ActiveSupport::TestCase
get "/other" => "foo#other"
get "/article/:id" => "foo#article", :as => :article
get "/category/:category" => "foo#category"
resources :workshops
resources :sessions
resources :workshops do
resources :sessions
end
scope :engine do
get "/" => "foo#bar"
@ -161,6 +164,62 @@ class UrlHelperTest < ActiveSupport::TestCase
)
end
def test_button_to_with_new_record_model
session = Session.new(nil)
assert_dom_equal(
%{<form method="post" action="/sessions" class="button_to"><button type="submit">Create Session</button></form>},
button_to("Create Session", session)
)
end
def test_button_to_with_new_record_model_and_block
workshop = Workshop.new(nil)
assert_dom_equal(
%{<form method="post" action="/workshops" class="button_to"><button type="submit">Create</button></form>},
button_to(workshop) { "Create" }
)
end
def test_button_to_with_nested_new_record_model_and_block
workshop = Workshop.new("1")
session = Session.new(nil)
assert_dom_equal(
%{<form method="post" action="/workshops/1/sessions" class="button_to"><button type="submit">Create</button></form>},
button_to([workshop, session]) { "Create" }
)
end
def test_button_to_with_persisted_model
workshop = Workshop.new("1")
assert_dom_equal(
%{<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) { "Update" }
)
end
def test_button_to_with_persisted_model_and_block
workshop = Workshop.new("1")
assert_dom_equal(
%{<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) { "Update" }
)
end
def test_button_to_with_nested_persisted_model_and_block
workshop = Workshop.new("1")
session = Session.new("1")
assert_dom_equal(
%{<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>},
button_to([workshop, session]) { "Update" }
)
end
def test_button_to_with_straight_url_and_request_forgery
self.request_forgery = true