アセットパイプライン

本ガイドでは、アセットパイプライン (asset pipeline) について解説します。

このガイドの内容:

1 アセットパイプラインについて

アセットパイプラインとは、JavaScriptやCSSのアセットを最小化 (minify: スペースや改行を詰めるなど) または圧縮して連結するためのフレームワークです。アセットパイプラインでは、CoffeeScriptやSass、ERBなど他の言語で記述されたアセットを作成する機能を追加することもできます。 アセットパイプラインはアプリケーションのアセットを自動的に他のgemのアセットと結合できます。たとえば、jquery-railsにはRailsでAJAXを使えるようにするjquery.jsが含まれています。

アセットパイプラインはsprockets-rails gemによって実装され、デフォルトで有効になっています。アプリケーションの新規作成中にアセットパイプラインを無効にするには、--skip-sprocketsオプションを渡します。

rails new appname --skip-sprockets

Railsではsass-rails gemが自動的にGemfileに追加されます。Sprocketsはアセット圧縮の際にこのgemを使用します。

gem 'sass-rails'

--skip-sprocketsオプションを使用すると、Railsでsass-railsがGemfileに追加されなくなります。アセットパイプラインを後から有効にしたい場合は、このgemもGemfileに追加する必要があります。同様に、アプリケーション新規作成時に--skip-sprocketsオプションを指定するとconfig/application.rbファイルの記述内容がデフォルトから若干異なります。具体的にはsprocket railtieで必要となる記述がコメントアウトされます。アセットパイプラインを手動で有効にする場合は、これらのコメントアウトも解除する必要があります。

# require "sprockets/railtie"

アセット圧縮方式を指定するには、production.rbの該当する設定オプションを設定します。config.assets.css_compressorはCSSの圧縮方式、config.assets.js_compressorはJavaScriptの圧縮方式をそれぞれ指定します。

config.assets.css_compressor = :yui
config.assets.js_compressor = :uglifier

sass-rails gemがGemfileに含まれていれば自動的にCSS圧縮に使用されます。この場合config.assets.css_compressorオプションは設定されません。

1.1 主要な機能

アセットパイプラインの第一の機能はアセットを連結することです。これにより、ブラウザがWebページをレンダリングするためのリクエスト数を減らすことができます。Webブラウザが同時に処理できるリクエスト数には限りがあるため、同時リクエスト数を減らすことができればその分読み込みが高速になります。

SprocketsはすべてのJavaScriptファイルを1つのマスター.jsファイルに連結し、すべてのCSSファイルを1つのマスター.cssファイルに連結します。本ガイドで後述するように、アセットファイルをグループ化する方法は自由にカスタマイズできます。production環境では、アセットファイル名にSHA256フィンガープリントを挿入し、アセットファイルがWebブラウザでキャッシュされるようにしています。このフィンガープリントを変更することでブラウザでキャッシュされていた既存のアセットを無効にすることができます。フィンガープリントの変更は、アセットファイルの内容が変更された時に自動的に行われます。

アセットパイプラインのもうひとつの機能はアセットの最小化 (一種の圧縮) です。CSSファイルの最小化は、ホワイトスペースとコメントを削除することによって行われます。JavaScriptの最小化プロセスはもう少し複雑です。最小化方法はビルトインのオプションから選んだり、独自に指定したりすることができます。

アセットパイプラインの第3の機能は、より高級な言語を使用したコーディングのサポートです。これらの言語で記述されたコードはプリコンパイルされ、実際のアセットになります。デフォルトでサポートされている言語は、CSSに代わるSass、JavaScriptに代わるCoffeeScript、CSS/JavaScriptに代わるERBです。

1.2 フィンガープリントと注意点

アセットファイル名で使用されるフィンガープリントは、アセットファイルの内容に応じて変わります。アセットファイルの内容が少しでも変わると、アセットファイル名も必ずそれに応じて変わります (訳注: MD5の性質により、異なるファイルからたまたま同じフィンガープリントが生成されることはほぼありません)。変更されていないファイルやめったに変更されないファイルがある場合、フィンガープリントも変化しないので、ファイルの内容が完全に同一であることが容易に確認できます。これはサーバーやデプロイ日が異なっていても有効です。

アセットファイル名は内容が変わると必ず変化するので、CDN、ISP、ネットワーク機器、Webブラウザなどあらゆる場面で有効なキャッシュをHTTPヘッダに設定することができます。ファイルの内容が更新されると、フィンガープリントも更新されます。これにより、リモートクライアントは (訳注: 既存のキャッシュを使用せずに) コンテンツの新しいコピーをサーバーにリクエストします。この手法を一般に キャッシュ破棄 (cache busting) と呼びます。

Sprocketsがフィンガープリントを使用する際には、ファイルの内容をハッシュ化したものをファイル名 (通常は末尾) に追加します。たとえば、global.cssというCSSファイル名は以下のようになります。

global-908e25f4bf641868d8683022a5b62f54.css

これはRailsのアセットパイプラインの戦略として採用されています。

以前のRailsでは、ビルトインのヘルパーにリンクされているすべてのアセットに日付ベースのクエリ文字列を追加するという戦略が使用されていました。当時のソースで生成されたコードは以下のようになります。

/stylesheets/global.css?1309495796

このクエリ文字列ベースの戦略には多くの問題点があります。

  1. クエリパラメータ以外にファイル名に違いのないコンテンツは確実にキャッシュされないことがある

    Steve Soudersのブログ記事によると、「キャッシュされる可能性のあるリソースにクエリ文字列でアクセスするのは避けること」が推奨されています。Steveは、5%から20%ものリクエストがキャッシュされていないことに気付きました。クエリ文字列は、キャッシュ無効化が発生する一部のCDNでは役に立ちません。

  2. マルチサーバー環境でファイル名が異なってしまうことがある

    Rails 2.xのデフォルトのクエリ文字列はファイルの更新日付に基いていました。このアセットをサーバークラスタにデプロイすると、サーバー間でファイルのタイムスタンプが同じになる保証がないため、リクエストを受けるサーバーが変わるたびに値が異なってしまいます。

  3. キャッシュの無効化が過剰に発生する

    コードリリース時のデプロイが行われると、アセットに変更があるかどうかにかかわらず すべての ファイルのmtime (最後に更新された時刻) が変更されてしまいます。このため、アセットに変更がなくてもWebブラウザを含むあらゆるリモートクライアントで強制的にアセットが再取得されてしまいます。

フィンガープリントが導入されたことによって上述のクエリ文字列による問題点が解決され、アセットの内容が同じであればファイル名も常に同じになるようになりました。

フィンガープリントはdevelopment環境とproduction環境の両方でデフォルトでオンになります。設定ファイルでconfig.assets.digestオプションを使用してフィンガープリントのオン/オフを制御できます。

詳細については以下を参照してください。

2 アセットパイプラインの使用方法

