アセットパイプライン(Asset Pipeline)in Ruby on Rails

このドキュメントは Ruby on Rails で用いられるアセットパイプライン解説の翻訳文です。翻訳日:2012年3月頃。整形日:2012年11月12日

導入部

このドキュメントはRuby on Rails 3.1で紹介されたアセットパイプラインの解説書です。これを見れば下の内容が分かるはずです。

  • アセットパイプラインがどんなもので何が出来るのか
  • アプリケーションへの適切な組み込み方
  • アセットパイプラインで得られる恩恵
  • アセットパイプラインを使う前に行うべき前処理
  • アセットをgemに梱包する方法

アセットパイプラインとは何か

アセットを和訳すると『資産』。ここで言うアセットとは、JavaScriptやCSSなど、多用なHTMLに付随するファイルを引っくるめた単語です。
パイプラインはUNIX/LINUXではおなじみの機能をイメージ出来れば大体合っています。

アセットパイプラインは、アセットを連結→最小化→圧縮するフレームワークを提供します。また、生のJavaScript, CSSだけでなく、CoffeeScriptやSass、ERBなどの他言語から書き出す機能も提供します。

Rails 3.1に先立ち、アセットパイプラインの機能はJammitやSprocketsなど、サードパーティのRubyライブラリによって提供されていました。Rails 3.1ではデフォルトでsprockets gemに依存したSprocketsというアクションパックに統合されました。

この統合はRailsConf 2011でのDHHのキーノート「デフォルトで早く!」戦略の一環です。Railsのコアに組み込まれたということは、どんな開発者もアセットパイプラインによる恩恵を享受できることを意味します。

Rails 3.1ではアセットパイプラインはデフォルトで有効になっています。
無効にしたければ、 config/application.rb のアプリケーションクラス定義で下記設定を行なってください。

config.assets.enabled = false

また、新アプリケーションを作成する時ならば、下記コマンドのように —skip-sprockets のオプションを付けることで無効にできます。

rails new appname –skip-sprockets

しかし、アセットパイプラインを使ってはならない特別な理由があるのでなければ、そのまま使用するべきでしょう。

主な特徴

パイプラインの一つ目の特徴はアセットの連結です。
連結すれはブラウザがウェブページを描画するのに必要なリクエスト数を削減できるので、とりわけ Production 環境で重要になります。ブラウザが並行して行えるリクエスト数には上限があるため、リクエスト数が少ないことはアプリケーションを早く読み込めることを意味するからです。

Rails 2.x では javascript_include_tag と stylesheet_link_tag メソッドに :cache => true を設置することでJavascript, CSSアセットの連結機能を盛り込んでいました。しかしながらこの技術には制限がありました。具体的には、予めキャッシュを生成しておけませんでしたし、容易にサードパーティライブラリと連携できませんでした。

Rails 3.1 では全てのJavaScriptファイルを一つのマスター.js ファイルに連結し、全CSSファイルを一つのマスター .css ファイルに連結します。後述しますが、この工程はカスタマイズ出来ます。
Production 環境では、RailsはウェブブラウザにキャッシュされるようFingerprinting(『指紋』を意味する。ここで実際に使われるのはMD5ハッシュ値)をファイル名に付与します。
ファイル内容を変更することで Fingerprinting は自動的に変更され、キャッシュを削除できます。

2つ目の特徴は最小化と圧縮です。
CSSに対しては空白とコメントを削除し、JavaScriptに対してはより複雑な工程により完了します。
この工程は内蔵の設定から選べますし、自前のものを選択することも可能です。

3つ目の特徴は実際のアセットへのプリコンパイルを利用した高レベル言語経由でのコーディングです。
デフォルトではCSSに対してSass、JavaScriptに対してCoffeeScript、そしてERBに対応しています。

Fingerprintingとは何か・どう扱えばよいか

Fingerprinting はファイル内容の依存関係を生成する技術です。
ファイル内容が変更されるとファイル名もまた更新されます。
大きくコンテンツに変更が加えられなければ、別サーバーであったりデプロイ日時が異なっていても、2ファイルが同一のものであると判別出来ます。

ファイル名がコンテンツに基づき一意な値であれば、CDN(コンテンツ配信ネットワーク)でもISPでもネットワーク設備でもWebブラウザでもどこに対してもコピーを取ってキャッシュしてもらえるようにHTTPヘッダを立てられます。
コンテンツが更新されたら fingerprint も変わるため、クライアント側に新しいコンテンツをリクエストするよう促せるからです。
これは一般的に cache busting (キャッシュあぼ~ん)として知られています。

