#279 Understanding the Asset Pipeline
- Download:
- source codeProject Files in Zip (438 KB)
- mp4Full Size H.264 Video (24.8 MB)
- m4vSmaller H.264 Video (13.5 MB)
- webmFull Size VP8 Video (17.5 MB)
- ogvFull Size Theora Video (28.5 MB)
Asset PipelineはRails 3.1のもっとも大きな新機能ですが、同時にもっとも混乱しやすい機能でもあります。今回のエピソードでは、このAsset PipelineがどのようにRailsアプリケーションのアセットを管理するかを見ることによって、少しその神秘性を取り除いていきたいと思います。もしAsset Pipelineについてまったく知識がない場合は、Rails GuideのAsset Pipelineのページが多くの機能を説明しているので、まずそこから始めるのがいいでしょう。
Rails 3.1アプリケーションを書いたことがある方なら、http://localhost:3000/assets/application.js
にアクセスすればアプリケーションのすべてのJavaScriptを含んだファイルがあることをご存知でしょう。しかしこれはどのような仕組みになっているのでしょうか?
application.js
ファイルには何も特別なことはありません。/app/assets/javascripts
ディレクトリの下に置かれたファイルも同じくアクセス可能です。例えばもしそのディレクトリにgreeting.txtというファイルを作成したら、ブラウザで同じようにhttp://localhost:3000/assets/greeting.txt
にアクセスして中身を見ることができます。ファイルが/app/assets/javascripts
にあったとしても、アクセスするURLは/assets/greeting.txt
になります。これは、/app/assets
の下のどのサブディレクトリにファイルを置いても同じです。任意の新規ディレクトリを作成してそこにファイルを置いたとしても、同じURLからアクセス可能です。ただし新規ディレクトリを作成した場合は、その中のファイルにアクセスするためにサーバを再起動する必要があります。
アセットを追加できる場所は/app/assets
ディレクトリだけではありません。/lib
の下にassetsディレクトリを作成した場合、そこに追加したすべてのファイルは、メインの/app/assets
ディレクトリに置いたかのようにアクセス可能になります。これは/vendor/assets
ディレクトリ下のすべてのファイルにも当てはまります。
現在のアプリケーションに固有ではない汎用的なアセットがある場合、/lib
か/vendor
の下のassetsディレクトリにそれらを置くのが理想でしょう。アプリケーションがjQueryプラグインを使用している場合、そのJavaScriptファイルは、別の人が管理することになるので/vendor/assets
ディレクトリにを置くのが適しています。自分で管理はするがアプリケーションに固有ではないアセットについては、/lib
ディレクトリに置くのがいいでしょう。
もっとも基本的な部分では、Asset Pipelineはロードパスの一覧です。このリストを見るために、コンソールを実行してRails.application.config.assets.paths
を見ます。出力を読みやすくするためにYAML形式で表示させます。
> y Rails.application.config.assets.paths --- - /Users/eifion/store/app/assets/images - /Users/eifion/store/app/assets/javascripts - /Users/eifion/store/app/assets/stylesheets - /Users/eifion/store/lib/assets/greeting.txt - /Users/eifion/store/vendor/assets/stylesheets - /Users/eifion/.rvm/gems/ruby-1.9.2-p180@railspre/gems/jquery-rails-1.0.13/vendor/assets/javascripts
出力には、app/assets
、/lib/assets
、/vendor/assets
以下のすべてのディレクトリが表示されています。リストの最後に、アプリケーションに含んだjquery-rails
gemに由来する興味深いディレクトリがあります。bundle open
コマンドでその中身を見ることができます。
$ bundle open jquery-rails
このコマンドは、シェルの環境変数のBUNDLER_EDITOR
かEDITOR
で定義されたテキストエディタでgemを開きます。gemのファイルを見ると、vendor/asset/javascripts
ディレクトリにAsset Pipelineを介して読み込むことができるjQueryのファイルがいくつか含まれているのがわかります。
予想される通り、ブラウザからこれらすべてのファイルにassets
パスの下でアクセス可能ですが、それはそれらのディレクトリがAsset Pipelineのロードパスに含まれるからです。
興味深いことに、つまりこれはRuby gemが単にRubyコードを管理するためだけのものではないということを意味しています。Javascriptやその他のアセットも、gemの中で同じように管理することができるのです。おそらく今後は、JavaScriptライブラリがRuby gemの形式で公開される事例が増え、それによって、それらもBundlerの依存関係管理の利点を享受できるようになるでしょう。
Sprocketsでアセットを管理する
ここでアプリケーションのapplication.js
ファイルに戻って、中身を見ていくことにしましょう。
// This is a manifest file that'll be compiled into including all the files listed below. // Add new JavaScript/Coffee code in separate files in this directory and they'll automatically // be included in the compiled file accessible from http://example.com/assets/application.js // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the // the compiled file. // //= require jquery //= require jquery_ujs //= require_tree .
ファイルにはコメントしかありませんが、その一部は重要な意味を持っています。この種のファイルはmanifestとよばれ、内部的にはSprocketsに管理されます。このファイルへのリクエストを受け取ると、Sprocketsはmanifestを見て、 そこに記述されているすべてのファイルを結合して、このファイルのコードの前にその内容をインクルードします。
ここでもロードパスが機能します。このファイルにはrequire jquery
(拡張子の.js
は任意のため、省略できます)が含まれています。Sprocketsはロードパスの中からこのファイルを探し、今回の場合はjquery-rails
エンジンのvendor/asset/javascripts
ディレクトリからロードします。
このロードパスに存在するすべてのJavaScriptファイルを追加できます。そのため、ファイルにrequire jquery-ui
を追加すると、gemのjquery-ui.js
ファイルがインクルードされます。これはCoffeeScriptファイルにも当てはまります。require home
を含めると /app/assets/javascripts/home.js.coffee
ファイルが解析されてインクルードされます。
しかしhome
ファイルを含める必要はありません。というのも、ファイルの一番下にrequire_tree .
があり、ここでのドット(.)はカレントディレクトリを表しているからです。これは、そのディレクトリやそのサブディレクトリにあるすべてのJavaScriptやCoffeeScriptのファイルが含まれるということです。
階層の中の特定のファイルを除外したければ、それも可能です。例えば、サイトにadminページがあり、これらのページを見るときだけ特定のJavaScriptファイルが含まれるようにしたいとします。デフォルトではこれらのファイルはサイトのすべてのページに含まれてしまいます。
含まれたファイルを見たい場合は、URLにdebug_assets=1
パラメータを追加します。この指定によってJavaScriptファイルは結合されず、ページのソースを見ると、Sprocketsがインクルードするファイルがadminディレクトリの中のものも含めてすべて表示されます。
この問題への対処方法はいくつかあります。require_tree
の代わりにrequire_directory
を使用すれば、カレントディレクトリにあるファイルのみを読み込み、サブディレクトリのファイルは読み込みません。インクルードされるファイルをより詳細に制御したければ、ディレクトリ全体をインクルードするのではなく、個別のファイルをrequire
することも可能です。もう一つの方法として、すべてのページに含めたいJavaScriptファイルをpublic
サブディレクトリに移動します。その上でrequire_tree ./public
を使用して、それらのファイルだけをインクルードできます。
Sprockets manifestにどのようなコマンドを使用できるかを知りたい方もいるでしょう。まだいいドキュメントがないのですが、directive_processor.rb
ファイルのソースコードのコメントには、動作の仕組みの説明とともに、使用できるコマンドが記載されています。
プリプロセス
Asset Pipelineはプリプロセスにも対応しています。この仕組みを説明するために、新たに作成した/app/assets/anything
ディレクトリにgreeting.txt
というファイルを作成します。そのままでは、このファイルは単なる静的なテキストファイルですが、ファイル名にもう一つ拡張子を追加します。ここではこのファイルにERBコードを追加すると、それが処理されます。
hello world <%= 1 + 1 %>
このファイルをブラウザで見てみるとERBコードが処理されているのがわかります。URLにはプロセッサの拡張子は含んでいません。
基本的にこれがSASSとCoffeeScriptが動作するしくみです。もしファイルに.scss
拡張子がついていたら、これがプリプロセッサ拡張子として扱われて、ファイルはSASS プロセッサに渡されます。拡張子を連結して、例えば.scss.erb
という拡張子をつけてファイルを作ることもできます。このファイルはまずERBプロセッサに渡され、その後SASSプロセッサに渡されます。
プリプロセッサは自由に設定を変えることができます。独自のプロセッサを追加したり、既存のものと置き換えたりすることができます。これはすべてTilt gemによって処理されます。サイトには動作のしくみや利用できる拡張機能についての情報もあります。
本番モード(Production Mode)での相違点
Asset Pipelineの機能についての簡単な概要紹介は以上です。Asset Pipelineは本番モードでの動作にいくつか相違点があるので、エピソードの残りを使って説明します。まずサーバを本番モードで起動します。
$ rails s -e production
アプリケーションのトップページにアクセスしてソースを表示させると、アセットが違う形で配信されていることがわかるでしょう。
<link href="/assets/application-412fe22651f4486c51e54176003a9f57.css" media="screen" rel="stylesheet" type="text/css" /> <script src="/assets/application-3e3a5167191afa70c7b72440eee7dd40.js" type="text/javascript"></script>
ファイル名にハッシュ値が含まれていますが、これはキャッシュするためです。Rails 3.0で用いられていたクエリ文字列を追加する以前の方法と比べると、実際にファイル名を変えるこの方法の方が優れています。またファイル自身を見てみると、通信コストを少なくするためにJavaScriptが縮小化(minify)されているのがわかります。
これらのアセットは自動的にキャッシュされ、Rack Cacheミドルウェアによって配信されるので速度はかなり速いです。もし代わりにWebサーバ自身にアセットの配信とホストをさせたい場合は、次のコマンドを使って事前にプリコンパイルします。
$ rake assets:precompile
このコマンドで、アセットは/public
ディレクトリにプリコンパイルされ、Webサーバから簡単にアクセスできます。
Asset Pipelineに関する今回のエピソードは以上です。忘れずにRails Guideでより詳しい情報をチェックしてください。