以前のRailsでは、すべてのアセットはpublicディレクトリの下のimagesjavascriptsstylesheetsなどのサブフォルダに置かれました。アセットパイプライン導入後は、app/assetsディレクトリがアセットの置き場所として推奨されています。このディレクトリに置かれたファイルはSprocketsミドルウェアによってサポートされます。

アセットは引き続きpublicディレクトリ以下に置くことも可能です。config.public_file_server.enabledがtrueに設定されていると、publicディレクトリ以下に置かれているあらゆるアセットはアプリケーションまたはWebサーバーによって静的なファイルとして取り扱われます。プリプロセスが必要なファイルはapp/assetsディレクトリの下に置く必要があります。

productionモードでは、Railsはプリコンパイルされたファイルをpublic/assetsに置きます。プリコンパイルされたファイルは、Webサーバーによって静的なアセットとして扱われます。app/assetsに置かれたファイルがそのままの形でproduction環境で使用されることは決してありません。

2.1 コントローラ固有のアセット

Railsでscaffoldやコントローラを生成すると、JavaScriptファイル (coffee-rails gemがGemfileで有効になっている場合はCoffeeScript) とCSS (sass-rails gemがGemfileで有効になっている場合はSCSS) もそのコントローラ用に生成されます。scaffold生成時には、さらにscaffolds.css (sass-rails gemがGemfileで有効になっている場合はscaffolds.css.scss) も生成されます。

たとえばProjectsControllerを生成すると、app/assets/javascripts/projects.coffeeファイルとapp/assets/stylesheets/projects.scssファイルが新しく作成されます。require_treeディレクティブを使用すると、これらのファイルを即座にアプリケーションから利用できます。require_treeの詳細についてはマニフェストファイルとディレクティブを参照してください。

関連するコントローラで以下のコードを使用することで、コントローラ固有のスタイルシートやJavaScriptファイルをそのコントローラだけで使用できます。

<%= javascript_include_tag params[:controller] %> または <%= stylesheet_link_tag params[:controller] %>

上のコードを使用する際は、require_treeディレクティブを使用していないことを必ず確認してください。require_treeと併用すると、アセットが2回以上インクルードされてしまいます。

アセットのプリコンパイルを使用する場合、ページが読み込まれるたびにコントローラのアセットがプリコンパイルされるようにしておく必要があります。デフォルトでは、.coffeeファイルと.scssファイルは自動ではプリコンパイルされません。プリコンパイルの動作の詳細については、アセットをプリコンパイルするを参照してください。

CoffeeScriptを使用するには、ExecJSがランタイムでサポートされている必要があります。macOSまたはWindowsを使用している場合は、OSにJavaScriptランタイムをインストールしてください。サポートされているすべてのJavaScriptランタイムに関するドキュメントは、ExecJS で参照できます。

config/application.rb設定に以下を追加することで、コントローラ固有のアセットファイル生成を止めることもできます。

config.generators do |g|
  g.assets false
end 

2.2 アセットの編成

パイプラインのアセットは、アプリケーション内のapp/assetslib/assetsvendor/assetsの3つのディレクトリのいずれかに置くことができます。

  • app/assetsは、カスタム画像ファイル、JavaScript、スタイルシートなど、アプリケーション自身が保有するアセットの置き場所です。

  • lib/assetsは、1つのアプリケーションの範疇に収まらないライブラリのコードや、複数のアプリケーションで共有されるライブラリのコードを置く場所です。

  • vendor/assetsは、JavaScriptプラグインやCSSフレームワークなど、外部の団体などによって所有されているアセットの置き場所です。

Rails 3からのアップグレードを行なう際には、lib/assetsvendor/assetsの下に置かれているアセットがRails 4ではアプリケーションのマニフェストによってインクルードされて利用可能になること、しかしプリコンパイル配列の一部には含まれなくなることを考慮に入れてください。ガイダンスについてはアセットをプリコンパイルするを参照してください。

2.2.1 パスの検索

ファイルがマニフェストやヘルパーから参照される場合、Sprocketsはデフォルトのアセットの置き場所である3つのディレクトリからファイルを探します。