fingerprinting のために Rails がしているのはファイル名にコンテンツのハッシュ値を挿入することで、挿入位置は基本的にファイル名末尾です。具体例を挙げると、global.cssという CSS ファイルにファイルコンテンツのMD5ハッシュ値を付与して global-908e25f4bf641868d8683022a5b62f54.css というファイル名が出来上がりました。
これがRailsのアセットパイプラインが取り入れた戦略です。

Railsの古い戦略はビルトインのヘルパーにリンクされた全アセットに時間ベースの文字列を付与するものでした。例えば /stylesheets/global.css?1309495796 のような。

この文字列付与方式にはいくつかの欠点がありました。

1.ファイル名のクエリパラメーターが異なるだけでは、すべて確実にキャッシュされると保証するには不十分
Steve Souders は「キャッシュ可能なリソースにクエリ文字列を付けない」ことを推奨しています。
彼はこのやり方では 5-20% のリクエストがキャッシュされないことを発見しました。
この問題はとりわけCDNで顕著でした。

2.複数サーバーの環境でサーバーノード間で異なるファイル名が使われてしまう
Rails 2.x のデフォルト設定では時間ベースの文字列付与方式を採用していました。
クラスタにアセットがデプロイされた場合、タイムスタンプが同一である保証はありません。結果的にリクエストを処理したサーバーに依存した値になってしまいます。

3.失効したキャッシュがたくさん残る
静的なファイルを再度デプロイしようとした時でも、ファイルの修正時刻が変わっていたらクライアントに全ファイル再読み込みを強いることになります。

Fingerprinting は時間ベースからコンテンツベースに切り替えることにより、これら問題を解決しました、。

Fingerprinting は Production 環境ではデフォルトで有効・その他環境では無効となっています。
切り替えたい場合は config.assets.digest 設定を変更してください。

さらに知るために:

キャッシュ最適化 http://code.google.com/speed/page-speed/docs/caching.html
ファイル名変更時に文字列付与方式を使っちゃだめだよ http://www.stevesouders.com/blog/2008/08/23/revving-filenames-dont-use-querystring/

使い方

前バージョンのRailsでは、全てのアセットが public/images, public/javascripts, public/stylesheets のように publicディレクトリの中に収まっていましたが、アセットパイプラインを用いる今後は app/assets ディレクトリに配置するのが望ましいです。
このディレクトリ内のファイルは『Sprockets』gem 内の Sprocketsミドルウェアに利用されるからです。

アセットを public の中にとどめても構いませんが、public 以下のアセットはアプリケーション・ウェブサーバーに静的ファイルとして取り扱われますので、アセットパイプラインの前処理を施したいファイルは app/assets ディレクトリに格納するようにしてください。

Production 環境のデフォルトでは、Railsはこれらファイルを public/assets ディレクトリにプリコンパイルします。プリコンパイルされたファイルはウェブサーバーによって静的なアセットとして扱われます。Production 環境では app/assets 内のファイルは直接出力されません。

Scaffold かコントローラーを生成する時、Rails は JavaScript(またはCoffeeScript)・CSS・SASS ファイルも生成します。

例えば、ProjectsController を生成するなら、Rails は app/assets/javascripts/projects.js.coffee, app/assets/stylesheets/projects.css.scss も生成します。
<%= javascript_include_tag params[:controller] %> や <%= stylesheet_link_tag params[:controller] %> のように、ファイルが対象となるコントローラーのみで読み込まれるように、JavaScript・CSSはコントローラー毎に一意にすべきです。

CoffeeScript を利用するには ExecJS をサポートしたランタイムが必要です。もし MAC OS X か Windows を利用していたなら、JavaScript ランタイムはすでに OS にインストールされています。全サポートランタイムを知りたい場合は ExecJS のドキュメントをチェックして下さい。

アセットの編成

アセットパイプラインで用いるアセットの配置場所は下記三箇所から選べます。

・app/assets
・lib/assets
・vendor/assets.

app/assets にはアプリケーションが保有するカスタム画像やJS, CSSファイルなどのアセットを格納します。
lib/assets には実際にはアプリケーションのスコープに合わない場合やアプリケーションをまたいで共有している場合の私物アセットを格納します。
vendor/assets にはJavaScriptプラグインやCSSフレームワークなどといった第三者のアセットを格納します。

