Webシステムを作っていると、データベースにマスターを入れることもありますが、それをJavaScript側にどのように引き渡すかも、悩ましいものです。
なお、ここでは「管理画面からならマスターは更新できる」というものではなく、「デプロイ時にシードするだけで、Railsから変更することはない」ようなマスターについて考えます。
いくつかの手法
このようなことをしたい場合にも、いくつか方法が考えられます。
JSON APIを用意する
いちばん素直に考えれば、APIを叩いてJSONを返すようにすることでも値の受け渡しは可能です。
- メリット…サーバサイドはシンプルに実装できる
- デメリット…同期的な取得ができない、使うJavaScriptのコードが煩雑になる
JavaScriptに書き出す
Sprocketsやrails-erb-loader
などはRails環境で.js.erb
を実行できますので、事前に必要な値を書き出したJavaScriptを生成しておく、という手段もあります。
- メリット…事前にJavaScriptにまでしておくため、実行時の負担は少ない(Turbolinksにも載せられる)
- デメリット…デプロイ環境にもDBの準備が必要になる、
rails-erb-loader
が別プロセスの呼び出しになるなどデプロイ処理が複雑化する
ビューに書き出す
ビューに適当な方法で値を埋め込んで、それを読み取って使う、という方法もあります。
- メリット…ふつうのビューとJavaScriptだけで、別に処理を用意する必要がない、値は動的に生成するけど同期的な取得も可能
- デメリット…使うページごとに値を書き出していくと、ビューが肥大化する
JavaScriptを返すビューを用意する
今回は、この方法について取り上げます。
- メリット…Railsで制御するので自由度が高い
- デメリット…自由度が高い分、正しく作るのが難しい
JavaScriptのビュー
Railsのビューはフォーマットごとに用意できるので、適切なコントローラーのshow
アクションに対応するようにshow.js.erb
を作ればいい…とも思えるのですが、そのままではActionController::InvalidCrossOriginRequest
となってしまいます。
CSRF対策→止める
JSONPなどにも使われている技ですが、<script>
に書かれたsrc
のURLは、クロスオリジンであっても読み込み、実行が可能となっています。そのため、<script>
として実行されうる、GETリクエストに対してJavaScriptを返すアクションは、protrect_from_forgery
が有効な場合、ActionController::InvalidCrossOriginRequest
を起こすようになっています。
とはいえ、今回のようなマスター系を前提とした場合、CSRFは問題となりません。protrect_from_forgery except: :show
のように上書きして、CSRF対策自体を止めましょう。
キャッシュの活用
そのままやっていては、毎回毎回データベースを読んでJavaScriptとして吐き出すことになって、効率が悪すぎます。
- ビューレベルのキャッシュ→
<%= cache do %>...<% end %>
を活用して、出来上がったビューをキャッシュしておきましょう。 - ブラウザキャッシュ→コントローラーで
expires_in
をかけると、キャッシュヘッダの調節ができます。マスター更新時のキャッシュ無効化のために、適当なキーを指定しておいてもいいかも知れません。 - Turbolinks→ビューとして生成したJavaScriptにも、Turbolinksは問題なく適用できます。
data-turbolinks-track="reload"
(Turbolinks 5の場合)を設定しておきましょう。