3つのディレクトリとは、app/assetsの下にあるimagesjavascriptsstylesheetsディレクトリです。ただしこれらのサブディレクトリは特殊なものではなく、実際にはassets/*以下のすべてのパスが検索対象になります。

以下のファイルを例に説明します。

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

上のファイルはマニフェスト内で以下のように参照されます。

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

サブディレクトリ内のアセットにもアクセスできます。

app/assets/javascripts/sub/something.js

上のファイルは以下のように参照されます。

//= require sub/something

検索パスを調べるには、RailsコンソールでRails.application.config.assets.pathsを調べます。

config/initializers/assets.rbに記述することで、標準のassets/*に加えて追加の (fully qualified) パスをパイプラインに追加することができます。以下の例で説明します。

Rails.application.config.assets.paths << Rails.root.join("lib", "videoplayer", "flash")

パスの探索は、検索パスでの出現順で行われます。デフォルトではapp/assetsの検索が優先されるので、対応するパスがlibvendorにある場合はマスクされます。

ここでご注意いただきたいのは、参照したいファイルがマニフェストの外にある場合は、それらをプリコンパイル配列に追加しなければならないという点です。追加しない場合、production環境で利用することができなくなります。

2.2.2 indexファイルを使用する

Sprocketsでは、indexという名前のファイル (および関連する拡張子) を特殊な目的に使用します。

たとえば、たくさんのモジュールがあるjQueryライブラリを使用していて、それらがlib/assets/javascripts/library_nameに保存されているとします。このlib/assets/javascripts/library_name/index.jsファイルはそのライブラリ内のすべてのファイルで利用できるマニフェストとして機能します。このファイルには必要なファイルをすべて順に記述するか、あるいは単にrequire_treeと記述します。

一般に、このライブラリはアプリケーションマニフェストに以下のように記述することでアクセスできます。

//= require library_name

このように記述することで、他でインクルードする前に関連するコードをグループ化できるようになり、記述が簡潔になり保守がしやすくなります。

2.3 アセットにリンクするコードを書く

Sprocketsはアセットにアクセスするためのメソッドを特に追加しません。従来同様javascript_include_tagstylesheet_link_tagを使用します。

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

Railsから同梱されるようになったturbolinks gemを使用している場合、'data-turbolinks-track'オプションが利用できます。これはアセットが更新されてページに読み込まれたかどうかをturbolinksがチェックします。

<%= stylesheet_link_tag "application", media: "all", "data-turbolinks-track" => "reload" %>
<%= javascript_include_tag "application", "data-turbolinks-track" => "reload" %>

通常のビューでは以下のような方法でapp/assets/imagesディレクトリの画像にアクセスできます。

<%= image_tag "rails.png" %>

パイプラインが有効でかつ現在の環境で無効になっていない場合、このファイルはSprocketsによって扱われます。ファイルがpublic/assets/rails.pngに置かれている場合、Webサーバーによって扱われます。

public/assets/rails-f90d8a84c707a8dc923fca1ca1895ae8ed0a09237f6992015fef1e11be77c023.pngなど、ファイル名にSHA256ハッシュを含むファイルへのリクエストについても同様に扱われます。ハッシュの生成法については、本ガイドのproduction環境の場合で後述します。

Sprocketsはconfig.assets.pathsで指定したパスも探索します。このパスには、標準的なアプリケーションパスと、Railsエンジンによって追加されるすべてのパスが含まれます。

必要であれば画像ファイルをサブディレクトリに置いて整理することもできます。この画像にアクセスするには、ディレクトリ名を含めて以下のようにタグで指定します。

<%= image_tag "icons/rails.png" %>

アセットのプリコンパイルを行っている場合 (production環境の場合参照)、存在しないアセットへのリンクを含むページを呼び出すと例外が発生します。空文字へのリンクも同様に例外が発生します。ユーザーから提供されたデータに対してimage_tagなどのヘルパーを使用する場合はご注意ください。

2.3.1 CSSとERB

アセットパイプラインは自動的にERBを評価します。たとえば、cssアセットファイルにerbという拡張子を追加すると (application.css.erbなど)、CSSルール内でasset_pathなどのヘルパーが使用できるようになります。

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

これは、指定されたアセットへのパスを記述します。上の例では、アセット読み込みパスのいずれかにある画像ファイル (app/assets/images/image.pngなど) が指定されたと解釈されます。この画像が既にフィンガープリント付きでpublic/assetsにあれば、このパスによる参照は有効になります。

データURIスキーム (CSSファイルにデータを直接埋め込む手法) を使用したい場合は、asset_data_uriを使用できます。

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

上のコードは、CSSソースに正しくフォーマットされたdata URIを挿入します。

この場合、-%>でタグを閉じることはできませんのでご注意ください。

2.3.2 CSSとSass

アセットパイプラインを使用する場合、最終的にアセットへのパスを変換する必要があります。このために、sass-rails gemは名前が-url-pathで終わる (Sass内ではハイフンですが、Rubyではアンダースコアで表します) 各種ヘルパーを提供しています。ヘルパーがサポートするアセットクラスは、画像、フォント、ビデオ、音声、JavaScript、stylesheetです。

  • image-url("rails.png")url(/assets/rails.png)に変換される
  • image-path("rails.png")"/assets/rails.png"に変換される

以下のような、より一般的な記法を使用することもできます。

  • asset-url("rails.png")url(/assets/rails.png)に変換される
  • asset-path("rails.png")"/assets/rails.png"に変換される
2.3.3 JavaScript/CoffeeScriptとERB

JavaScriptアセットにerb拡張子を追加すると (application.js.erbなど)、以下のようにJavaScriptコード内でasset_pathヘルパーを使用できます。

$('#logo').attr({ src: "<%= asset_path('logo.png') %>" });

これは、指定されたアセットへのパスを記述します。

CoffeeScriptファイルでも、application.coffee.erbのようにerb拡張子を追加することで同様にasset_pathヘルパーを使用できます。

$('#logo').attr src: "<%= asset_path('logo.png') %>"

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

Sprocketsでは、どのアセットをインクルードしてサポートするかを指定するのにマニフェストファイルを使用します。マニフェストファイルには ディレクティブ (directive: 命令、指示) を含めます。ディレクティブを使用して必要なファイルを指定し、それに基いて最終的に単一のCSSやJavaScriptファイルがビルドされます。Sprocketsはディレクティブで指定されたファイルを読み込み、必要に応じて処理を行い、連結して単一のファイルを生成し、圧縮します (Rails.application.config.assets.compressがtrueの場合)。ファイルを連結してひとつにすることにより、ブラウザからサーバーへのリクエスト数を減らすことができ、ページの読み込み時間が大きく短縮されます。圧縮することによってもファイルサイズが小さくなり、ブラウザへの読み込み時間が短縮されます。

新規作成したRailsアプリケーションにはデフォルトでapp/assets/javascripts/application.jsファイルに以下のような記述が含まれています。

// ...
//= require rails-ujs
//= require turbolinks
//= require_tree .

JavaScriptのSprocketsディレクティブは//=で始まります。上の例ではrequirerequire_treeというディレクティブが使用されています。requireは、必要なファイルをSprocketsに指定するのに使用します。ここではrails-ujs.jsturbolinks.jsを必要なファイルとして指定しています。これらのファイルはSprocketsの検索パスのどこかから読み込み可能になっています。このディレクティブでは拡張子を明示的に指定する必要はありません。ディレクティブが.jsファイルに書かれていれば、Sprocketsによって自動的に.jsファイルが必要ファイルとして指定されます。

require_treeディレクティブは、指定されたディレクトリ以下の すべての JavaScriptファイルを再帰的にインクルードし、出力に含めます。このパスは、マニフェストファイルからの相対パスとして指定する必要があります。require_directoryディレクティブを使用すると、指定されたディレクトリの直下にあるすべてのJavaScriptファイルのみをインクルードします。この場合サブディレクトリを再帰的に探索しません。

ディレクティブは記載した順に実行されますが、require_treeでインクルードされるファイルの読み込み順序は指定できません。従って、特定の読み込み順に依存しないようにする必要があります。もしどうしても特定のJavaScriptファイルを他のJavaScriptファイルよりも結合順を先にしたい場合、そのファイルへのrequireディレクティブをマニフェストの最初に置きます。requireおよび類似のディレクティブは、出力時に同じファイルを2回以上インクルードしないようになっています。

Railsは以下の行を含むデフォルトのapp/assets/stylesheets/application.cssファイルも作成します。

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

Railsはapp/assets/javascripts/application.jsapp/assets/stylesheets/application.cssファイルを両方作成します。これはRailsアプリケーション新規作成時に--skip-sprocketsを指定するかどうかにかかわらず行われます。これにより、必要に応じて後からアセットパイプラインを追加することもできます。

JavaScriptで使用できるディレクティブはスタイルシートでも使用できます (なおJavaScriptと異なりスタイルシートは明示的にインクルードされるという違いがあります)。CSSマニフェストにおけるrequire_treeディレクティブの動作はJavaScriptの場合と同様に現在のディレクトリにあるすべてのスタイルシートをrequireします。

上の例ではrequire_selfが使用されています。このディレクティブは、require_self呼び出しが行われた場所にそのファイルが持っているCSSを書き込みます。

Sassファイルを複数使用しているのであれば、Sprocketsディレクティブで読み込まずにSass @importルールを使用する必要があります。このような場合にSprocketsディレクティブを使用してしまうと、Sassファイルが自分自身のスコープに置かれるため、その中で定義されている変数やミックスインが他のSassから利用できなくなってしまいます。

@import "*"@import "**/*"などのようにワイルドカードマッチでツリー全体を指定することもできます。これはrequire_treeと同等です。詳細および重要な警告についてはsass-railsドキュメントを参照してください。

マニフェストファイルは必要に応じていくつでも使用できます。たとえば、アプリケーションのadminセクションで使用するJSファイルとCSSファイルをadmin.cssadmin.jsマニフェストにそれぞれ記載することができます。