サーチパス

マニフェストやヘルパーにファイルを参照されると、デフォルトではSprocketsは三箇所を検索します。
デフォルトの三箇所とは app/assets/images 以下のディレクトリで、*の部分は JavaScript やスタイルシートの種類ディレクトリ構成により変化します。

例えば下記パスを参照します。

app/assets/javascripts/home.js
lib/assets/javascripts/moovinator.js
vendor/assets/javascripts/slider.js

そして、マニフェスト内を下記のように参照されます。

//= require home
//= require moovinator
//= require slider

サブディレクトリ内もアクセスされ、

app/assets/javascripts/sub/something.js

下記のように参照されます。

//= require sub/something

Railsコンソールで Rails.application.config.assets.paths を見ればサーチパスを閲覧できます。

config/application.rb にパイプラインで使用するパス(フルパス)を追加できます。例えば

config.assets.paths << Rails.root.join(“app”, “assets”, “flash”)

サーチパスに記載した順にパスは検索されます。
重要なことですが、プリコンパイルのために拡張したパスは Production 環境では無効なので気をつけてください。

インデックスファイルの利用

Sprockets は index という名前のファイル(と、関連あるファイルも)を特別な目的に利用します。

例えば lib/assets/library_name にたくさんのモジュールを含んだ jQuery ライブラリを持っていたとすると、 lib/assets/library_name/index.js はその中のマニフェストとして機能します。
このファイルにインクルードするファイルを順に記述することも出来ますし、単純に require_tree の指示も可能です。

アプリケーション全体にアクセスするライブラリのマニフェストは下記のようになります。

//= require library_name

インデックスを使ってインクルード前に関連あるコードをグループ化することで管理を単純化しファイルをクリーンな状態で保持します。

Sprockets はアセットへのアクセスに特別なメソッドを必要としませんので、使い慣れた javascript_include_tag・stylesheet_link_tag を使用してOKです。

<%= stylesheet_link_tag “application” %>
<%= javascript_include_tag “application” %>

通常の View なら下記のように assets/images ディレクトリへアクセス出来ます。

<%= image_tag “rails.png” %>

アプリケーションでパイプラインが有効かつ現在の環境で無効にされていなければ、このファイルは Sprockets に取り扱われます。
もしこのファイルが存在すれば、ウェブサーバーは public/assets/rails.png を応答します。

また、public/assets/rails-af27b6a414e6da00003503148be9b409.png のようなファイルにMD5ハッシュ値が付与されたファイルリクエストも同様に処理されます。
どのようにハッシュ値が生成されるかについては当ガイドの Production 環境セクションで補完します。

Sprockets は標準のアプリケーションパスやRailsエンジンに追加されたパスを含む config.assets.paths に記述された特定のパスも参照します。

画像は必要ならサブディレクトリに集約されます。この場合、ディレクトリ名を特定すればファイルへアクセス出来ます。

<%= image_tag “icons/rails.png” %>

CSSとERB

アセットパイプラインは自動的に ERB を評価します。
つまり、 application.css.erb のようにCSSアセットに erb の拡張子を付与すれば、asset_path のようなヘルパーをあなたのCSS内で利用できます。

.class { background-image: url(<%= asset_path ‘image.png’ %>) }

これは特定のアセットへの参照を書き出します。
この例では app/assets/images/image.png のような実物へのパスが生成・参照されます。
もしすでに public/assets にfingerprintファイルが存在すればそこが参照されます。

画像データをCSSに直接埋め込むデータURIという方法を使いたければ、 asset_data_uri ヘルパーを利用できます。

#logo { background: url(<%= asset_data_uri ‘logo.png’ %>) }

これは正しいデータURI形式のデータをCSSに挿入します。
下記のクローズタグがスタイルの末尾にならないことに気をつけてください。
%>

CSSとSass

アセットパイプラインを利用する場合、Railsは『Saas・画像・フォント・動画・音楽・JavaScript・CSS・…』様々なアセットへの参照パスを書き換えられねばなりませんし、SassでにおけるハイフンやRubyにおけるアンダースコアのように-urlや-path などのヘルパーが提供されていなければなりません。

image-url(“rails.png”) は url(/assets/rails.png) になり、
image-path(“rails.png”) は “/assets/rails.png”になります。

もっと一般的な書き方も出来ますが、アセットのパスとファイルタイプを特定せねばなりません。

