TechCommit Advent Calendar 2019 - Qiita 3日目担当の井上です。
TechCommitのアドベントカレンダー 12/3が空いてたので軽くネタ投稿をしておきます!
前回はrattcvさんが「ターミナルで使えるファイラrangerを使ってみる」という記事を投稿されています。
ranger使ったことがないので入れてみようかと思いますw
さて、今回自分はTechCommitメンバーのRails初学者さんに質問されて回答したrails-ujsのウケが良かったのでそれの話でもします。
- rails-ujsとはなんぞや
- rails-ujsの使い方はどんなんや
- 具体的にrails-ujsはの動作はどういう感じなんや
- どのように実現してるんや
- rails-ujsは何でこんな事をしているのか
- まとめ
rails-ujsとはなんぞや
Railsの一部のRESTfulな動作や非同期な処理などを実現するために、JavaScriptの送信に関する処理などが書かれたライブラリです。
例えば、Viewヘルパーの form_with
( form_for
など)の remote: true
オプションだったり、 link_to
の method: :delete
などのオプションを「それっぽく動くように」動作させられるようになります。
rails-ujsの使い方はどんなんや
Rails 5.1.0 からはこれらの処理はRails本体のコードに取り込まれている為、Railsで内部的に使われる分には気にしなくても使えます。
昔は rails-ujs
もjQueryで書かれていましたが、Rails自体も脱jQueryが出来るように、CoffeeScriptで書き直されたのでjQueryを入れる必要はなくなりました。
なお、Webpacker(Webpack)を使ってフロントを切り離す場合や、半SPAっぽい動作でRailsのRESTfulなルーティングが動作するようにしたりさせる場合には、npm(yarn)で入れてあげないと使えない場合があります。
具体的にrails-ujsはの動作はどういう感じなんや
例えばViewに、
<%= link_to 'logout', logout_path, method: :delete, data: { confirm: 'マジでログアウトすんのか?' } %>
という処理があった場合、
などの動作をします。
どのように実現してるんや
そんなに難しいことをしていないので、コードを見ればそれぞれ何をしているのか大体分かると思われますが、
ここでは link_to
の method
指定が特に面白い動作をしているので見てみましょう。
link_to の method オプションの確認
まず、Railsでは link_to
ヘルパーに method
のオプションを付けると data-method=""
という属性を持ったHTMLのaタグが出力されます。
さきほど例にあげたerbの処理、
<%= link_to 'logout', logout_path, method: :delete, data: { confirm: 'マジでログアウトすんのか?' } %>
は、サーバーサイドレンダリングで作られるHTMLでは、
<a href="(ログアウトのパス)" data-method="delete" data-confirm="マジでログアウトすんのか?">
のようになります。
rails-ujs
ではこの link_to
ヘルパーによって付与された data-method
や data-confirm
という属性に対して、JavaScriptで処理をするように書かれています。
rails-ujsでdata-method等に対する処理をしている部分
下記が rails-ujs
の data-method
属性に対する処理です。
何をしているのかコメントをいくつか追加して見たので見てみましょう。
Rails.handleMethod = (e) -> # aタグがクリックされた時に走る link = this method = link.getAttribute('data-method') # クリックしたaタグに `data-method` 属性がついていたら取得 return unless method # ついていなければこの処理は不要 href = Rails.href(link) csrfToken = Rails.csrfToken() csrfParam = Rails.csrfParam() form = document.createElement('form') # formを生成 formContent = "<input name='_method' value='#{method}' type='hidden' />" # formタグ内に子要素として `_method` という名前で `data-method` の値のhiddenフィールドを準備 if csrfParam? and csrfToken? and not Rails.isCrossDomain(href) formContent += "<input name='#{csrfParam}' value='#{csrfToken}' type='hidden' />" # CSRF Tokenもform内容として準備 # Must trigger submit by click on a button, else "submit" event handler won't work! # https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/submit formContent += '<input type="submit" />' # 送信ボタンもform内容として準備 form.method = 'post' # formを送信するHTTPリクエスト自体はPOSTのmethod form.action = href # formの送信先(action)はaタグで設定していたhref form.target = link.target form.innerHTML = formContent # form内容として準備したものをformの子要素として追加 form.style.display = 'none' document.body.appendChild(form) # bodyタグに作ったformを動的にこっそり挿入 form.querySelector('[type="submit"]').click() # 自分で発火 stopEverything(e)
つまり、
link_to
によって delete
メソッドなどをaタグで実装できているように感じますが、実際にはrails-ujsが動的にformを組み立てて送信しているという動作になります。
一見とてもキモいですが、実装上はやっぱり便利ですね。
rails-ujsは何でこんな事をしているのか
HTTPの仕様としてはもちろんmethodとしてPUTだのDELETEだののmethodは設定できます。
しかし、(ちょっと理由は探せませんでしたが…)HTMLとしての送信はGETとPOSTしか対応していないからのようです。
よって、送信パラメーターに _method
という名前で "delete"
などの文字列を含めることで、
RailsらしいRESTfulなルーティングでの動作ができるようになっているということですね。
rails-ujs
を webpacker
を使っていてimportし忘れたりすると、Railsでサーバーサイドレンダリングした link_to
の method: :delete
などが動作しなくなります。
まとめ
rails-ujs
は今ではRails本体に含められているが、RailsでSSRしてJSを別管理してるならnpm等で読む必要があるrails-ujs
はViewヘルパー等で作られる属性と組み合わせて、RailsのRESTfulな処理を支える役目をしているrails-ujs
の動作で、実はlink_to
のmethod: :delete
オプションなどがついたaタグは普通のリンクではなくformの送信として動作するように魔改造されている
前回これらの処理を一緒に見ていた方も物凄くテンションが上っていましたw
Railsは特におまじないが多いですが、このような動作を1つ1つ見ていくのは物凄く勉強になるので良いですね。
何か仕様についてゆるく聞きたい方はTechCommitにもぜひ遊びに来てくださいね!
次回TechCommit Advent Calendar 2019 - Qiita 4日目はikemoさんですね!よろしくお願い致します!