読み込み順についても前述のとおり反映されます。特に、個別に指定したファイルは、そのとおりの順序でコンパイルされます。たとえば、以下では3つのCSSファイルを結合しています。

/* ...
*= require reset
*= require layout
*= require chrome
*/

2.5 プリプロセス

適用されるプリプロセスの種類は、アセットファイルの拡張子によって決まります。コントローラやscaffoldをデフォルトのgemセットで生成した場合、通常JavaScriptファイルやCSSファイルが置かれる場所にCoffeeScriptファイルとSCSSファイルがそれぞれ生成されます。先の例では、コントローラ名が"projects"で、app/assets/javascripts/projects.coffeeファイルとapp/assets/stylesheets/projects.scssファイルが生成されます。

developmentモードの場合、あるいはアセットパイプラインが無効になっている場合は、これらのアセットへのリクエストはcoffee-script gemとsass gemが提供するプロセッサによって処理され、それぞれJavaScriptとCSSとしてブラウザへのレスポンスが送信されます。アセットパイプラインが有効になっている場合は、これらのアセットファイルはプリプロセスの対象となり、処理後のファイルがpublic/assetsディレクトリに置かれてRailsアプリケーションまたはWebサーバーによって利用されます。

アセットファイル名に別の拡張子を追加することにより、プリプロセス時に別のレイヤを追加でリクエストすることができます。アセットファイル名の拡張子は、「右から左」の順に処理されます。従って、アセットファイル名の拡張子は、これに従って処理を行うべき順序で与える必要があります。たとえば、app/assets/stylesheets/projects.scss.erbというスタイルシートでは、最初にERBとして処理され、続いてSCSS、最後にCSSとして処理されます。同様にして、 app/assets/javascripts/projects.coffee.erb というJavaScriptファイルの場合では、ERB → CoffeeScript → JavaScript の順に処理されます。

このプリプロセス順序は非常に重要ですので、心に留めておいてください。たとえば、仮にapp/assets/javascripts/projects.erb.coffeeというファイルを呼び出すと、最初にCoffeeScriptインタプリタによって処理されます。しかしこれは次のERBで処理できないので問題が発生することがあります。

3 development環境の場合

developmentモードの場合、アセットは個別のファイルとして、マニフェストファイルの記載順に読み込まれます。

app/assets/javascripts/application.jsというマニフェストの内容が以下のようになっているとします。

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

上によって以下のHTMLが生成されます。

<script src="/assets/core.js?body=1"></script>
<script src="/assets/projects.js?body=1"></script>
<script src="/assets/tickets.js?body=1"></script>

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

3.1 アセットが見つからない場合にエラーをraiseする

sprockets-rails >= 3.2.0を使っている場合は、アセットの探索時に何も見つからなかった場合の挙動を設定できます。「asset fallback」を無効にすると、アセットが見つからない場合にエラーをraiseします。

config.assets.unknown_asset_fallback = false

「asset fallback」を有効にすると、エラーをraiseせずにパスを出力します。アセットのフォールバック動作はデフォルトで有効になっています。

3.2 ダイジェストをオフにする

config/environments/development.rbを更新して以下のようにすることで、ダイジェストをオフにできます。

config.assets.digest = false

このオプションがtrueになっていると、ダイジェストが生成されてアセットへのURLに含まれるようになります。

3.3 デバッグをオフにする

デバッグモードをオフにするには、config/environments/development.rbに以下を追記します。

config.assets.debug = false

デバッグモードをオフにすると、Sprocketsはすべてのファイルを結合して、必要なプリプロセッサを実行します。デバッグモードをオフにすると、上のマニフェストファイルによって以下が生成されるようになります。

<script src="/assets/application.js"></script> 

アセットは、サーバー起動後に最初にリクエストを受け取った時点でコンパイルとキャッシュが行われます。Sprocketsは、must-revalidateというCache-Control HTTPヘッダを設定することで、以後のリクエストのオーバーヘッドを減らします。この場合、ブラウザはレスポンス304 (Not Modified) を受け取ります。

リクエストとリクエストの合間に、マニフェストに記載されているファイルのいずれかで変更が生じた場合、Railsサーバーは新しくコンパイルされたファイルをレスポンスで返します。

Railsのヘルパーメソッドを使用してデバッグモードをオンにすることもできます。

<%= stylesheet_link_tag "application", debug: true %>
<%= javascript_include_tag "application", debug: true %>

デバッグモードが既にオンの場合、:debugオプションは冗長です。

developmentモードで健全性チェックの一環として圧縮をオンにしたり、デバッグの必要性に応じてオンデマンドで無効にしたりすることもできます。

4 production環境の場合

Sprocketsは、production環境では前述のフィンガープリントによるスキームを使用します。デフォルトでは、Railsのアセットはプリコンパイル済みかつ静的なアセットとしてWebサーバーから提供されることが前提になっています。

SHA256はコンパイルされるファイルの内容を元にプリコンパイル中に生成され、ファイル名に挿入されてディスクに保存されます。マニフェスト名はRailsヘルパーによってこれらのフィンガープリント名と置き換えられて使用されます。

以下の例で説明します。

<%= javascript_include_tag "application" %>
<%= stylesheet_link_tag "application" %>

上のコードによって以下のような感じで生成されます。

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

アセットパイプラインの:cacheオプションと:concatオプションは廃止されました。これらのオプションはjavascript_include_tagstylesheet_link_tagから削除してください。

フィンガープリントの振る舞いについてはconfig.assets.digest初期化オプションで制御できます。デフォルトではtrueです。

デフォルトのconfig.assets.digestオプションは、通常は変更しないでください。ファイル名にダイジェストが含まれないと、遠い将来にヘッダが設定されたときに (ブラウザなどの) リモートクライアントがファイルの内容変更を検出して再度取得することができなくなってしまいます。

4.1 アセットをプリコンパイルする

Railsには、パイプラインにあるアセットマニフェストなどのファイルを手動でコンパイルするためのタスクが1つバンドルされています。

コンパイルされたアセットは、config.assets.prefixで指定された場所に保存されます。この保存場所は、デフォルトでは/assetsディレクトリです。

デプロイ時にこのタスクをサーバー上で呼び出すと、コンパイル済みアセットをサーバー上で直接作成できます。ローカル環境でコンパイルする方法については次のセクションを参照してください。

以下がそのタスクです。

$ RAILS_ENV=production bin/rails assets:precompile

Capistrano (v2.15.1以降) にはデプロイ中にこのタスクを扱うレシピが含まれています。 Capfileに以下を追加します。

load 'deploy/assets'

これにより、config.assets.prefixで指定されたフォルダがshared/assetsにリンクされます。 既にこの共有フォルダを使用しているのであれば、独自のデプロイ用タスクを作成する必要があります。

このフォルダは、複数のデプロイによって共有されている点が重要です。これは、サーバー以外の離れた場所でキャッシュされているページが古いコンパイル済みアセットを参照している場合でも、キャッシュ済みページの寿命が来て削除されるまではその古いページへの参照が有効になるようにするためです。

