1 アセットパイプラインについて
アセットパイプラインとは、JavaScriptやCSSのアセットを最小化 (minify: スペースや改行を詰めるなど) または圧縮して連結するためのフレームワークです。アセットパイプラインでは、CoffeeScriptやSASS、ERBなど他の言語で記述されたアセットを作成する機能を追加することもできます。
技術的には、アセットパイプラインは既にRails 4のコア機能ではありません。フレームワークから分離され、sprockets-railsというgemに書き出されています。
Railsではデフォルトでアセットパイプラインが有効になっています。
Railsアプリケーションを新規作成する際にアセットパイプラインをオフにしたい場合は、以下のように--skip-sprockets
オプションを渡します。
rails new appname --skip-sprockets
Rails 4ではsass-rails
、coffee-rails
、uglifier
gemが自動的にGemfileに追加されます。Sprocketsはアセット圧縮の際にこれらのgemを使用します。
gem 'sass-rails' gem 'uglifier' gem 'coffee-rails'
--skip-sprockets
オプションを使用すると、Rails 4でsass-rails
とuglifier
が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環境では、アセットファイル名にMD5フィンガープリントを挿入し、アセットファイルが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
このクエリ文字列ベースの戦略には多くの問題点があります。
-
クエリパラメータ以外にファイル名に違いのないコンテンツは確実にキャッシュされないことがある
Steve Soudersのブログ記事によると、「キャッシュされる可能性のあるリソースにクエリ文字列でアクセスするのは避けること」が推奨されています。Steveは、5%から20%ものリクエストがキャッシュされていないことに気付きました。クエリ文字列は、キャッシュ無効化が発生する一部のCDNでは役に立ちません。
-
マルチサーバー環境でファイル名が異なってしまうことがある
Rails 2.xのデフォルトのクエリ文字列はファイルの更新日付に基いていました。このアセットをサーバークラスタにデプロイすると、サーバー間でファイルのタイムスタンプが同じになる保証がないため、リクエストを受けるサーバーが変わるたびに値が異なってしまいます。
-
キャッシュの無効化が過剰に発生する
コードリリース時のデプロイが行われると、アセットに変更があるかどうかにかかわらず すべての ファイルのmtime (最後に更新された時刻) が変更されてしまいます。このため、アセットに変更がなくてもWebブラウザを含むあらゆるリモートクライアントで強制的にアセットが再取得されてしまいます。
フィンガープリントが導入されたことによって上述のクエリ文字列による問題点が解決され、アセットの内容が同じであればファイル名も常に同じになるようになりました。
フィンガープリントはproduction環境ではデフォルトでオンになっており、それ以外の環境ではオフになります。設定ファイルでconfig.assets.digest
オプションを使用してフィンガープリントのオン/オフを制御できます。
詳細については以下を参照してください。
2 アセットパイプラインの使用方法
以前のRailsでは、すべてのアセットはpublic
ディレクトリの下のimages
、javascripts
、stylesheets
などのサブフォルダに置かれました。アセットパイプライン導入後は、app/assets
ディレクトリがアセットの置き場所として推奨されています。このディレクトリに置かれたファイルはSprocketsミドルウェアによってサポートされます。
アセットは引き続きpublic
ディレクトリ以下に置くことも可能です。config.serve_static_files
がtrueに設定されていると、public
ディレクトリ以下に置かれているあらゆるアセットはアプリケーションまたはWebサーバーによって静的なファイルとして取り扱われます。プリプロセスが必要なファイルはapp/assets
ディレクトリの下に置く必要があります。
Railsは、productionモードではデフォルトで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.js.coffee
ファイルとapp/assets/stylesheets/projects.css.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がランタイムでサポートされている必要があります。Mac OS XまたはWindowsを使用している場合は、OSにJavaScriptランタイムをインストールしてください。サポートされているすべてのJavaScriptランタイムに関するドキュメントは、ExecJS で参照できます。
config/application.rb
設定に以下を追加することで、コントローラ固有のアセットファイル生成を止めることもできます。
config.generators do |g| g.assets false end
2.2 アセットの編成
パイプラインのアセットは、アプリケーション内のapp/assets
、lib/assets
、vendor/assets
の3つのディレクトリのいずれかに置くことができます。
app/assets
は、カスタム画像ファイル、JavaScript、スタイルシートなど、アプリケーション自身が保有するアセットの置き場所です。lib/assets
は、1つのアプリケーションの範疇に収まらないライブラリのコードや、複数のアプリケーションで共有されるライブラリのコードを置く場所です。vendor/assets
は、JavaScriptプラグインやCSSフレームワークなど、外部の団体などによって所有されているアセットの置き場所です。
Rails 3からのアップグレードを行なう際には、lib/assets
とvendor/assets
の下に置かれているアセットがRails 4ではアプリケーションのマニフェストによってインクルードされて利用可能になること、しかしプリコンパイル配列の一部には含まれなくなることを考慮に入れてください。ガイダンスについてはアセットをプリコンパイルするを参照してください。
2.2.1 パスの検索
ファイルがマニフェストやヘルパーから参照される場合、Sprocketsはデフォルトのアセットの置き場所である3つのディレクトリからファイルを探します。
3つのディレクトリとは、app/assets
の下にあるimages
、javascripts
、stylesheets
ディレクトリです。ただしこれらのサブディレクトリは特殊なものではなく、実際には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/application.rb
に記述することで、標準のassets/*
に加えて追加の (fully qualified) パスをパイプラインに追加することができます。以下の例で説明します。
config.assets.paths << Rails.root.join("lib", "videoplayer", "flash")
パスの探索は、検索パスでの出現順で行われます。デフォルトではapp/assets
の検索が優先されるので、対応するパスがlib
やvendor
にある場合はマスクされます。
ここでご注意いただきたいのは、参照したいファイルがマニフェストの外にある場合は、それらをプリコンパイル配列に追加しなければならないという点です。追加しない場合、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_tag
とstylesheet_link_tag
を使用します。
<%= stylesheet_link_tag "application", media: "all" %> <%= javascript_include_tag "application" %>
Rails 4から同梱されるようになったturbolinks gemを使用している場合、'data-turbolinks-track'オプションが利用できます。これはアセットが更新されてページに読み込まれたかどうかをturbolinksがチェックします。
<%= stylesheet_link_tag "application", media: "all", "data-turbolinks-track" => true %> <%= javascript_include_tag "application", "data-turbolinks-track" => true %>
通常のビューでは以下のような方法でpublic/assets/images
ディレクトリの画像にアクセスできます。
<%= image_tag "rails.png" %>
パイプラインが有効でかつ現在の環境で無効になっていない場合、このファイルはSprocketsによって扱われます。ファイルがpublic/assets/rails.png
に置かれている場合、Webサーバーによって扱われます。
public/assets/rails-af27b6a414e6da00003503148be9b409.png
など、ファイル名にMD5ハッシュを含むファイルへのリクエストについても同様に扱われます。ハッシュの生成法については、本ガイドの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.js.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 4アプリケーションにはデフォルトでapp/assets/javascripts/application.js
ファイルに以下のような記述が含まれています。
// ... //= require jquery //= require jquery_ujs //= require_tree .
JavaScriptのSprocketsディレクティブは//=
で始まります。上の例ではrequire
とrequire_tree
というディレクティブが使用されています。require
は、必要なファイルをSprocketsに指定するのに使用します。ここではjquery.js
とjquery_ujs.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 4はapp/assets/javascripts/application.js
とapp/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.css
とadmin.js
マニフェストにそれぞれ記載することができます。
読み込み順についても前述のとおり反映されます。特に、個別に指定したファイルは、そのとおりの順序でコンパイルされます。たとえば、以下では3つのCSSファイルを結合しています。
/* ... *= require reset *= require layout *= require chrome */
2.5 プリプロセス
適用されるプリプロセスの種類は、アセットファイルの拡張子によって決まります。コントローラやscaffoldをデフォルトのgemセットで生成した場合、通常JavaScriptファイルやCSSファイルが置かれる場所にCoffeeScriptファイルとSCSSファイルがそれぞれ生成されます。先の例では、コントローラ名が"projects"で、app/assets/javascripts/projects.js.coffee
ファイルとapp/assets/stylesheets/projects.css.scss
ファイルが生成されます。
developmentモードの場合、あるいはアセットパイプラインが無効になっている場合は、これらのアセットへのリクエストはcoffee-script
gemとsass
gemが提供するプロセッサによって処理され、それぞれJavaScriptとCSSとしてブラウザへのレスポンスが送信されます。アセットパイプラインが有効になっている場合は、これらのアセットファイルはプリプロセスの対象となり、処理後のファイルがpublic/assets
ディレクトリに置かれてRailsアプリケーションまたはWebサーバーによって利用されます。
アセットファイル名に別の拡張子を追加することにより、プリプロセス時に別のレイヤを追加でリクエストすることができます。アセットファイル名の拡張子は、「右から左」の順に処理されます。従って、アセットファイル名の拡張子は、これに従って処理を行うべき順序で与える必要があります。たとえば、app/assets/stylesheets/projects.css.scss.erb
というスタイルシートでは、最初にERBとして処理され、続いてSCSS、最後にCSSとして処理されます。同様にして、 app/assets/javascripts/projects.js.coffee.erb
というJavaScriptファイルの場合では、ERB → CoffeeScript → JavaScript の順に処理されます。
このプリプロセス順序は非常に重要ですので、心に留めておいてください。たとえば、仮にapp/assets/javascripts/projects.js.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 ランタイムエラーをチェックする
アセットパイプラインはdevelopmentモードでランタイム時のエラーをデフォルトでチェックします。この動作を無効にするには、以下の設定を使用します。
config.assets.raise_runtime_errors = false
このオプションがtrueになっていると、アプリケーションのアセットがconfig.assets.precompile
に記載されているとおりにすべて読み込まれているかどうかをチェックします。config.assets.digest
もtrueになっている場合、アセットへのリクエストにダイジェストを含むことが必須となります。
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サーバーから提供されることが前提になっています。
MD5はコンパイルされるファイルの内容を元にプリコンパイル中に生成され、ファイル名に挿入されてディスクに保存されます。マニフェスト名は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_tag
とstylesheet_link_tag
から削除してください。
フィンガープリントの振る舞いについてはconfig.assets.digest
初期化オプションで制御できます。productionモードではデフォルトでtrue
、それ以外ではfalse
です。
デフォルトのconfig.assets.digest
オプションは、通常は変更しないでください。ファイル名にダイジェストが含まれないと、遠い将来にヘッダが設定されたときに (ブラウザなどの) リモートクライアントがファイルの内容変更を検出して再度取得することができなくなってしまいます。
4.1 アセットをプリコンパイルする
Railsには、パイプラインにあるアセットマニフェストなどのファイルを手動でコンパイルするためのrakeタスクが1つバンドルされています。
コンパイルされたアセットは、config.assets.prefix
で指定された場所に保存されます。この保存場所は、デフォルトでは/assets
ディレクトリです。
デプロイ時にこのrakeタスクをサーバー上で呼び出すと、コンパイル済みアセットをサーバー上で直接作成できます。ローカル環境でコンパイルする方法については次のセクションを参照してください。
以下がそのrakeタスクです。
$ RAILS_ENV=production bin/rake assets:precompile
Capistrano (v2.15.1以降) にはデプロイ中にこのrakeタスクを扱うレシピが含まれています。
Capfile
に以下を追加します。
load 'deploy/assets'
これにより、config.assets.prefix
で指定されたフォルダがshared/assets
にリンクされます。
既にこの共有フォルダを使用しているのであれば、独自のデプロイ用タスクを作成する必要があります。
このフォルダは、複数のデプロイによって共有されている点が重要です。これは、サーバー以外の離れた場所でキャッシュされているページが古いコンパイル済みアセットを参照している場合でも、キャッシュ済みページの寿命が来て削除されるまではその古いページへの参照が有効になるようにするためです。
ファイルをコンパイルする際のデフォルトのマッチャによって、app/assets
フォルダ以下のapplication.js
、application.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.rb
のprecompile
という配列を使用します。
Rails.application.config.assets.precompile += ['admin.js', 'admin.css', 'swfObject.js']
あるいは、以下のようにすべてのアセットをプリコンパイルすることもできます。
# 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で終わるファイル名 (つまりコンパイル後のファイル名として期待されているファイル名) も指定してください。
このrakeタスクは、manifest-md5hash.json
ファイルも生成します。これはすべてのアセットとそれらのフィンガープリントのリストです。Railsヘルパーはこれを使用して、マッピングリクエストがSprocketsへ戻されることを回避します。典型的なマニフェストファイルの内容は以下のような感じになっています。
{"files":{"application-723d1be6cc741a3aabb1cec24276d681.js":{"logical_path":"application.js","mtime":"2013-07-26T22:55:03-07:00","size":302506, "digest":"723d1be6cc741a3aabb1cec24276d681"},"application-12b3c7dd74d2e9df37e7cbb1efa76a6d.css":{"logical_path":"application.css","mtime":"2013-07-26T22:54:54-07:00","size":1560, "digest":"12b3c7dd74d2e9df37e7cbb1efa76a6d"},"application-1c5752789588ac18d7e1a50b1f0fd4c2.css":{"logical_path":"application.css","mtime":"2013-07-26T22:56:17-07:00","size":1591, "digest":"1c5752789588ac18d7e1a50b1f0fd4c2"},"favicon-a9c641bf2b81f0476e876f7c5e375969.ico":{"logical_path":"favicon.ico","mtime":"2013-07-26T23:00:10-07:00","size":1406, "digest":"a9c641bf2b81f0476e876f7c5e375969"},"my_image-231a680f23887d9dd70710ea5efd3c62.png":{"logical_path":"my_image.png","mtime":"2013-07-26T23:00:27-07:00","size":6646, "digest":"231a680f23887d9dd70710ea5efd3c62"}},"assets":{"application.js": "application-723d1be6cc741a3aabb1cec24276d681.js","application.css": "application-1c5752789588ac18d7e1a50b1f0fd4c2.css", "favicon.ico":"favicona9c641bf2b81f0476e876f7c5e375969.ico","my_image.png": "my_image-231a680f23887d9dd70710ea5efd3c62.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 ""; break; }
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環境のところで説明したとおりにコンパイルおよびキャッシュされます。ヘルパーで使用されるマニフェスト名にはMD5ハッシュが含まれます。
また、SprocketsはCache-Control
HTTPヘッダーをmax-age=31536000
に変更します。このヘッダーは、サーバーとクライアントブラウザの間にあるすべてのキャッシュ (プロキシなど) に対して、サーバーが提供するこのコンテンツは1年間キャッシュしてよいと通知します。これにより、そのサーバーのアセットに対するリクエスト数を減らすことができ、アセットをローカルブラウザのキャッシュやその他の中間キャッシュで代替するよい機会が与えられます。
このモードはデフォルトよりもメモリを余分に消費し、パフォーマンスも落ちるためお勧めできません。
本番アプリケーションのデプロイ先のシステムに既存のJavaScriptランタイムがない場合は、以下をGemfileに記述します。
group :production do gem 'therubyracer' 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/production.rb
のconfig.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.static_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-Control
のmax-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による圧縮は次のように行われます。ホワイトスペースとコメントを除去し、ローカル変数名を短くし、可能であればif
とelse
を三項演算子に置き換えるなどの細かな最適化を行います。
以下の設定により、JavaScriptの圧縮にuglifier
が使用されます。
config.assets.js_compressor = :uglifier
uglifier
を利用するにはExecJSをサポートするJavaScriptランタイムが必要です。Mac OS XやWindowsを使用している場合は、OSにJavaScriptランタイムをインストールしてください。
CSSやJavaScriptの圧縮を有効にするconfig.assets.compress
初期化オプションはRails 4で廃止されました。現在はこのオプションを設定しても何も変わりません。CSSおよびJavaScriptアセットの圧縮を制御するには、config.assets.css_compressor
およびconfig.assets.js_compressor
を使用します。
5.3 独自の圧縮機能を使用する
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.4 アセット のパスを変更する
デフォルトでは、Sprocketsが使用するパブリックなパスは/assets
になります。
このパスは以下のように変更可能です。
config.assets.prefix = "/他のパス"
このオプションは次のような場合に便利です。アセットパイプラインを使用しない既存のプロジェクトがあり、そのプロジェクトの既存のパスを指定したり、別途新しいリソース用のパスを指定したりする場合なのです。
5.5 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
ではありません。
6 アセットのキャッシュストア
Railsのキャッシュストアは、Sprocketsを使用してdevelopment環境とproduction環境のアセットをキャッシュを使用します。キャッシュストアの設定はconfig.assets.cache_store
で変更できます。
config.assets.cache_store = :memory_store
アセットキャッシュストアで利用できるオプションは、アプリケーションのキャッシュストアと同じです。
config.assets.cache_store = :memory_store, { size: 32.megabytes }
アセットキャッシュストアを無効にするには以下のようにします。
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/assets
、lib/assets
、vendor/assets
ディレクトリがSprocketsの検索パスに追加されます。
8 ライブラリやGemをプリプロセッサ化する
Sprocketsは異なるテンプレートエンジンへの一般的なインターフェイスとしてTiltを使用するため、gemにTiltテンプレートプロトコルのみを実装するだけで済みます。通常、TiltをTilt::Template
のようにサブクラス化してprepare
メソッドとevaluate
メソッドを再実装します。prepare
メソッドはテンプレートを初期化し、evaluate
メソッドは処理の終わったソースを返します。処理前のソースはdata
に保存されます。詳細についてはTilt::Template
のソースを参照してください。
module BangBang class Template < ::Tilt::Template def prepare # ここですべての初期化を行なう end # 元のテンプレートに"!"を追加する def evaluate(scope, locals, &block) "#{data}!" end end end
これでTemplate
クラスができましたので、続いてテンプレートファイルの拡張子との関連付けを行います。
Sprockets.register_engine '.bang', BangBang::Template
9 古いバージョンのRailsからアップグレードする
Rails 3.0やRails 2.xからのアップグレードの際には、いくつかの作業を行う必要があります。最初に、public/
ディレクトリ以下のファイルを新しい場所に移動します。ファイルの種類ごとの正しい置き場所については、アセットの編成を参照してください。
続いて、JavaScriptファイルの重複を解消します。jQueryはRails 3.1以降におけるデフォルトのJavaScriptライブラリなので、jquery.js
をapp/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( search.js )
Rails 4はSprocketsのデフォルト設定値をtest環境用のtest.rb
に設定しなくなりました。従って、test.rb
にSprocketsの設定を行なう必要があります。test環境における以前のデフォルト値は、config.assets.compile = true
、config.assets.compress = false
、config.assets.debug = false
、config.assets.digest = false
です。
以下をGemfile
に追加する必要があります。
gem 'sass-rails', "~> 3.2.3" gem 'coffee-rails', "~> 3.2.1" gem 'uglifier'