homeASCIIcasts

279: Asset Pipelineを理解する 

(view original Railscast)

Other translations: En Es Fr

Other formats:

Written by Naomi Fujimoto

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_EDITOREDITORで定義されたテキストエディタでgemを開きます。gemのファイルを見ると、vendor/asset/javascriptsディレクトリにAsset Pipelineを介して読み込むことができるjQueryのファイルがいくつか含まれているのがわかります。

jquery-rails gemのファイルとディレクトリの構成

予想される通り、ブラウザからこれらすべてのファイルにassetsパスの下でアクセス可能ですが、それはそれらのディレクトリがAsset Pipelineのロードパスに含まれるからです。

jquery.jsファイルはassetsフォルダからアクセスできる

興味深いことに、つまりこれはRuby gemが単にRubyコードを管理するためだけのものではないということを意味しています。Javascriptやその他のアセットも、gemの中で同じように管理することができるのです。おそらく今後は、JavaScriptライブラリがRuby gemの形式で公開される事例が増え、それによって、それらもBundlerの依存関係管理の利点を享受できるようになるでしょう。

Sprocketsでアセットを管理する

ここでアプリケーションのapplication.jsファイルに戻って、中身を見ていくことにしましょう。

/app/assets/javascripts/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ディレクトリの中のものも含めてすべて表示されます。

debug_assetsパラメータを検索文字列に含めた場合、JavaScriptファイルは結合されない

この問題への対処方法はいくつかあります。require_treeの代わりにrequire_directoryを使用すれば、カレントディレクトリにあるファイルのみを読み込み、サブディレクトリのファイルは読み込みません。インクルードされるファイルをより詳細に制御したければ、ディレクトリ全体をインクルードするのではなく、個別のファイルをrequireすることも可能です。もう一つの方法として、すべてのページに含めたいJavaScriptファイルをpublicサブディレクトリに移動します。その上でrequire_tree ./publicを使用して、それらのファイルだけをインクルードできます。

Sprockets manifestにどのようなコマンドを使用できるかを知りたい方もいるでしょう。まだいいドキュメントがないのですが、directive_processor.rbファイルのソースコードのコメントには、動作の仕組みの説明とともに、使用できるコマンドが記載されています。

プリプロセス

Asset Pipelineはプリプロセスにも対応しています。この仕組みを説明するために、新たに作成した/app/assets/anythingディレクトリにgreeting.txtというファイルを作成します。そのままでは、このファイルは単なる静的なテキストファイルですが、ファイル名にもう一つ拡張子を追加します。ここではこのファイルにERBコードを追加すると、それが処理されます。

/app/assets/anything/greeting.txt.erb

hello world <%= 1 + 1 %>

このファイルをブラウザで見てみるとERBコードが処理されているのがわかります。URLにはプロセッサの拡張子は含んでいません。

ブラウザに送信される前にerbコードがプリプロセスされる

基本的にこれが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でより詳しい情報をチェックしてください。