ファイルをコンパイルする際のデフォルトのマッチャによって、app/assetsフォルダ以下のapplication.jsapplication.css、およびすべての非JS/CSSファイル (これにより画像ファイルもすべて自動的にインクルードされます) がインクルードされます。app/assetsフォルダにあるgemも含まれます。

[ Proc.new { |filename, path| path =~ /app\/assets/ && !%w(.js .css).include?(File.extname(filename)) },
/application.(css|js)$/ ]

このマッチャ (および後述するプリコンパイル配列の他のメンバ) が適用されるのは、コンパイル前やコンパイル中のファイル名ではなく、コンパイル後の最終的なファイル名である点にご注意ください。これは、コンパイルされてJavaScriptやCSSになるような中間ファイルはマッチャの対象からすべて除外されるということです (純粋なJavaScript/CSSと同様)。たとえば、.coffee.scssファイルはコンパイル後にそれぞれJavaScriptとCSSになるので、これらは自動的にはインクルードされません。

他のマニフェストや、個別のスタイルシート/JavaScriptファイルをインクルードしたい場合は、config/initializers/assets.rbprecompileという配列を使用します。

Rails.application.config.assets.precompile += %w( admin.js admin.css )

あるいは、以下のようにすべてのアセットをプリコンパイルすることもできます。

# config/initializers/assets.rb
Rails.application.config.assets.precompile << Proc.new do |path|
  if path =~ /\.(css|js)\z/
    full_path = Rails.application.assets.resolve(path).to_path
    app_assets_path = Rails.root.join('app', 'assets').to_path
    if full_path.starts_with? app_assets_path
      logger.info "including asset: " + full_path
      true
    else
      logger.info "excluding asset: " + full_path
      false
    end
  else
    false
  end
end

プリコンパイル配列にSassやCoffeeScriptファイルなどを追加する場合にも、必ず.js.cssで終わるファイル名 (つまりコンパイル後のファイル名として期待されているファイル名) も指定してください。

このタスクによって、すべてのアセットファイルのリストとそれに対応するフィンガープリントを含む.sprockets-manifest-md5hash.jsonファイルも生成されます(md5hashはMD5ハッシュを意味します)。これは、マッピングのリクエストがSprocketsに戻されるのを回避するためにRailsヘルパーメソッドで使われます。典型的なマニフェストファイルは以下のような感じになります。

{"files":{"application-aee4be71f1288037ae78b997df388332edfd246471b533dcedaa8f9fe156442b.js":{"logical_path":"application.js","mtime":"2016-12-23T20:12:03-05:00","size":412383,
"digest":"aee4be71f1288037ae78b997df388332edfd246471b533dcedaa8f9fe156442b","integrity":"sha256-ruS+cfEogDeueLmX3ziDMu39JGRxtTPc7aqPn+FWRCs="},
"application-86a292b5070793c37e2c0e5f39f73bb387644eaeada7f96e6fc040a028b16c18.css":{"logical_path":"application.css","mtime":"2016-12-23T19:12:20-05:00","size":2994,
"digest":"86a292b5070793c37e2c0e5f39f73bb387644eaeada7f96e6fc040a028b16c18","integrity":"sha256-hqKStQcHk8N+LA5fOfc7s4dkTq6tp/lub8BAoCixbBg="},
"favicon-8d2387b8d4d32cecd93fa3900df0e9ff89d01aacd84f50e780c17c9f6b3d0eda.ico":{"logical_path":"favicon.ico","mtime":"2016-12-23T20:11:00-05:00","size":8629,
"digest":"8d2387b8d4d32cecd93fa3900df0e9ff89d01aacd84f50e780c17c9f6b3d0eda","integrity":"sha256-jSOHuNTTLOzZP6OQDfDp/4nQGqzYT1DngMF8n2s9Dto="},
"my_image-f4028156fd7eca03584d5f2fc0470df1e0dbc7369eaae638b2ff033f988ec493.png":{"logical_path":"my_image.png","mtime":"2016-12-23T20:10:54-05:00","size":23414,
"digest":"f4028156fd7eca03584d5f2fc0470df1e0dbc7369eaae638b2ff033f988ec493","integrity":"sha256-9AKBVv1+ygNYTV8vwEcN8eDbxzaequY4sv8DP5iOxJM="}},
"assets":{"application.js":"application-aee4be71f1288037ae78b997df388332edfd246471b533dcedaa8f9fe156442b.js",
"application.css":"application-86a292b5070793c37e2c0e5f39f73bb387644eaeada7f96e6fc040a028b16c18.css",
"favicon.ico":"favicon-8d2387b8d4d32cecd93fa3900df0e9ff89d01aacd84f50e780c17c9f6b3d0eda.ico",
"my_image.png":"my_image-f4028156fd7eca03584d5f2fc0470df1e0dbc7369eaae638b2ff033f988ec493.png"}}

マニフェストのデフォルトの置き場所は、config.assets.prefixで指定された場所のルートディレクトリ (デフォルトでは'/assets') です。

productionモードで見つからないプリコンパイル済みファイルがあると、見つからないファイル名をエラーメッセージに含んだSprockets::Helpers::RailsHelper::AssetPaths::AssetNotPrecompiledErrorが発生します。

4.1.1 遠い将来に期限切れになるヘッダー

プリコンパイル済みのアセットはファイルシステム上に置かれ、Webサーバーから直接クライアントに提供されます。これらプリコンパイル済みアセットには、いわゆる遠い将来に期限切れになるヘッダ (far-future headers) はデフォルトでは含まれていません。したがって、フィンガープリントのメリットを得るためには、サーバーの設定を更新してこのヘッダを含める必要があります。

Apacheの設定:

# Expires* ディレクティブを使用する場合はApacheの
# `mod_expires`モジュールを有効にする必要あり
<Location /assets/>
  # Last-Modifiedフィールドが存在する場合はETagの使用が妨げられる
  Header unset ETag
  FileETag None
  # RFCによるとキャッシュは最長1年まで
  ExpiresActive On
  ExpiresDefault "access plus 1 year"
</Location>

NGINXの場合:

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

  add_header ETag "";
}
4.1.2 GZip圧縮

ファイルをプリコンパイルする際に、Sprocketsによってgzipされた (.gz) アセットも作成されます。Webサーバーによる圧縮はほどほどの圧縮率で行われるのが普通ですが、プリコンパイルが1度発生するとSprocketsによって最大圧縮率で圧縮され、Webサーバーからのデータ転送量が最小化されます。逆に、圧縮されてないファイルを自前で圧縮する代りに、事前に圧縮しておいたコンテンツを直接ディスクに配置しておくようWebサーバーを設定することもできます。

NGINXではgzip_staticを使用することでこれを自動的に行なうことができます。

location ~ ^/(assets)/  {
  root /path/to/public;
  gzip_static on; # gzip済みのバージョンを提供する
  expires max;
  add_header Cache-Control public;
}

