development環境下でprecompileすると,以降developmentでassetsが上手く読み込まれない

寒くなったら鼻の調子が最悪になりました,@h3_potetoです.
息をするのがつらい.


今日はRailsのasset関連の話をちょとします.


RailsにはAsset Pipelineという機能があります.
これが非常に便利な話は以前したことがあったかと思います.


このassets,実際にproduction環境下で動かすためにはprecompileをするのが一般的です.


たとえば,

config/environments/production.rb

config.assets.compile = false

config.assets.precompile += %w(*.js *.less *.css *.erb)

というような設定をしておくことで,production環境で動くrailsがassets以下のフォルダから動的にコンパイルするのを防ぎ,予めprecompileしておいたassetsのみを参照することができます.

precompileをローカルで行うとおかしい


普通に運用する限り,precompileしたassetsはproductionで動かすときにだけ存在すれば良いはずです.
しかし様々な事情(※後述)により,どうしてもローカルでprecompileしなければならないときがあります.

そうしてprecompileするのですが,一度precompileしてしまうと,developmentでアプリケーションを起動しても,precompile時に生成されたmanifestの値をassetsとして読み込んでしまうことが,たまにあります.


とりあえず試しにassets:clean
$ bundle exec rake assets:clean

rails4.0以降ではassets:clobberという名前に変更されています.



ただ,これだと特になんの変化も起きません.
生成されたmanifestファイルを削除しても変化なし.

キャッシュを消してみる

これはcache_stroeに何を使っているかにもよりますが,一度キャッシュを消してみましょう.
tmp/cache/assetsに大量にファイルができていると思うので,潔く消しましょう.



で,ここまで来てもダメなことがあります.
というか,たいていこの状況になったらこれらの手段では改善しません.


gitの差分を見てみても,特になにか設定が変更されているということはないと思います.

developmentの設定をいじる


一時的にdevelopmentの設定を変更します.
おそらく

config/environments/development.rb

config.assets.debug = true

となっているので,

config.assets.debug = false

に変更します.


もともとのこ設定は,application.css等で読み込んでいる外部ファイル(scssなど)を,個別に読込し直すかどうかの設定になります.
これをtrueにしているため,development環境下では,個々のscssファイルやcoffeeファイルを読み込んでいて,ブラウザ側からファイル別にたどることができます.

precompileをすると,これら個別のファイルがすべてapplication.css等に統合され,各サイトではapplication.cssしか読み込まなくなっているかと思います.

この設定を一度変更してやることで,development環境でのassets読み込みが正常に戻ります.
一度成功したら,assets.debugの設定は元に戻しても大丈夫です.



ローカルでprecompileしたい理由

Railsアプリケーションを単一のサーバーで動作させている限り,さほど大きな問題はありません.
しかし,複数台のサーバーでバランシングして運用する場合,個々のサーバーのpublicフォルダ内にprecompileしたassetsを格納しておくのは,あまり賢い手段とは言い難いです.

そのため,インターファームではAWS S3にprecompileしたassetsファイル群を格納し,それをcloudfrontを通してキャッシュして読み込むという手法を取っています.


これを実現するために,asset_sync(https://github.com/rumblelabs/asset_sync)とaws-sdkを使って,precompileと同時にpublicフォルダ以下に作成されたassetsファイル群をS3にアップロードしています.
そして,manifestファイルのみをコミット対象として,本番にデプロイすることで,最新のprecompileによって生成されたassetsが読み込まれるようになります.


このprecompile,サーバー側で行ってもいいのですが,deployタスクに積むと
・デプロイが重くなる
複数台構成のサーバーで各々がprecompileすると,同じassetsが二重にS3にアップロードされて冗長

という問題が発生します.


となると,ローカルでprecompileしておいて(asset_syncされる),manifestファイルのみをコミットしてpushするというのが望ましい.
だからこそローカルでprecompileが必要になってきます.