asset-url(“rails.png”, image) ならば url(/assets/rails.png) になり、
asset-path(“rails.png”, image) ならば “/assets/rails.png” になります。

JavaScript/CoffeeScriptとERB

もし application.js.erb のようにJavaScriptアセットにerbの拡張子を付与すれば、JavaScriptコードに asset_path ヘルパーを利用できます。

$(‘#logo’).attr({
src: “<%= asset_path(‘logo.png’) %>”
});

このコードはあるアセットへの参照を書き出します。
同様に、”application.js.coffee.erb”のようにCoffeeScriptファイルにもerb拡張子を用いて asset_path ヘルパーを利用できます

$(‘#logo’).attr src: “<%= asset_path(‘logo.png’) %>”

マニフェストファイルとディレクティヴ

Sprockets はどのアセットをインクルード・応答するか決定するのにマニフェストファイルを利用します。
マニフェストファイルは一つのCSS・JavaScriptを生成するのにどのファイルが必要か指示するディレクティヴ(指示命令)を持ちます。
これらディレクティヴを伴って Sprockets は特定のファイルを読み込み、必要なら加工し、単一ファイルに連結し、Rails.application.config.assets.compressがtrueであれば連結します。
多くのファイルではなく単一ファイルとして応答することによりブラウザのリクエスト時間を減らせるので、ページ読み込み時間は劇的に削減出来ます。
例えば、新しいRailsアプリケーションの app/assets/javascripts/application.js ファイルには下記の行があります。

// …
//= require jquery
//= require jquery_ujs
//= require_tree .

JavaScriptファイルであれば//=で行が始まります。このケースではrequireとrequire_treeディレクティヴ(指示命令)が使用されています。
requreディレクティヴはSprocketsに「このファイルが必要ですよ」と伝えています。
つまり、あなたはjquery.jsとjquery_ujs.jsファイルがSprocketsの検索ディレクトリのどこかに必要だと言っているわけです。
拡張子を厳密に書く必要はありません。Sprocketsは .js が付くだろうと推測して探してくれるからです。

Rails 3.1でjquery-rails gemはアセットパイプラインを通じて jquery.js と jquery_ujs.js を供給します。
が、アプリケーションの中には見当たりませんね。

require_tree ディレクティヴは Sprockets に特定ディレクトリ内を再帰的に検索し、全ての JavaScript ファイルを格納して出力させる指示です。
これらはマニフェストファイルとの相対パスで記述されていなければなりません。
サブディレクトリを検索せず、特定の一ディレクトリ内の全JavaScriptを格納するrequire_directory というディレクティヴも使用できます。

ディレクティヴは上から下へ処理されますが、require_tree でのファイル処理順序は定められていません。
このパス内ではランダムに処理されると思っておくべきです。
連結したファイル中で特定のJavaScriptが一番上に来るよう保証するには、マニフェストの一番上に記述せねばなりません。
※何度もrequireディレクティヴを記述しても、同じ項目は一度しか出力されません


また、デフォルトのRailsは app/assets/stylesheets/application.css に下行を持つファイルを生成します。

/* …
*= require_self
*= require_tree .
*/

JavaScriptファイルに対して動作していたディレクティヴはスタイルシートに対しても動作します(と言っても、JavaScriptではなくスタイルシートをインクルードすることになるのは自明ですが)。
CSSマニフェスト中の require_tree ディレクティヴもJavaScriptマニフェスト中のものと同じ動作をし、現在のディレクトリ中すべてのスタイルシートを読み込みます。

例では require_self が使われています。
これは require_self が呼び出された正確な場所にCSSファイルを配置します。
require_self が複数回呼び出される場合、最後の一回のみが反映されます。

複数のSassファイルを使用したい場合は通常 Sprockets のディレクティヴを使用する代わりにSassの @import ルールを使用すべきです。
Sprockets のディレクティヴを使用すると全Sassファイルはそれぞれのスコープ下に存在することになり、変数やmixinは定義されたドキュメント内でしか使えません。

マニフェストファイルは必要な数使えます。
例えば admin.css や admin.js マニフェストはadminセクションやapplicationセクションに使用されるJS・CSSを含めます。

順序は”上から下へ”です。
特定のファイルを明記し、順序どおりにコンパイルさせることができます。
例えばこの方法で下記3CSSを連結できます。

/* …
*= require reset
*= require layout
*= require chrome
*/

前準備(Pre-Processing)

アセットのファイル名拡張子でどんな前準備が適用されるか決まります。
コントローラー・scaffoldがデフォルトのRails gemsetと生成されるとき、JavaScriptとCSSファイルの保存場所にCoffeeScriptとSCSSも生成されます。

app/assets/javascripts/projects.js.coffee と app/assets/stylesheets/projects.css.scss ファイルが projects コントロールの場合の実例です。

これらファイルがリクエストされた時、 coffee-script や sass gem によって処理された後、それぞれJavaScript・CSSとしてブラウザに返されます。

異なる拡張子を追加すれば前処理の多重化ができます。処理の順序はファイル名の右から左です。
これらは処理が適用される順に記述すべきです。
たとえば、 app/assets/stylesheets/projects.css.scss.erb というスタイルシートは最初にERB、次にSCSSとして処理され、最終的にCSSとして応答されます。
同様に app/assets/javascripts/projects.js.coffee.erb はERB → CoffeeScript → JavaScript という順に処理されます。

これら前処理の順序は重要なので頭の片隅にいれておいてください。
と言うのも、 app/assets/javascripts/projects.js.erb.coffee のJavaScriptファイルが呼び出されるとした場合、最初にCoffeeScriptのインタプリタが呼び出されますが、これでは次にERBが解釈できず問題が発生してしまうからです。

JavaScript圧縮

JavaScript圧縮用に選べる設定は:closure :uglifier :yui の3種類です。
これらはそれぞれclosure-compiler・uglifier・yui-compressor のgemを必要とします。

Gemfileにはデフォルトでuglifierがあります。
これはNodeJS用のUglifierJSをRuby用にラップするgemです。
これはホワイトスペースを削除し、if-else文を三項演算子に変更するなどの最適化を施します。

下の設定はJavaScript圧縮でuglifierを呼び出します。

config.assets.js_compressor = :uglifier

※JavaScript圧縮を有効にするには、config.assets.compress 設定を true にしておいてください

また、uglifierを使うにはExecJSをサポートするランタイムも必要です。
もしMAC OS XかWindowsを使っていればインストール済みです。
もしすべてのサポート状況が知りたければ、ExecJSの記述をチェックしてください。

Development環境にて

Development 環境ではアセットはマニフェストファイルに記述された順に別々のファイルとして利用されます。

下記記述のある app/assets/javascripts/application.js というマニフェストでは

//= require core
//= require projects
//= require tickets

下記のHTMLを出力することになります。

<script src=”/assets/core.js?body=1″ type=”text/javascript”></script>
<script src=”/assets/projects.js?body=1″ type=”text/javascript”></script>
<script src=”/assets/tickets.js?body=1″ type=”text/javascript”></script>

body パラメータは Sprockets に必要となります。

デバッグ機能をオフにする

config/environments/development.rb を変更すればデバッグモードを変更できます。

config.assets.debug = false

デバッグモードをオフにすると、Sprocketsは必要な全ファイルを連結します。この機能をオフにしたデバッグモードでは、マニフェストは下記を代わりに生成します。

<script src=”/assets/application.js” type=”text/javascript”></script>

サーバー開始後最初にリクエストがあった時、アセットはコンパイル・キャッシュされます。
Sprocketsはリクエストの冗長を削減するため、304 (Not Modified)を応答する must-revalidate というキャッシュ操作のHTTPヘッダをセットします。

リクエストの間にマニフェスト記述中のどれかのファイルに変更があると、サーバーは新しいコンパイルファイルを応答します。

デバッグモードはRailsヘルパーメソッドでも有効にできます。

<%= stylesheet_link_tag “application”, :debug => true %>
<%= javascript_include_tag “application”, :debug => true %>

が、デバッグモードが有効な時、このデバッグ設置は冗長になります。
Development環境では、需要に応じて圧縮モードのON-OFF切り替えも潜在的に可能になっています。

Production環境にて

Production環境ではRailsは下に軽く書いたような fingerprint スキーマを利用します。
デフォルトではRailsはアセットをプリコンパイル済みで静的なアセットが応答されるものだと仮定しています。

プリコンパイルフェイズではコンパイルされるファイルのコンテンツに応じて fingerprint(MD5ハッシュ値)が生成され、ディスクに書き込まれる時に挿入されます。
これらfingerprintの挿入された名前はマニフェスト内のRailsヘルパーに使用されます。

具体的には

<%= javascript_include_tag “application” %>
<%= stylesheet_link_tag “application” %>

は下記のようなコードを生成します。

<script src=”/assets/application-908e25f4bf641868d8683022a5b62f54.js” type=”text/javascript”></script>
<link href=”/assets/application-4dd5b109ee3439da54f5bdfd78a80473.css” media=”screen” rel=”stylesheet” type=”text/css” />

Rails上の fingerprint の振る舞いは config.assets.digest setting 設定で制御されます。
デフォルトでは Production 環境で有効(true)、その他で無効(false)になっています。

通常はデフォルトの設定を変更すべきではありません。
ファイル名に fingerprint がなかったり、遠い将来ヘッダがセットされたりした場合はリモートのクライアントはコンテンツ変更時にファイルを再取得すべきか判断出来なくなります。

アセットのプリコンパイル

Rails はディスクへのパイプライン中でアセットのマニフェストとその他のファイルをコンパイルするために rake task と共に配布されています。
コンパイルされたアセットは config.assets.prefix で指定されたディレクトリ(デフォルトでは public/assets)に書き込まれます。

デプロイ時にサーバー上に直接サーバー上のアセットのコンパイル版を生成するためこのタスクを呼び出せます。
Production環境のファイルシステムへ書き込む権限が無ければ、ローカルでこれを呼び出しコンパイルしたアセットをデプロイ出来ます。

rake task は:

bundle exec rake assets:precompile

アプリケーションで config.assets.initialize_on_precompile 設定を false にすればアセットのプリコンパイルが早くなりますが、テンプレートはアプリケーションオブジェクト・メソッドを見れません。
なお、Herokuで使うにはこの機能を false にする必要があります。

config.assets.initialize_on_precompile を false に設定する場合、デプロイ前にrake assets:precompileをテストしてください。
Development 環境でもこのフラグの値とは無関係にアセットがオブジェクトかメソッドを読み込む箇所でバグが露呈するかもしれません。

Capistrano ヴァージョン2.8.0 以上 は Development 環境でのこの問題を制御するレシピを含有します。
Capfileに下行を追記してください。

load ‘deploy/assets’

これはconfig.assets.prefix設定で指定したフォルダをshared/assetsにリンクします。
ただし、すでにこの共有フォルダ(shared/assets)を使用していたら、独自のデプロイタスクを記述する必要があります。

クライアント側で保持している古いコンパイル済アセットのキャッシュがキャッシュ期間中機能してしまうため、このフォルダがデプロイ間で共有されていることは重要です。
もしアセットをローカルでプリコンパイルしているなら、Gemfile内アセットグループに所属するアセットGemsのインストールを防止するため、『bundle install –without assets』を使用できます。

デフォルトのファイルコンパイルのためのマッチャーはapplication.js, application.css その他JS/CSSでないファイル(言い換えれば.coffeeや.scssファイルなど自動的にJS/CSSにコンパイルされないファイル)を含みます。

[ Proc.new{ |path| !File.extname(path).in?([‘.js’, ‘.css’]) }, /application.(css|js)$/ ]

他に格納したいマニフェスト・スタイルシート・JavaScriptがあれば、プリコンパイル配列に追加できます。

config.assets.precompile += [‘admin.js’, ‘admin.css’, ‘swfObject.js’]

rake taskはアセットとアセットそれぞれのfingerprintsのリストを記述したmanifest.ymlもまた生成します。
これはRailsヘルパーメソッドがマッピングリクエストをスプロケットに渡さないために使用されます。
典型的なマニフェストはこんな感じです:


rails.png: rails-bd9ad5a560b5a3a7be0808c5cd76a798.png
jquery-ui.min.js: jquery-ui-7e33882a28fc84ad0e0e47e46cbf901c.min.js
jquery.min.js: jquery-8a50feed8d29566738ad005e19fe1c2d.min.js
application.js: application-3fdab497b8fb70d20cfc5495239dfc29.js
application.css: application-8af74128f904600e41a6e39241464e03.css
The default location for the manifest is the root of the location specified in config.assets.prefix (‘/assets’ by default).

これはconfig.assets.manifest設定で変更出来ます。
フルパスが必要になります。

config.assets.manifest = ‘/path/to/some/other/location’

Production環境でプリコンパイルされたファイルが見つからなければ、存在しないファイル名と共にSprockets::Helpers::RailsHelper::AssetPaths::AssetNotPrecompiledError の例外がRaiseされます。

サーバー設定

プリコンパイル済みのアセットがファイルシステム上に存在し、Webサーバーから直接送信されます。
このヘッダがデフォルトになるのは遠い未来でハ無いでしょうが、 fingerprinting の恩恵にあやかるため、次の設定を追加してください。

Apache用:

<LocationMatch “^/assets/.*$”>
Header unset ETag
FileETag None
# RFC says only cache for 1 year
ExpiresActive On
ExpiresDefault “access plus 1 year”
</LocationMatch>

nginx用:

location ~ ^/assets/ {
expires 1y;
add_header Cache-Control public;

add_header ETag “”;
break;
}

ファイルをプリコンパイルしたら、SprocketsはアセットをGGIP圧縮したヴァージョン(.gz)を生成します。
Webサーバーは通常普通の圧縮率に妥協しますが、プリコンパイルは一度だけなので Sprockets は最高圧縮率を使用し、転送データサイズを最小に抑えます。
一方で、Webサーバーは未圧縮ファイルを圧縮するのではなくディスクから圧縮済みファイルを直接読みだすよう設定出来ます。

Nginxでは gzip_static を有効にすることで自動的にこれを行えます:

location ~ ^/(assets)/ {
root /path/to/public;
gzip_static on; # to serve pre-gzipped version
expires max;
add_header Cache-Control public;
}

このディレクティヴはWebサーバーがこのコアモジュール付きでコンパイルされていた場合に使用出来ます。
Ubuntuパッケージでは、nginx-light でさえこのモジュールがコンパイルされています。万一存在しなければ手動でコンパイルする必要があるかもしれません。

./configure –with-http_gzip_static_module

もし nginx を Phusion Passenger とコンパイルしたければ、プロンプト時にこの設定を通す必要があります。
Apache用の堅牢な設定は可能ではあるものの扱いづらいです。あちこちで検索するか、Apache用の良い設定例があればこのガイドの更新を手伝ってください。

4.2 Live Compilation

状況によっては Live Compilation を使いたい場合もあるでしょう。
このモードではアセットへのすべてのパイプラインは直接 Sprockets に処理されます。

この設定を有効にするには:

config.assets.compile = true

Development 環境より上の環境では最初のリクエスト時にアセットはコンパイル・キャッシュされ、ヘルパー内で使用されるマニフェスト名はMD5ハッシュ付きの名前に改名されます。

Sprockets は「max-age=31536000」のキャッシュコントロール用HTTPヘッダをセットします。
この信号はサーバー・クライアントブラウザ間の全キャッシュが一年保持されることを表し、サーバーからアセットを取得するリクエスト数を減らす効果を得られます。
アセットはローカルブラウザキャッシュや中間キャッシュを行われる良い機会を得られるのです。

ただし、このモードは多くのメモリを使用する上にパフォーマンスがへたれるのでおすすめしません。

もしJavaScriptランタイムが予めセットされていないシステムにProductionアプリケーションをデプロイしたければ、ひとつGemfileを追加すればOKです。

group :production do
gem ‘therubyracer’
end

パイプラインのカスタマイズ

CSS圧縮

一つだけYUIというCSS圧縮に関する設定が存在します。YUIが提供するのは最小化というCSS圧縮機能です。

下行はYUIの圧縮を有効にします(yui-compressor gem が必要になります)。

config.assets.css_compressor = :yui

※CSS圧縮を有効にするには、config.assets.compress が true に設置しなければなりません。

自前の圧縮方法

CSS と JavaScript の圧縮部分にオブジェクトを渡すことも出来ます。
オブジェクトは compress メソッドを持っていて、一つの文字列引数・一つの文字列戻り値を持っていなければなりません。

class Transformer
def compress(string)
do_something_returning_a_string(string)
end
end

有効にするには application.rb でオブジェクトをコンフィグオプションに渡してください。

config.assets.css_compressor = Transformer.new

アセットパスの変更

Sprockets がデフォルトで使用するパスは /assets ですが、他の場所に変更出来ます。

config.assets.prefix = “/some_other_path”

これはRails 3.1以前からのアップグレードや新リソースを使用したい場合の手近な設定です。

X-Sendfile ヘッダ

X-Sendfileヘッダはアプリケーションの応答を無視し特定のファイルをディスクから取得するWebサーバーのディレクティヴ(指示命令)です。
デフォルトではこのオプションはOFFですが、サーバーがサポートしていれば有効にできます。
有効にすると重荷が減りファイルをサーバーにアップロードするのが早くなります。

Apache と nginx はこの設定をサポートしており、 config/environments/production.rb で有効に出来ます。

# config.action_dispatch.x_sendfile_header = “X-Sendfile” # Apache用
# config.action_dispatch.x_sendfile_header = ‘X-Accel-Redirect’ # nginx用

もしあなたが既存アプリケーションをアップグレードしこの設定を追加したくなった場合は、Production 環境でのみ有効になるよう気をつけて production.rb に設定してください。

キャッシュの動作

Sprockets は Development 環境・Production 環境においてRails標準のキャッシュストアを利用します。
他のキャッシュ方法を追加するのが今後の課題です。

Gemsにアセットを追加する

外部ソースGemsに由来するアセットも存在します。

Railsの標準JavaScriptライブラリ jquery-rails gemが良い例です。
このGemは Rails::Engine を継承したクラスを内包していて、 Rails はこの Gem の中にアセットがあるかもしれないことを通知され、app/assets, lib/assets and vendor/assets のディレクトリが Sprockets の検索パスに追加されます。

自作ライブラリ・Gemをプリプロサッサとして動作させる

すべきことは、Tilt 上に Gems を登録し Sprockets にそこをサーチさせることです。

旧バージョンRailsからのアップグレード

アップグレードには2つの問題があります。
一つは public/ のファイルを新しい場所に移動すること。
別種類ファイルの正しい設置場所に関しては、当ガイダンスのアセット部分を見てください。

もう一方はたくさんある設定項目に正しいデフォルトを設定することです。
下記変更はヴァージョン 3.1.0. でのデフォルトです。

application.rb 内:

# Enable the asset pipeline
config.assets.enabled = true

# Version of your assets, change this if you want to expire all your assets
config.assets.version = ‘1.0’

# Change the path that assets are served from
# config.assets.prefix = “/assets”
In development.rb:

# Do not compress assets
config.assets.compress = false

# Expands the lines which load the assets
config.assets.debug = true
And in production.rb:

# Compress JavaScripts and CSS
config.assets.compress = true

# Choose the compressors to use
# config.assets.js_compressor = :uglifier
# config.assets.css_compressor = :yui

# Don’t fallback to assets pipeline if a precompiled asset is missed
config.assets.compile = false

# Generate digests for assets URLs.
config.assets.digest = true

# Defaults to Rails.root.join(“public/assets”)
# config.assets.manifest = YOUR_PATH

# Precompile additional assets (application.js, application.css, and all non-JS/CSS are already added)
# config.assets.precompile += %w( search.js )
You should not need to change test.rb. The defaults in the test environment are: config.assets.compile is true and config.assets.compress, config.assets.debug and config.assets.digest are false.

下記項目もまたGemfileに追加すべきです。

# Gems used only for assets and not required
# in production environments by default.
group :assets do
gem ‘sass-rails’, “~> 3.1.0”
gem ‘coffee-rails’, “~> 3.1.0”
gem ‘uglifier’
end

If you use the assets group with Bundler, please make sure that your config/application.rb has the following Bundler require statement:

if defined?(Bundler)
# If you precompile assets before deploying to production, use this line
Bundler.require *Rails.groups(:assets => %w(development test))
# If you want your assets lazily compiled in production, use this line
# Bundler.require(:default, :assets, Rails.env)
end

Instead of the old Rails 3.0 version:

# If you have a Gemfile, require the gems listed there, including any gems
# you’ve limited to :test, :development, or :production.
Bundler.require(:default, Rails.env) if defined?(Bundler)

フィードバック

このガイドの品質を高めたくなったら。

もしタイポや実際のエラーを見つけたら、DocRailsをコピーして変更を反映してください。
Railsのブランチには誰でも書き込めます。
あなたが改善に寄与したあとにはまだレビューが控えてますが、DocRailsは定期的に統合されます。

また、不完全であったり情報が古かったりなコンテンツを見つけることもあるかもしれません。
Ruby on Railsガイドのスタイル・記法のガイドラインをチェックし、変なドキュメントがあったら補完してください。

なんらかの理由により自分で補完出来ない場合は、問題を公開してください。
そして最後に手短に。Ruby on Railsのドキュメントについて話したいことがあれば rubyonrails-docs メーリングリストは歓迎しています。