このディレクティブは、この機能を提供するコアモジュールがWebサーバーと一緒にコンパイルされている場合に使用可能になります。Ubuntu/Debianパッケージはもちろん、nginx-lightにもこのモジュールがコンパイル済みで用意されています。それ以外の場合には、自分でコンパイルを行う必要があるでしょう。

./configure --with-http_gzip_static_module

NGINXをPhusion Passengerと共にコンパイルする場合は、コンパイル中にプロンプトが表示された時にそのためのオプションを渡す必要があります。

Apacheで堅牢な設定を行なうことは可能ですが、何かとトリッキーであるため、ネットを検索して情報を十分集めてください。(Apache用のよい設定例を確立したら本ガイドに反映いただけると大変助かります)

4.2 ローカルでプリコンパイルを行なう

アセットをローカルでプリコンパイルする理由はいくつか考えられます。たとえば以下のようなものがあります。

  • production環境のファイルシステムへの書き込み権限がない。
  • デプロイ先が複数あり、同じ作業を繰り返したくない。
  • アセットの変更を伴わないデプロイが頻繁に発生する。

ローカルでのコンパイルを行なうことで、コンパイル済みのアセットファイルをGitなどによるソース管理対象に含め、他のファイルと一緒にデプロイできるようになります。

ただし、以下の3つの注意点があります。

  • Capistranoのデプロイメントタスクでアセットのプリコンパイルを行わないこと。
  • development環境で圧縮機能や最小化機能がすべて利用できるようにしておくこと。
  • 以下のアプリケーション設定を変更しておくこと。

config/environments/development.rbに以下の行があります。

config.assets.prefix = "/dev-assets"

prefixを変更すると、Sprocketsはdevelopmentモードで別のURLを使用してアセットを提供し、すべてのリクエストがSprocketsに渡されるようになります。production環境のプレフィックスは/assetsのままです。この変更を行わなかった場合、アプリケーションはdevelopment環境でもproduction環境と同じ/assetsからプリコンパイルしたアセットを提供します。この場合、アセットを再コンパイルしないとローカルでの変更が反映されません。

実用上は、この変更によってローカルでのプリコンパイルが行えるようになり、必要に応じてそれらのファイルをワーキングツリーに追加してソース管理にコミットできるようになります。developmentモードは期待どおり動作します。

4.3 動的コンパイル

状況によっては動的コンパイル (live compilation) を使用したいこともあるでしょう。このモードでは、パイプラインのアセットへのリクエストは直接Sprocketsによって扱われます。

このオプションを有効にするには以下を設定します。

config.assets.compile = true

最初のリクエストを受けると、アセットは上述のdevelopment環境のところで説明したとおりにコンパイルおよびキャッシュされます。ヘルパーで使用されるマニフェスト名にはSHA256ハッシュが含まれます。

また、SprocketsはCache-Control HTTPヘッダーをmax-age=31536000に変更します。このヘッダーは、サーバーとクライアントブラウザの間にあるすべてのキャッシュ (プロキシなど) に対して、サーバーが提供するこのコンテンツは1年間キャッシュしてよいと通知します。これにより、そのサーバーのアセットに対するリクエスト数を減らすことができ、アセットをローカルブラウザのキャッシュやその他の中間キャッシュで代替するよい機会が与えられます。

このモードはデフォルトよりもメモリを余分に消費し、パフォーマンスも落ちるためお勧めできません。

本番アプリケーションのデプロイ先のシステムに既存のJavaScriptランタイムがない場合は、以下をGemfileに記述します。

group :production do
  gem 'mini_racer'
end

4.4 CDN

CDN (コンテンツデリバリーネットワーク)は、全世界を対象としてアセットをキャッシュすることを主な目的として設計されています。それにより、ブラウザからアセットをリクエストすると、ネットワーク上で最も近くにあるキャッシュのコピーが使用されます。production環境のRailsサーバーから (中間キャッシュを使用せずに) 直接アセットを提供しているのであれば、アプリケーションとブラウザの間でCDNを使用するのがベストプラクティスです。

CDNの典型的な利用法は、productionサーバーを "origin" サーバーとして設定することです。つまり、ブラウザがCDN上のアセットをリクエストしてキャッシュが見つからない場合、オンデマンドでサーバーからアセットファイルを取得してキャッシュするということです。たとえば、Railsアプリケーションをexample.comというドメインで運用しており、mycdnsubdomain.fictional-cdn.comというCDNが設定済みであるとします。mycdnsubdomain.fictional-cdn.com/assets/smile.pngがリクエストされると、CDNはいったん元のサーバーのexample.com/assets/smile.pngにアクセスしてこのリクエストをキャッシュします。CDN上の同じURLに対して次のリクエストが発生すると、キャッシュされたコピーがヒットします。CDNがアセットを直接提供する場合、ブラウザからのリクエストが直接Railsサーバーに達することはありません。CDNが提供するアセットはネットワーク上でブラウザに近い位置にあるので、リクエストは高速化されます。また、サーバーはアセットの送信に使う時間を節約できるので、アプリケーション本来のコードをより高速で提供することに集中できます。

4.4.1 CDNで静的なアセットを提供する

CDNを設定するには、Railsアプリケーションがインターネット上でproductionモードで運用されており、example.comなどのように誰でもアクセスできるURLがある必要があります。続いて、クラウドホスティングプロバイダーが提供するCDNサービスと契約を結ぶ必要もあります。その際、CDNの"origin"設定をRailsアプリケーションのWebサイトexample.comにする必要もあります。originサーバーの設定方法のドキュメントについてはプロバイダーにお問い合わせください。

サービスに使用するCDNから、アプリケーションで使用するためのカスタムサブドメイン (例: mycdnsubdomain.fictional-cdn.com) を交付してもらう必要もあります (メモ: fictional-cdn.comは説明用であり、少なくとも執筆時点では本当のCDNプロバイダーではありません)。以上でCDNサーバーの設定が終わりましたので、今度はブラウザに対して、Railsサーバーに直接アクセスするのではなく、CDNからアセットを取得するように通知する必要があります。これを行なうには、従来の相対パスに代えてCDNをアセットのホストサーバーとするようRailsを設定します。Railsでアセットホストを設定するには、config/environments/production.rbconfig.action_controller.asset_hostを以下のように設定します。

config.action_controller.asset_host = 'mycdnsubdomain.fictional-cdn.com'

ここに記述するのは "ホスト名" (サブドメインとルートドメインを合わせたもの) のみです。http://https://などのプロトコルスキームを記述する必要はありません。アセットへのリンクで使用されるプロトコルスキームは、Webページヘのリクエスト発生時に、そのページへのデフォルトのアクセス方法に合わせて適切に生成されます。

この値は環境変数で設定することもできます。これを使用すると、ステージングサーバー (訳注: 検証用に本番サーバーを複製したサーバー) の実行が楽になります。

config.action_controller.asset_host = ENV['CDN_HOST']

