railsで単一リソースのrouting(resource)を定義するときはダイレクトルーティングも検討する

## 発生する問題
railsのroutingでresourceを定義し`form_with`ヘルパーの`model`引数にモデルのインスタンスを指定するとエラーになる。

## 再現手順

### 1. resourceで単一のリソースのCRUDを作成する
```ruby:config/routes.rb
Rails.application.routes.draw do
  resource :user
end
```

以下のpathが生成される

```shell
$ bundle exec rails routes | grep user
new_user  GET    /user/new(.:format)                                                                          users#new
edit_user GET    /user/edit(.:format)                                                                         users#edit
     user GET    /user(.:format)                                                                              users#show
          PATCH  /user(.:format)                                                                              users#update
          PUT    /user(.:format)                                                                              users#update
          DELETE /user(.:format)                                                                              users#destroy
          POST   /user(.:format)                                                                              users#create
```

### 2. viewでform_withにmodelを指定する

```ruby:app/views/users/new.html.erb
<%= form_with(model: user) do |form| %>
<% end %>
```

### 3. 画面でフォームをサブミットする
エラーになる

```shell
undefined method `users_path' for #<ActionView::Base:0x00000000009420>
Did you mean?  user_path
```

## 原因
`form_with`メソッドはmodel引数に渡されたインスタンスのクラスからurlのpathを判定する。
この判定ロジックで判定されたpathと、単一リソースのrouting(resource)定義で生成されるpathの間で不整合が発生するためエラーが発生する。

## 原因の説明

### 1. `form_with`ヘルパーの`url`引数が存在しない場合、urlを判定するために`polymorphic_path`を呼び出す

```ruby
def form_with(model: nil, scope: nil, url: nil, format: nil, **options, &block)
  options[:allow_method_names_outside_object] = true
  options[:skip_default_ids] = !form_with_generates_ids

  if model
    url ||= polymorphic_path(model, format: format)
```
See: https://github.com/rails/rails/blob/main/actionview/lib/action_view/helpers/form_helper.rb#L737-L742

### 2. `polymorphic_url`は`HelperMethodBuilder.polymorphic_method`を呼び出す

```ruby
def polymorphic_url(record_or_hash_or_array, options = {})
  if Hash === record_or_hash_or_array
    options = record_or_hash_or_array.merge(options)
    record  = options.delete :id
    return polymorphic_url record, options
  end

  if mapping = polymorphic_mapping(record_or_hash_or_array)
    return mapping.call(self, [record_or_hash_or_array, options], false)
  end

  opts   = options.dup
  action = opts.delete :action
  type   = opts.delete(:routing_type) || :url

  HelperMethodBuilder.polymorphic_method self,
                                         record_or_hash_or_array,
                                         action,
                                         type,
                                         opts
end
```
See: https://github.com/rails/rails/blob/main/actionpack/lib/action_dispatch/routing/polymorphic_routes.rb#L101-L121

### 3. `HelperMethodBuilder.polymorphic_method`から`builder.handle_model`が呼ばれる

```ruby
def self.polymorphic_method(recipient, record_or_hash_or_array, action, type, options)
  builder = get action, type

  case record_or_hash_or_array
  .
  .
  else
    method, args = builder.handle_model record_or_hash_or_array
  end
```

See: https://github.com/rails/rails/blob/main/actionpack/lib/action_dispatch/routing/polymorphic_routes.rb#L205-L235

### 4. `builder.handle_model`では`model.persisted?`が`true`の場合は`user_path`、 `false`の場合は`users_path`を返す
```ruby
def handle_model(record)
  args  = []

  model = record.to_model
  named_route = if model.persisted?
    args << model
    get_method_for_string model.model_name.singular_route_key
  else
    get_method_for_class model
  end

  [named_route, args]
end
```

See: https://github.com/rails/rails/blob/main/actionpack/lib/action_dispatch/routing/polymorphic_routes.rb#L261-L273

```ruby
$ model #=> User
$ get_method_for_string model.model_name.singular_route_key #=> "user_path"
$ get_method_for_class model #=> "users_path"
```

### 5. createアクションなので対象のmodelはまだ存在しないため、`users_path`がかえり、urlが存在しないエラーになっている

## 対応
### 1. routesでダイレクトルーティングを指定する

以下のようにインスタンスのクラスが`User`の場合は`user_path`に固定する。

```diff:config/routes.rb
Rails.application.routes.draw do
  resource :user
+ resolve('User') { [:user] }
end
```

### 2. `form_with`にurl引数を指定

```diff:app/views/users/new.html.erb
- <%= form_with(model: user) do |form| %>
+ <%= form_with(model: user, url: user_path) do |form| %>
<% end %>
```

## 参考
https://github.com/rails/rails/issues/1769
https://edgeguides.rubyonrails.org/routing.html#singular-resources

## 環境
rails 6.1.1