sprocketsの機能に不満を感じたのでgemを作った


Railsのassets関連の機構として採用されているSprockets.github.com


かなり便利な上に,普段あまりこいつの機能について意識せずに使える設計となっているので,見事だなぁと思うわけですが,ちょっとだけ不満なことがありました.

Sprocketsの機能により,

<%= stylesheet_link_tag "application", media: "all" %>

と記述しておけば,実際にhtmlとしてレンダリングされるときには,

<link rel="stylesheet" href="/assets/stylesheets/application.css" />

というタグが生成される.


実はこの,stylesheet_link_tag自体はActionViewメソッドです.
rails/asset_tag_helper.rb at 4-2-stable · rails/rails · GitHub



それをSprockets-railsが上書きしています.
sprockets-rails/helper.rb at master · rails/sprockets-rails · GitHub


Sprocketsを通すことにより,cssなら自動でapp/assets/sytlesheets/以下を見てくれたり,jsなら自動でapp/assets/javascripts以下を見てくれたり,digestをつけてくれたりと,そういう計らいをしてくれます.


存在しないassetsに関しては,スルーする件


ところがこのstylesheet_link_tag,存在しないファイルを指定した場合でも,気にせずhtmlタグを生成してくれます.
そして,このとき存在しないcssを指定するとどうなるのか.


もちろん読み込めないcssなので,ブラウザのデベロッパーコンソールには404エラーが出てきます.

それだけで済めばスタイルが適応されない程度の話なのですが…….

ところが本番だとエラーを吐く


そんな控えめな読み込みエラーなので,うっかり気づかずに本番にアップしたりするとことがあります.
すると,なんと今度はエラーになります.

application.css isn't precompiled


こういうのが出てきます.
もちろん,assets:precompileを忘れた場合もこれが出てくるのですが,存在しないassetsを読み込んだ場合も,同じ扱いになります.


で,これは500エラーになってしまうので,なんとページ自体が見られないわけです.


これ,もうちょっと早い段階で通知できないの?

sprockets-rails-nonexistent

いろいろ探して,sprockets-rails自体にpull requestを飛ばそうとも思ったのですが,どうも拡張的な要素が強すぎる気がして…….

Gemとして作りました.

github.com



rubygems.orgへの登録も済ませてあるので,普通にgem install sprockets-rails-nonexistentで入ります.


development環境,およびtest環境のとき

stylesheet_link_tagと,javascript_include_tagに反応して,ファイルの存在チェックをします.
存在していた場合,特に何もせず,そのままhtmlタグが生成されます.
存在していなかった場合,raiseエラーとなり,Sprockets::FileNotFoundを返します.

開発中の画面であれば,エラーになるのですぐに気づくことが出来ます.

また,テストであってもcontrollerのテストやintegrationテストであれば,表示されるところまでテストで行われるので,その時点でエラーとなり,気づくことが出来ます.

production環境のとき

仮にstylesheet_link_tagや,javascript_include_tagで存在しないファイルを指定した場合,そのまま今までどおりの挙動を示します.

なので,isn't precompiledになることは変化ありません.
というか,production環境に対する影響は特にありません,今まで通りの挙動です.


最近のRailsだともしかしてあまり困らないのかもしれない

このassetsの仕組み自体がRails3.2あたりから取り入れられた仕組みであり,Rails4.0からはデフォルトでturbolinksが入ります.

今回のgemはturbolinksを使うような局面では,あまり役に立たない気がしています.
というのも,turbolinksは,ページの初回ロード時にassetsをすべて読み込んでしまって,その上でpjaxでページの中身だけを切り替える方式です.
なので,stylesheet_link_tagを使うとしても,すべてを初回に読み込むようなassetsの構成にするのが望ましいです(そうでなければ速くならない).

つまりはすべてのページでcssやjsはすべて読み込まれる前提で,実行だけは別々に,という方式が一番turbolinks的なのです.

だとするなら,isn't precompiledが出るとしても,最初の1ページだけの問題であり,そこだけ解決してしまえば残りはすべて同じ構成となるので,一回だけしか困らないこと,なんですね.



今回のgemはそれとは逆行していて,コントローラごと,ページごとに読み込むcssやjsを分けていきたいときに,isn't precompiledが出てしまうと潰しきれない,というのが主眼にあります.

たしかにturbolinksは速くなるのですが,最初からturbolinksで設計していないと,Rails3.2で構築していたものをそのまま移すのはかなり難しいです.
そして,turbolinksを使わない前提であれば,ページをロードする度に必要のないcssやjsの読み込みが走る分,「すべてのassetsを読み込む」方式だとどんどん遅くなってしまうのです.