上の設定が有効になるためには、サーバーのCDN_HOST環境変数に値 (この場合であればmycdnsubdomain.fictional-cdn.com) を設定しておく必要があります。

サーバーとCDNの設定完了後、以下のアセットを持つWebページにアクセスしたとします。

<%= asset_path('smile.png') %>

上の例では、/assets/smile.pngのようなパスは返されません (読みやすくするためダイジェスト文字は省略してあります)。実際に生成されるCDNへのフルパスは以下のようになります。

http://mycdnsubdomain.fictional-cdn.com/assets/smile.png

smile.pngのコピーがCDNにあれば、CDNが代りにこのファイルをブラウザに送信します。元のサーバーはリクエストがあったことすら知りません。ファイルのコピーがCDNにない場合、CDNは "origin" (この場合example.com/assets/smile.png) を探して今後のために保存しておきます。

CDNで扱うアセットを一部だけに限っておきたい場合、アセットヘルパーのカスタム:hostオプションを使用してconfig.action_controller.asset_hostの値セットを上書きすることもできます。

<%= asset_path 'image.png', host: 'mycdnsubdomain.fictional-cdn.com' %>
4.4.2 CDNのキャッシュの動作をカスタマイズする

CDNはコンテンツをキャッシュすることで動作します。CDNに保存されているコンテンツが古くなったり壊れていたりすると、メリットよりも害の方が大きくなります。本セクションでは、多くのCDNにおける一般的なキャッシュの動作について解説します。プロバイダによってはこの記述のとおりでないことがありますのでご注意ください。

4.4.2.1 CDNリクエストキャッシュ

これまでCDNがアセットをキャッシュするのに向いていると説明しましたが、実際にキャッシュされているのはアセット単体ではなくリクエスト全体です。リクエストにはアセット本体の他に各種ヘッダーも含まれています。ヘッダーの中でもっとも重要なのはCache-Controlです。これはCDN (およびWebブラウザ) にキャッシュの取り扱い方法を通知するためのものです。たとえば、誰かが実際には存在しないアセット/assets/i-dont-exist.pngにリクエストを行い、Railsが404エラーを返したとします。このときにCache-Controlヘッダーが有効になっていると、CDNはこの404エラーページをキャッシュしようとします。

4.4.2.2 CDNヘッダをデバッグする

このヘッダが正しくキャッシュされているかどうかを確認するひとつの方法として、curlを使用するという方法があります。curlを使用して、サーバーとCDNにそれぞれリクエストを送信し、ヘッダーが同じであるかどうかを以下のように確認できます。

$ curl -I http://www.example/assets/application-d0e099e021c95eb0de3615fd1d8c4d83.css
HTTP/1.1 200 OK
Server: Cowboy
Date: Sun, 24 Aug 2014 20:27:50 GMT
Connection: keep-alive
Last-Modified: Thu, 08 May 2014 01:24:14 GMT
Content-Type: text/css
Cache-Control: public, max-age=2592000
Content-Length: 126560
Via: 1.1 vegur

今度はCDNのコピーです。

$ curl -I http://mycdnsubdomain.fictional-cdn.com/application-d0e099e021c95eb0de3615fd1d8c4d83.css
HTTP/1.1 200 OK Server: Cowboy
Last-Modified: Thu, 08 May 2014 01:24:14 GMT Content-Type: text/css
Cache-Control:
public, max-age=2592000
Via: 1.1 vegur
Content-Length: 126560
Accept-Ranges:
bytes
Date: Sun, 24 Aug 2014 20:28:45 GMT
Via: 1.1 varnish
Age: 885814
Connection: keep-alive
X-Served-By: cache-dfw1828-DFW
X-Cache: HIT
X-Cache-Hits:
68
X-Timer: S1408912125.211638212,VS0,VE0

CDNが提供するX-Cacheなどの機能やCDNが追加するヘッダなどの付加的情報については、CDNのドキュメントを確認してください。

4.4.2.3 CDNとCache-Controlヘッダ

Cache-Controlヘッダは、リクエストがキャッシュされる方法を定めたW3Cの仕様です。CDNを使用していない場合、ブラウザはこのヘッダ情報を使用してコンテンツをキャッシュします。このヘッダのおかげで、アセットで変更が発生していない場合にブラウザがCSSやJavaScriptをリクエストのたびに再度ダウンロードせずに済み、非常に有用です。アセットのCache-Controlヘッダは一般に "public" にしておくものであり、RailsサーバーはCDNやブラウザに対してこのヘッダを通じてそのことを通知します。アセットが "public" であるということは、そのリクエストをどんなキャッシュにも保存してよいということを意味します。同様にmax-age もこのヘッダでCDNやブラウザに通知されます。max-ageは、キャッシュがオブジェクトを保存する期間を指定します。この期間を過ぎるとキャッシュは廃棄されます。max-ageの値は秒単位で指定します。最大値は31536000であり、これは一年に相当します。Railsでは以下の設定でこの期間を指定できます。

config.public_file_server.headers = {
  'Cache-Control' => 'public, max-age=31536000'
}

production環境のアセットは上の設定によってアプリケーションから提供されるようになり、キャッシュは1年間保存されます。多くのCDNはリクエストのキャッシュも保存しているので、このCache-Controlヘッダーはアセットをリクエストするすべてのブラウザ (将来登場するブラウザも含める) に渡されます。ブラウザはこのヘッダを受け取ると、次回再度リクエストが必要になったときに備えてそのアセットを当分の間キャッシュに保存してよいことを知ります。

4.4.2.4 CDNにおけるURLベースのキャッシュ廃棄について

多くのCDNでは、アセットのキャッシュを完全なURLに基いて行います。たとえば以下のアセットへのリクエストがあるとします。

http://mycdnsubdomain.fictional-cdn.com/assets/smile-123.png

上のリクエストのキャッシュは、下のアセットへのリクエストのキャッシュとは完全に異なるものとして扱われます。

http://mycdnsubdomain.fictional-cdn.com/assets/smile.png

Cache-Controlmax-ageを遠い将来に設定する場合は、アセットに変更が生じた時にこれらのキャッシュが確実に廃棄されるようにしてください。たとえば、ニコニコマーク画像の色を黄色から青に変更したら、サイトを訪れた人には変更後の青いニコニコマークが見えるようにしたいはずです。RailsをCDNを併用している場合、Railsのアセットパイプラインconfig.assets.digestはデフォルトでtrueに設定されるので、アセットの内容に少しでも変更が生じれば必ずファイル名も変更されます。このとき、キャッシュ内の項目を手動で削除する必要がまったくない点にご注目ください。アセット名が内容に応じて常に一意になるので、ユーザーは常に最新のアセットを利用できます。

5 パイプラインをカスタマイズする

5.1 CSSを圧縮する

YUIはCSS圧縮方法のひとつです。YUI CSS compressorは最小化機能を提供します (訳注: この項では、圧縮 (compress) という語は最小化 (minify) や難読化 (uglify) と同じ意味で使用されており、圧縮後のファイルはzipのようなバイナリになりません)。

