きっかけ
書籍のwebを支える技術を読んでいたら、p98に以下の記述がありました。
しかし、現実に一番よく利用されているのはGETとPOSTの2つです。
これはHTMLのフォームで指定できるメソッドがGETとPOSTだけという制限に起因します。
これを読んで、「だとすると、updateアクション(PATCHメソッド)はどうやって実現されるのだろう??」
と思ったのですが、すぐ隣のページに書かれてました。
どうやらRailsは_method パラメーターを使って実現させているみたいです。
実際に確認してみる
お知らせを投稿できるNoticeというModelがあるとします。
ルーティング
(当たり前ですが)updateアクションはPATCHかPUTで発動となっています。
$ rails routes|grep notice
     notices GET      /notices(.:format)                                                                  notices#index
             POST     /notices(.:format)                                                                  notices#create
  new_notice GET      /notices/new(.:format)                                                              notices#new
 edit_notice GET      /notices/:id/edit(.:format)                                                         notices#edit
      notice PATCH    /notices/:id(.:format)                                                              notices#update
             PUT      /notices/:id(.:format)                                                              notices#update
             DELETE   /notices/:id(.:format)                                                              notices#destroy
HTML
editページのformを右クリック→ページのソースを表示で見てみます。
<form class="edit_notice" id="edit_notice_18" action="/notices/18" accept-charset="UTF-8" method="post"><input name="utf8" type="hidden" value="✓" />
  <input type="hidden" name="_method" value="patch" /><input type="hidden" name="authenticity_token" value="lGc0ypr/fV1WxhHZm6OS1ehZMpPj9IIgKF/8S5pm3FglyNHWxFgGLGt25AjoFb9BAINs4iRwg7VXn8eLjv9QGQ==" />
  <label>お知らせ</label>
  <textarea name="notice[body]" id="notice_body">test</textarea>
  <input type="submit" name="commit" value="登録" data-disable-with="登録中…" />
</form>
確かにformタグを見ると、 method="put" や method="patch"ではなく、 method="post" となっています。
そして、隠しパラメータ(hidden)に_methodパラメータを用意しているのが確認できます。↓
<input type="hidden" name="_method" value="patch" />
Wiresharkでパケットキャプチャーして見た
リアルなhttpパケットが見たいのでキャプってみました。
wiresharkでインターフェイスをlocalhostに指定してあげると、localhost:3000とのパケット通信をキャプチャーできます。
画像はeditページでsubmitした時のhttpパケットです。

確かに**POST メソッドのHTTPリクエストが送信されている**ことが確認できます。
また、HTML Form URL Encoded: application/x-www-form-urlencoded 内で
Form item: "_method" = "patch"というパラメーターが確認できる。
おそらくこのパラメーターを見て**"patch"メソッドが送られたこととして処理している**んだと思われる(本当はPOSTメソッドを受け取っているが)。
デバッグ
updateアクション内にbinding.pryを仕込んでデバッグ。
[1] pry(#<NoticesController>)> request
=> #<ActionDispatch::Request:0x00007fd6cb6156d0
 @env=
  {"CONTENT_LENGTH"=>"210",
   "CONTENT_TYPE"=>"application/x-www-form-urlencoded",
   "GATEWAY_INTERFACE"=>"CGI/1.1",
   "PATH_INFO"=>"/notices/15",
   "QUERY_STRING"=>"",
   "REMOTE_ADDR"=>"::1",
   "REMOTE_HOST"=>"::1",
   "REQUEST_METHOD"=>"PATCH",
   "REQUEST_URI"=>"http://localhost:3000/notices/15",
   "SCRIPT_NAME"=>"",
   "SERVER_NAME"=>"localhost",
   "SERVER_PORT"=>"3000",
実際に送信したHTTPリクエストはPOSTメソッドでしたが、Rails上ではPATCHメソッドを受け取ったと認識して処理しているようです。
("REQUEST_METHOD"=>"PATCH")
疑問に思ったこと
今回の確認では_methodでpatchが当てがわれているが、putはどのような条件で出てくるのだろう??
(何かの条件でputのパターン、patchのパターンがあるのだろうか)