RailsでjQueryを読み込まずにmethod: :deleteのリンクを実現する（Routing Error No route matches [GET] ... の対処法）

jQueryを読み込まずにRailsで開発している場合、deviseのログアウト処理など、`method: :delete`を使っている時に困ることがあります。

```erb
<%= link_to 'ログアウト', destroy_user_session_path, method: :delete %>
```

このように書いていてもget処理にしかならず、routingエラーが出てしまいます。

![Routing Error No route matches [GET] ...](https://qiita-image-store.s3.amazonaws.com/0/90682/f92a7b34-67fd-fe1c-6e60-a3c835520446.png)

原因はざっくり説明するとjQueryを読み込んでいないからで、下記[参考記事](http://d.hatena.ne.jp/spitfire_tree/20120323/1332477590)によると`jquery-ujs`を読み込んでいる時は`jquery-ujs`の`rails.js`がうまいこと動作させてくれているようです。

```js:jquery-ujs/src/rails.js
    // Handles "data-method" on links such as:
    // <a href="/users/5" data-method="delete" rel="nofollow" data-confirm="Are you sure?">Delete</a>
    handleMethod: function(link) {
      var href = rails.href(link),
        method = link.data('method'),
        target = link.attr('target'),
        csrfToken = rails.csrfToken(),
        csrfParam = rails.csrfParam(),
        form = $('<form method="post" action="' + href + '"></form>'),
        metadataInput = '<input name="_method" value="' + method + '" type="hidden" />';

      if (csrfParam !== undefined && csrfToken !== undefined && !rails.isCrossDomain(href)) {
        metadataInput += '<input name="' + csrfParam + '" value="' + csrfToken + '" type="hidden" />';
      }

      if (target) { form.attr('target', target); }

      form.hide().append(metadataInput).appendTo('body');
      form.submit();
    },
```
https://github.com/rails/jquery-ujs/blob/9e805c90c8/src/rails.js#L213-L232



この問題の解決策は

- get処理をするように`route.rb`に書く
- deviseの設定を書き換え、getをdeleteにする

などがよく挙げられていたのですが、あまり気持ちよくないので別の方法がないか調べていました。

```ruby:config/initializers/devise.rbでdeleteをgetにする
# Use this hook to configure devise mailer, warden hooks and so forth.
# Many of these configuration options can be set straight in your model.
Devise.setup do |config|
  ...
  # The default HTTP method used to sign out a resource. Default is :delete.
  config.sign_out_via = :delete # ここをgetにする
  ...
end
```

すると、良さそうな方法が見つかったのでご紹介しようと思います。

## button_toを使う

`link_to`を`button_to`に書き換えるだけで、一応deleteとして動くようになります。

```erb
<%= button_to 'ログアウト', destroy_user_session_path, method: :delete %>
```
```html:HTML出力結果
<form class="button_to" method="post" action="/users/sign_out">
  <input type="hidden" name="_method" value="delete">
  <input type="submit" value="ログアウト">
  <input type="hidden" name="authenticity_token" value="（省略）">
</form>
```


## `confirm`に対応させる

ただ、これは確認ダイアログを出す処理には対応していません（押してもconfirmが出ない）。これだと、削除処理を行うときは少し心もとないので、以下の様なjsを追加してやります。

```js
window.onload = () => {

  class Confirm {
    constructor(el) {
      this.message = el.getAttribute('data-confirm')
      if (this.message) {
        el.form.addEventListener('submit', this.confirm.bind(this))
      } else {
        console && console.warn('No value specified in `data-confirm`', el)
      }
    }

    confirm(e) {
      if (!window.confirm(this.message)) {
        e.preventDefault();
      }
    }
  }

  Array.from(document.querySelectorAll('[data-confirm]')).forEach((el) => {
    new Confirm(el)
  })

}
```

これでconfirmも動くようになり、削除などを伴う処理でもいい感じに処理ができるようになりました。

```erb
<%= button_to 'ログアウト', destroy_user_session_path, method: :delete, data: { confirm: 'ログアウトしてもよろしいですか？' } %>
```

## <span id="sankou">参考記事</span>
- [Devise で Sign Out が Routing Error になる問題の対処方（method の delete が get になる）Rails 4.x - Ruby on Railsのビシバシはぁはぁ日記](http://tango-ruby.hatenablog.com/entry/2015/09/30/135738)
- [link_to に :method =&gt; :delete を指定した時の動作 - happy lie, happy life](http://d.hatena.ne.jp/spitfire_tree/20120323/1332477590)
- [Delete in Rails Without jQuery and UJS | Viget](https://www.viget.com/articles/delete-in-rails-without-jquery-and-ujs)