YUI圧縮は以下の記述で有効にできます。これにはyui-compressor gemが必要です。

config.assets.css_compressor = :yui

sass-rails gemを使用している場合は、以下のように代替のCSS圧縮方法として指定できます。

config.assets.css_compressor = :sass

5.2 JavaScriptを圧縮する

JavaScriptを圧縮する際には:closure:uglifier:yuiのいずれかのオプションを指定できます。それぞれ、closure-compiler gem、uglifier gem、yui-compressor gemが必要です。

RailsのGemfileにはデフォルトでuglifierが含まれています。このgemは、NodeJSで記述されたUglifyJSをRubyでラップしたものです。uglifierによる圧縮は次のように行われます。ホワイトスペースとコメントを除去し、ローカル変数名を短くし、可能であればifelseを三項演算子に置き換えるなどの細かな最適化を行います。

以下の設定により、JavaScriptの圧縮にuglifierが使用されます。

config.assets.js_compressor = :uglifier

uglifierを利用するにはExecJSをサポートするJavaScriptランタイムが必要です。macOSやWindowsを使用している場合は、OSにJavaScriptランタイムをインストールしてください。

5.3 gzip圧縮されたアセットを提供する

デフォルトで、非圧縮版のアセットの他にgzipで圧縮されたコンパイル済みアセットが生成されます。gzipアセットはデータ転送の削減に役立ちます。これを指定するにはgzipフラグを設定します。

config.assets.gzip = false # disable gzipped assets generation

5.4 独自の圧縮機能を使用する

CSSやJavaScriptの圧縮設定にはあらゆるオブジェクトを設定できます。設定に与えるオブジェクトにはcompressメソッドが実装されている必要があります。このメソッドは文字列のみを引数として受け取り、圧縮結果を文字列で返す必要があります。

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

上のコードを有効にするには、application.rbの設定オプションに新しいオブジェクトを渡します。

config.assets.css_compressor = Transformer.new

5.5 アセット のパスを変更する

デフォルトでは、Sprocketsが使用するパブリックなパスは/assetsになります。

このパスは以下のように変更可能です。

config.assets.prefix = "/他のパス"

このオプションは次のような場合に便利です。アセットパイプラインを使用しない既存のプロジェクトがあり、そのプロジェクトの既存のパスを指定したり、別途新しいリソース用のパスを指定したりする場合です。

5.6 X-Sendfileヘッダー

X-SendfileヘッダーはWebサーバーに対するディレクティブであり、アプリケーションからのレスポンスをブラウザに送信せずに破棄し、代りに別のファイルをディスクから読みだしてブラウザに送信します。このオプションはデフォルトでは無効です。サーバーがこのヘッダーをサポートしていればオンにできます。このオプションをオンにすると、それらのファイル送信はWebサーバーに一任され、それによって高速化されます。この機能の使用法についてはsend_fileを参照してください。

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用

既存のRailsアプリケーションをアップグレードする際にこの機能を使用することを検討している場合は、このオプションの貼り付け先に十分ご注意ください。このオプションを貼り付けてよいのはproduction.rbと、production環境として振る舞わせたい他の環境ファイルのみです。application.rbではありません。

詳細については、production環境用Webサーバーのドキュメントを参照してください。 - Apache - NGINX

6 アセットのキャッシュストア

デフォルトのSprocketsは、development環境とproduction環境でtmp/cache/assetsにアセットをキャッシュします。これは以下のように変更できます。

config.assets.configure do |env|
  env.cache = ActiveSupport::Cache.lookup_store(:memory_store,
                                                { size: 32.megabytes })
end                                                

アセットキャッシュストアを無効にするには以下のようにします。

config.assets.configure do |env|
  env.cache = ActiveSupport::Cache.lookup_store(:null_store)
end

7 アセットをGemに追加する

アセットはgemの形式で外部ソースから持ち込むこともできます。

そのよい例はjquery-rails gemです。これは標準のJavaScriptライブラリをgemとしてRailsに提供します。このgemにはRails::Engineから継承したエンジンクラスが1つ含まれています。このgemを導入することにより、Railsはこのgem用のディレクトリにアセットを配置可能であることを認識し、app/assetslib/assetsvendor/assetsディレクトリがSprocketsの検索パスに追加されます。

8 ライブラリやGemをプリプロセッサ化する

Sprocketsでは機能を拡張するのにProcessors、Transformers、Compressors、Exportersを使います。詳しくはExtending Sprocketsをご覧ください。ここではtext/css (.css)ファイルの末尾にコメントを追加するプリプロセッサを登録しました。

module AddComment
  def self.call(input)
    { data: input[:data] + "/* Hello From my sprockets extension */" }
  end
end

これで入力データを変更するモジュールができましたので、続いてMIMEタイプのプリプロセッサとして登録しましょう。

Sprockets.register_preprocessor 'text/css', AddComment

9 古いバージョンのRailsからアップグレードする

Rails 3.0やRails 2.xからのアップグレードの際には、いくつかの作業を行う必要があります。最初に、public/ディレクトリ以下のファイルを新しい場所に移動します。ファイルの種類ごとの正しい置き場所については、アセットの編成を参照してください。

続いて、JavaScriptファイルの重複を解消します。jQueryはRails 3.1以降におけるデフォルトのJavaScriptライブラリなので、jquery.jsapp/assetsに置かなくても自動的に読み込まれます。

3番目に、多くの環境設定ファイルを正しいデフォルトオプションに更新します。

application.rbの場合。

# アセットのバージョンを指定する。アセットをすべて期限切れにしたい場合はこの値を変更する。
config.assets.version = '1.0'

# config.assets.prefix = "/assets"は、アセットの置き場所となるパスを変更する際に使用する。

development.rbの場合。

# アセットで読み込んだ行を展開する。
config.assets.debug = true

production.rbの場合。

# 使うコンプレッサを選択する(ある場合)
config.assets.js_compressor = :uglifier
# config.assets.css_compressor = :yui

# プリコンパイル済みのアセットが見当たらない場合にアセットパイプラインにフォールバックしない
config.assets.compile = false

# アセットURLのダイジェストを生成する。
config.assets.digest = true

# 追加のアセットをプリコンパイルする (application.js、application.css、およびすべての
# 非JS/CSSファイルが追加済み)
# config.assets.precompile += %w( admin.js admin.css )

Rails 4以降では、Sprocketsのデフォルト設定値をtest環境用のtest.rbに設定しなくなりました。従って、test.rbにSprocketsの設定を行なう必要があります。test環境における以前のデフォルト値は、config.assets.compile = trueconfig.assets.compress = falseconfig.assets.debug = falseconfig.assets.digest = falseです。

以下をGemfileに追加する必要があります。

gem 'sass-rails',   "~> 3.2.3"
gem 'coffee-rails', "~> 3.2.1"
gem 'uglifier'

支援・協賛

Railsガイドは下記のサポーターから継続的な支援を受けています。