app/assets/javascripts/foo.js.coffee で Rails の変数を使いたい
諸般の事情から、スクリプトを使って <img>
タグの src
を特定の条件下でアプリケーション側のアセットに置き換えたい、という事があったんですよ。
大雑把に書くと、つまりこういうのですね。
# app/assets/javascripts/foo.js.coffee
# 今時 jQuery なんて使ってんのかよ、と言われるとまぁ…
$ ->
$('img.portrait').on 'error', ->
$(this).attr 'src', '/assets/portrait/no-image.png'
ただ、Railsのアセットって最終的にはフィンガープリント付与される関係で、これだとアセットプリコンパイルされる環境ではそのままだと参照出来ないわけで、Railsの asset_path
ヘルパーなりを利用して実際に有効なパスを書かないといけないわけです。
でも、CoffeeScriptって、Railsの変数とか実行結果をそのまま置けないじゃないですか。多分。
※色々調べた結果「ない」と結論付けていますが、実際には全然あるかもしれません
# つまり、こういうのが書きたい
$(this).attr 'src', "#{asset_path('portrait/no-image.png')}"
# けどこれコンパイル結果はこうなるから、全然期待通りじゃないじゃん
#=> $(this).attr('src', `${asset_path('portrait/no-image.png')}`);
なので、Railsの方の実行を踏まえたJavaScriptアセットを記述する時にはCoffeeScriptは使えません。故に、Railsの実行が使えるERBを採用し、結果こうなります。
<%# app/assets/javascripts/foo.js.erb %>
$(function(){
$('img.portrait').on('error', function(){
$(this).attr('src', '<%= asset_path("portrait/no-image.png") %>');
});
});
……ほんの些末な一節を書く、たったそれだけのために。
それだけのために、CoffeeScriptのいい感じの記述が使えない。この世は間違ってる。
じゃあERBも併せて使えばよかったんだよ!
アセットパイプラインの動作って、拡張子を連結させてたら末端から順に処理してくれるらしいじゃないですか。
もしかしたら全然常識の範囲なのかもしれないですが。どうして誰も教えてくれなかったんだ!
「聞かれなかったからさ」
ということで、ERBで当該のパス出力部分だけを実行させた後、改めてCoffeeScriptで評価すると、いい感じに期待結果が得られるというわけです。
# app/assets/javascripts/foo.js.coffee.erb
$ ->
$('img.portrait').on 'error', ->
# 本命の portrait/no-image.png の期待通りのパスが文字列として設定される:うれしい
$(this).attr 'src', '<%= asset_path("portrait/no-image.png") %>'
余談: Slimテンプレートでもやりたいんだけど
まぁSlimテンプレート内で coffee:
文脈からRailsの実行を引用したい場合は知りません。そっちは取り敢えず javascript:
文脈で変数に突っ込むとかで対応するでもいいんじゃないですかね……。
// テンプレートの描画側でスクリプト仕込む系実装
javascript:
// 最近は let とか使うんだっけ? JavaScriptにわか勢だからあんま詳しくない
var noImagePath = "#{asset_path('portrait/no-image.png')}";
coffee:
$ ->
$('img.portrait').on 'error', ->
$(this).attr 'src', noImagePath
参考
参考っていうか、なんならRailsガイドにほぼダイレクトに書かれてましたわ!
Railsガイド v6.1 アセットパイプライン #2.3.3 JavaScript/CoffeeScriptとERB
結論:ドキュメントはちゃんと隅々まで読み込みましょう