アセットパイプライン
このガイドでは、アセットパイプラインについて説明します。 このガイドを読むことで、次の事が学べるはずです。
- アセットパイプラインとは何か、何が出来るのか
- アプリケーションのアセットの適切な構成について
- アセットパイプラインを使用するメリット
- パイプラインにプリプロセッサを追加する方法
- アセットをgemパッケージにする方法
- 1. アセットパイプラインとは?
- 2. アセットパイプラインの使用方法
- 3. 開発環境での使用
- 4. 本環境での使用
- 5. パイプラインのカスタマイズ
- 6. アセットのキャッシュストア
- 7. Gemにアセットを追加する方法
- 8. ライブラリまたはGemのプリプロセッサの作成
- 9. 古いバージョンのRailsからのアップグレード
1. アセットパイプラインとは?
アセットパイプラインは、javaScriptとCSSの連結・圧縮をフレームワークとつなぎ合わせる機能を提供します。 CoffeeScript、Sass、ERBのような他の言語を、これらのアセットに書き出す機能も備えています。
Railsの主機能であるアセットパイプラインによって、全ての開発者が、 主ライブラリの1つであるSprocketsの連結・圧縮によって、プリプロセッサの恩恵に預かることが出来ます。 RailsConf 2011でのDHH氏の講演で、Railsの「"fast by default"」方針の概要の一部として紹介されました。
アセットパイプラインは、デフォルトで有効になっています。
config/application.rb
の、Applicationクラス内の下記の行でこれを無効にすることが可能です。
config.assets.enabled = false
新しいアプリケーションを作る際に、--skip-sprockets
オプションを指定することで、
アセットパイプラインを無効にすることも可能です。
rails new appname --skip-sprockets
アセットパイプラインを避ける特別な理由がない限り、 新しいアプリケーションを作る際は、デフォルトで使用するようにすべきです。
1.1 主機能
パイプラインでまず挙げられる機能は、アセットの連結です。 これは、Webページをブラウザで描画する際にリクエスト数を減らすことに繋がるため、本環境下で非常に重要になります。 Webブラウザは並列で送ることの出来るリクエスト数が限られているため、 リクエスト数を減らすことは、アプリケーション読み込みの高速化に繋がります。
Rails2.xでは、javaScriptのファイル同士、CSSのファイル同士を連結する機能は、
javascript_include_tag
とstylesheet_link_tag
の最後に、
cache: true
を指定することで実現していました。
ただし、これにはいくつか制限がありました。
例えば、事前にキャッシュを作成する機能が無く、
またサードパーティ製のライブラリによって提供されるアセットを、そのまま含める事が出来ません。
Rails 3.1から、Railsはデフォルトで全てのjavaScriptファイルを1つの.js
ファイルに、
また、全てのCSSファイルを1つの.css
ファイルに連結するようになりました。
このガイドで学べば、好みに合わせてファイルのグルール化をカスタマイズ出来るようになります。
本環境下では、RailsはWebブラウザのキャッシュ機能のために、
各ファイルの名前に識別文字列となるMD5のフィンガープリントを挿入します。
このキャッシュを無効にするために、ファイル内容を編集する度にRailsは自動的にフィンガープリントを差し替えてくれます。
アセットパイプラインの第2の機能に、アセットの縮小・圧縮があります。 CSSファイルであれば、コメントと空白を削除します。 javaScriptファイルであれば、更に複雑な処理が適用されます。 オプションで構築セットから選択するか、独自のものを指定することが可能です。
アセットパイプラインの3つ目の機能に、高水準言語のコーディングを可能にし、 コンパイルによって実際に使用されるアセットにプリコンパイルしてくれます。 サポートされる言語として、CSSのためのSass、javaScriptのためのCoffeeScript、 そして両方のためにERBがデフォルトで含まれています。
1.2 何が識別文字列となり、何に気を配るべきか?
フィンガープリントは、ファイルの内容をファイル名に依存させるテクニックです。 ファイル内容が変更されると、ファイル名も同様に変更されます。 静的または滅多に更新しない内容でも、この方法であれば、異なるサーバ、異なる開発時期をまたいで、 2つの異なるバージョンのファイルの識別を容易にしてくれます。
ファイル名がその内容を元に一意である時、HTTPヘッダーは何処にあるファイル(CDN、ISP、ネットワーク、Webブラウザ内)であれ、 その内容のコピーを保持するように設定を促します。 内容が更新された際に、フィンガープリントは変更され、 これはリモートのクライアントに対し、新しい内容のコピーをリクエストすることを促すことになります。 これは一般的に、"cache busting"として知られています。
Railsはこのフィンガープリントを、通常はファイル名の末尾にハッシュ文字列を挿入することで実現しています。
例えば、global.css
というCSSファイルであれば、その内容のMD5ダイジェストで下記のように名前を変更します。
global-908e25f4bf641868d8683022a5b62f54.css
Railsアセットパイプラインによって、この方針が採択されました。
Railsの古い方針では、組み込みのヘルパーによって各アセットに日付ベースの文字列のクエリーを追記していました。 ソース内に、下記のようなコードを出力していました。
/stylesheets/global.css?1309495796
このクエリー文字列を使った方針には、いくつか不都合な点があります。
フィンガープリントは、クエリー文字列によるこうした問題を解決し、 ファイル名が各内容に対して、一貫して依存していることを保証します。
フィンガープリントはデフォルトで、本環境下で有効となり、他の環境下では無効になります。
config.assets.digest
の設定を変更することで、有効・無効を任意に変更することが可能です。
参照:
- Optimize caching - Make the Web Faster — Google Developers
- Revving Filenames: don’t use querystring | High Performance Web Sites
2. アセットパイプラインの使用方法
Railsの以前のバージョンでは、全てのアセットはimages
、javascripts
、stylesheets
のように、
public
のサブディレクトリに配置されていました。
アセットパイプラインでは、これらのアセットをapp/assets
ディレクトリ内に配置するようにしています。
このディレクトリ内のファイルは、sprocketsのGem内に含まれるSprocketsミドルウェアによって提供されます。
アセットは、まだpublic
階層内に配置することも可能です。
public
内のアセットは、アプリケーションまたはWebサーバーによって静的ファイルとして提供されます。
これらのファイルにプリプロセスを施さなければいけないのであれば、
app/assets
を使用すべきです。
本環境下では、デフォルトでRailsはこれらのファイルをpublic/assets
にプリコンパイルします。
このプリコンパイルされたコピーは、Webサーバーの静的アセットとして提供されます。
app/assets
内のファイルは、本環境下で直接提供される事はありません。
2.1 特別なアセットを制御する
スキャフォールド、またはコントローラーを生成した際に、
RailsはjavaScriptファイル(Gemfile
内でcoffee-rails
のgemが指定されていれば、CoffeeScriptファイル)と、
CSSファイルも(Gemfile
内でsass-rails
のgemが指定されていれば、SCSSファイル)を、
コントローラーのために生成します。
例えば、ProjectsController
を生成すると、
Railsは新しいapp/assets/javascripts/projects.js.coffee
ファイル、
また他にもapp/assets/stylesheets/projects.css.scss
を追加します。
デフォルトでは、これらのファイルはrequire_tree
を使用して指定された場所から使用され始めます。
require_tree
の詳細については、マニフェストファイルと指定を参照して下さい。
<%= javascript_include_tag params[:controller]>
、
<%= stylesheet_link_tag params[:controller]>
のようにして、
コントローラー毎に独自のCSS、javaScriptファイルを読み込むことも可能です。
require_tree
指定をしない場合、アセットを何度も読み込むように指定する必要があります。
アセットのプリコンパイルを使用する(本環境ではデフォルト)場合、
コントローラーのアセットが各ページで読み込まれる際に、プリコンパイルされている必要があります。
デフォルトでは、.coffee
と.scss
ファイルは、プリコンパイルされません。
開発環境下であれば、これらのファイルはその場でコンパイルされるため、正常に動作します。
ただし、本環境ではデフォルトでは、その場でのコンパイルが有効ではないため、500エラーになります。
プリコンパイルの動作については、アセットのプリコンパイルを参照してください。
CoffeeScriptを使用するためのランタイムをサポートするExecJSが必要になります。 もし、Mac OS X、またはWindowsを使用していれば、既にjavaScriptランタイムがインストールされてるはずです。 ExecJSのドキュメントで、 サポートされているjavaScriptランタイムを確認してください。
config/application.rb
に下記のコードを追加することで、
コントローラーが生成される際にアセットファイルの生成を無効にすることも可能です。
config.generators do |g|
g.assets false
end
2.2 アセットの構成
プリコンパイルのアセットは、
app/assets
、lib/assets
、vendor/assets
の3つのうちのいずれかに置くことが出来ます。
2.2.1 パスの検索
ファイルがマニフェストまたはヘルパーによって参照される際に、 Sprocketsは3つのデフォルトの場所からアセットを探します。
デフォルトでは、app/assets
のサブディレクトリにimages
、
javascripts
、stylesheets
があり、他の3つのアセットの格納場所でも同様です。
ただし、これらのサブディレクトリが特別というわけでは無く、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
を調べることで、
検索パスを確認することが可能です。
標準のassets/*
に加え、追加の(完全修飾)パスをconfig/application.rb
内で、
パイプラインに追加することが可能です。
例えば、下記のように指定します。
config.assets.paths << Rails.root.join("lib", "videoplayer", "flash")
パスは、検索パスで該当した順に参照されます。
デフォルトでは、app/assets
が優先され、
lib
とvender
に一致するパスがその後に続いて参照されることになります。
マニフェスト外で参照したいファイルは、プリコンパイルの配列に追加しなければ、 本環境下で利用出来なくなる事に注意して下さい。
2.2.2 インデックスファイルの使用
Sprocketsは、特別な目的のためにindex
(該当する拡張子を持つ)と名付けられたファイルを使用します。
例えば、もし多くのモジュールのjQueryライブラリを所有している場合、
それはlib/assets/(モジュール名)
に格納し、
lib/assets/(モジュール名)/index.js
のファイルは、
このライブラリ内で全てのファイルに対してのマニフェストを提供します。
このファイルは、必要とする全てのファイルのリスト、または単純にrequire_tree
の指定を含みます。
ライブラリ全体として、マニフェストを次のようにして、アプリケーションからアクセスさせることが可能です。
//= require library_name
これは、個別に含むファイルを指定するの代わりに、関連するコードのグループ化を行うことによって、 メンテナンスを簡素化して、クリーンな状態を維持します。
2.3 アセットへのリンクのコーディング
Sprockets
は、アセットにアクセスするための新しいメソッドを追加するような事はしません。
引き続き、javascript_include_tag
とstylesheet_link_tag
を使用します。
<%= stylesheet_link_tag "application" %>
<%= javascript_include_tag "application" %>
通常、ビューから画像には、次のようにassets/images
内の画像にアクセスします。
<%= image_tag "rails.png" %>
パイプラインがアプリケーション内(そして現在の環境下で無効になっていない)で有効になっていれば、
このファイルはSprocketsによって処理されます。
もし、ファイルがpublic/assets/rails.png
に存在すれば、
それはWebサーバによって提供されます。
代わりに、public/assets/rails-af27b6a414e6da00003503148be9b409.png
のような、
MD5ハッシュ付きのファイルにリクエストを送る場合も、同じ方法で処理されます。
ハッシュの生成については、このガイドで後述する本環境のセクションで説明します。
Sprocketsはまた、アプリケーション標準のパスとRailsエンジンによって追加されたパスを含め、
config.assets.paths
内で指定されているパスも探します。
必要であれば、画像もサブディレクトリを使った構築が可能で、 tagのメソッで内で、そのディレクトリ名を指定することで、アクセスすることが可能です。
<%= image_tag "icons/rails.png" %>
もし、アセットをプリコンパイルする際に(後述の「開発環境下」を参照)、
アセットへのリンク先に何もないと、呼び出したページ内で例外が発生します。
これには、空文字列のリンクも含まれます。
そのため、ユーザー入力のデータを元にしたimage_tag
などのヘルパーを使用する際には、十分注意してください。
2.3.1 CSSとERB
アセットパイプラインは、自動的にERBを評価します。
これは、もしerb
付きの拡張子をCSSアセットに追加して(例えば、application.css.erb
)、
asset_path
のようなヘルパーが、CSSルールの中で利用可能になることを意味します。
.class { background-image: url(<%= asset_path 'image.png' %>) }
これは、参照される特定のアセットのパスを書き出します。
この例では、画像はアセットのパスの中の画像であるとみなれされ、
app/assets/images/image.png
のようにして参照されます。
もし、この画像が既にpublic/assets内でフィンガープリントとして利用可能であれば、
パスはそちらが参照されます。
もし、data URIを使用したいのであれば、
(CSSファイル内に直接画像データを埋め込む手法)
asset_data_uri
でそれを行うことが出来ます。
#logo { background: url(<%= asset_data_uri 'logo.png' %>) }
これは、CSSソース内に正しいフォーマットでdata URIを挿入します。
Rubyの閉じタグに-%\u003E
を使用出来ないことに注意してください。
2.3.2 CSSとSass
アセットパイプラインを使用している場合、アセットへのパスは書き直す必要があり、
sass-rails
は、アセットクラスのimage、font、video、audio、javaScript、CSS用に、
-url
と-path
ヘルパー(Sassならハイフン、Rubyならアンダースコア)を提供します。
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"
になります。
2.3.3 JavaScript/CoffeeScriptとERB
もし、javaScriptアセットにerb
拡張子を追加してapplication.js.erb
のようにした場合、
javaScript内でasset_path
ヘルパーを使用することが出来ます。
$('#logo').attr({
src: "<%= asset_path('logo.png') %>"
});
これは、参照されるアセットのパスを出力します。
同様に、erb
拡張子のCoffeeScriptファイル(例:application.js.coffee.erb)でも、
asset_path
を使用することが可能です。
$('#logo').attr src: "<%= asset_path('logo.png') %>"
2.4 マニフェストファイルとディレクティブ
Sprocketsはマニフェストファイルを使用することで、読み込んで処理を行うアセットを決定します。
これらのマニフェストファイルはディレクティブを含みます。
ディレクティブはSprocketsにどのファイルが必要で、
どの順番で一つのCSSまたはjavaScriptにまとめるかを指示します。
ディレクティブによって、Sprocketsは指定されたファイルを読み込み、必要であればそれらを1つのファイルに連結、圧縮の処理を行います。
(もし、Rails.application.config.assets.compress
がtrueであれば)
多くのファイルでは無く、1つのファイルを提供することで、ブラウザのリクエスト数減らし、
ページの読み込み時間を大幅に減らすことが出来ます。
また、圧縮もファイルサイズを減らすため、ブラウザによるダウンロード時間を短縮してくれます。
例えば、新しくRailsアプリケーションを作るとデフォルトで、
app/assets/javascripts/application.js
ファイルが含まれており、
下記の行が記述されています。
// ...
//= require jquery
//= require jquery_ujs
//= require_tree .
javaScriptファイルでは、ディレクティブは//=
から始まります。
このケースでは、require
とrequire_tree
のディレクティブが使用されています。
require
ディレクティブは、Sprocketsに必要とするファイルを教える際に使用します。
ここでは、jquery.js
とjquery_ujs.js
が指定されていて、
Sprocketsのための検索パス内であれば、どこでも利用可能になります。
必ずしも、拡張子を明確にする必要はありません。
Sprocketsは、.jsファイル内での指定であれば、.js
ファイルが必要とされているとみなします。
require_tree
ディレクティブは、Sprocketsに指定したディレクトリ内から、
再帰的に全てのjavaScriptファイルを対象として、出力するように指示します。
これらのパスはマニフェストファイルと相対的に指定されなければいけません。
また、require_directory
ディレクティブを使用することも可能で、
これは再帰的にではなく、指定したディレクトリ内のjavaScriptファイルのみを対象とします。
ディレクティブは上から下へ処理されますが、require_tree
によって含まれるファイルの順番は不定です。
そのため、特定の順でファイルが読み込まれる事を、当てにするべきではありません。
もし、いくつかの特定のjavaScriptを他のグループ化されるファイルより先に読み込む必要がある場合、
マニフェストファイルの最初に、先に読み込むファイルをrequire
で指定します。
require
関連のディレクティブは、出力の際に同じファイルを繰り返し出力する事を防ぐことに注意してください。
Railsはまた、デフォルトで下記の行が含まれたapp/assets/stylesheets/application.css
のファイルを作成します。
/* ...
*= require_self
*= require_tree .
*/
javaScriptファイル内で動作するディレクティブは、CSS内でも動作します。
(javaScriptファイルというよりも、明らかにスタイルシートを含むもの)
CSSマニフェスト内のrequire_tree
ディレクティブは、javaScriptと同じように動作し、
カレントディレクトリ内の全てのスタイルシートを読み込みます。
この例では、require_self
が使用されています。
これは、require_self
が呼び出されたファイル内の、(もし、あれば)正にその場所にCSSを出力します。
もし、require_self
が複数回呼び出された場合、最後の呼び出しが適用されます。
複数のSassファイルを使用したい場合は、Sprocketsのここで紹介したディレクティブの代わりに、 通常はSassの@importルールを使用すべきです。 Sprocketsのディレクティブで全てのスコープ内のSassファイルを対象にする方法ですと、 作成される変数、ミックスインはそれが定義されたドキュメント内でしか有効になりません。
必要な分だけ、マニフェストファイルは作ることが出来ます。
例えば、admin.css
とadmin.js
マニフェストは、
アプリケーションの管理者機能で使用するjsとcssファイルを含むといった事も出来ます。
同じく順序についても補足しておきます。(翻訳に自信無し) 個々にファイルを指定することで、指定された順にコンパイルされる事が可能です。 例えば、あなたは3つのCSSファイルを下記のようにして連結させるかもしれません。
/* ...
*= require reset
*= require layout
*= require chrome
*/
2.5 プリプロセス
アセットでのファイル拡張子は、どのプリプロセスを適用するかを決定します。
デフォルトのRailsのGemsetでは、コントローラーまたはスキャフォールドは、
CoffeeScriptファイルとSCSSファイルを標準のjavaScriptとCSSファイルとして生成します。
例として、予め"projects"と呼ばれるコントローラーは、app/assets/javascripts/projects.js.coffee
と、
app/assets/stylesheets/projects.css.scss
ファイルを生成しているとします。
これらのファイルがリクエストされると、coffee-scrip
とsass
のgemによるプロセッサーにより処理され、
ブラウザにそれぞれjavaScriptとCSSとして送り返されます。
他の拡張子を追加することによって、プリプロセスを重ねて処理するように要求する事が可能で、
拡張子の右から左に処理されます。
これらは、処理を適用すべき順に並べるべきです。
例えば、app/assets/stylesheets/projects.css.scss.erb
のスタイルシートがある場合、
最初の処理はERB、次はSCSS、そして最終的にCSSが提供されます。
同様にjavaScriptファイルも、app/assets/javascripts/projects.js.coffee.erb
であれば、
ERB、CoffeeScript、の順に処理され、javaScriptとして提供されます。
これらのプリプロセスの順番は、重要なので注意してください。
例えば、app/assets/javascripts/projects.js.erb.coffee
というjavaScriptファイルがあった場合、
最初にCoffeeScriptインタプリタによって処理されますが、ERBの書式が理解出来ないため、実行した際に問題が発生します。
3. 開発環境での使用
開発環境下では、アセットはマニフェストファイルに指定された順で、別々のファイルとして提供されます。
このような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 デバッグのOFF
config/environments/development.rb
に含まれる下記の部分を更新することで、
デバッグモードをOFFにすることが出来ます。
config.assets.debug = false
デバッグモードをOFFにすると、Sprocketsは全てのファイルを連結し、必要なプリプロセスを実行します。 デバッグモードをOFFにすることで、出力されるHTMLは前述したものの代わりに下記のようなHTMLを出力します。
<script src="/assets/application.js"></script>
サーバの起動後の最初のリクエストで、アセットはコンパイルされ、キャッシュされます。
Sprocketsは、Cache-ControlのHTTPヘッダにmust-revalidate
を設定することで、
ブラウザは304(Not Modified)を受け取ることになり、後続のリクエストによるオーバーヘッドを減らします。
マニフェストに指定されている、いずれかのファイルが次のリクエストまでの間に変更されると、 サーバーは新しくコンパイルしたファイルを返します。
Railsのヘルパーメソッドで、デバッグモードを有効にすることも可能です。
<%= stylesheet_link_tag "application", debug: true %>
<%= javascript_include_tag "application", debug: true %>
デバッグモードがONになっているなら、:debug
オプションを使用するのは冗長です。
開発環境であっても動作確認のために圧縮を有効にしたい場合があるかもしれません。 必要に応じてオプションを指定することで、デバッグモードをOFFにしてこの確認を行います。(翻訳に自信なし)
4. 本環境での使用
本環境でのRailsは、上記で説明したスキームをフィンガープリントを使用して出力します。 デフォルトで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
から、削除してください。
フィンガープリントの動作は、Railsのconfig.assets.digest
設定によって制御されます。
(本環境下ではtrue
,それ以外ではfalse
です。)
特別な事情が無ければ、デフォルトのオプションは変更すべきではありません。 もし、ファイル名が変更されず、HTTPヘッダーのキャッシュの期限が長く設定されていたら、 ファイル内容が変更されても、リモートクライアントはそれを知ることが出来ません。
4.1 アセットのプリコンパイル
Railsには、マニフェストと関係するファイルをコンパイルして出力するrakeタスクの機能が備わっています。
コンパイルされるアセットは、config.assets.prefix
によって指定された場所に書きだされます。
デフォルトで、public/assets
ディレクトリが指定されています。
デプロイ時に、このタスクをサーバ上で呼び出すことで、直接コンパイル済みのアセットを作成することが出来ます。 次は、コンパイルの方法について確認していきましょう。
(本環境で)プリコンパイルを実行するrakeタスクは、下記のとおりです。
$ RAILS_ENV=production bundle exec rake assets:precompile
アセットのプリコンパイルをより早くするために、
config/application.rb
内のconfig.assets.initialize_on_precompile
を、
false
に設定することで、部分的にアプリケーションを読み込むことが可能になります。
ただし、これを行うとテンプレートはアプリケーションオブジェクト、またはメソッドを参照することが出来ません。(翻訳に自信なし)
Herokuでは、これをfalse
に設定する必要があります。
もし、config.assets.initialize_on_precompile
をfalse
に設定した場合、
デプロイ前にrake assets:precompile
を実行して問題が無いか確認してください。
アセットがアプリケーションのオブジェクトまたはメソッドを参照していると、
このフラグの値に関わらず、開発環境下であっても、
その不具合の有無を明らかにしてくれるかもしれません。
このフラグの変更は、Railsエンジンにも影響します。
エンジンは、同様にプリコンパイルのアセットを定義することが可能です。
完全な環境が読み込まれないと、エンジン(または、他のGem)は読み込まれず、アセットの欠損が起こります。
Capistrano(v2.8.0とそれ以降)では、デプロイでの、この事についての処方箋(?)が含まれています。
次の行をCapfile
に追加してください。
load 'deploy/assets'
これは、config.assets.prefix
内で指定されたフォルダをshared/assets
にリンクします。
もし、既にこの共有(schared/assets
)フォルダを使用しているのであれば、
独自のデプロイタスクを記述する必要があります。
リモートのキャッシュページは、まだキャッシュページにとっては必要な古いコンパイル済みアセットを参照するので、 このフォルダがデプロイ時に共有される事が重要になります。
もし、ローカル上でアセットをプリコンパイルしているのであれば、サーバ上でbundle install --without assets
を使用して、
アセット用のGem(Gemfile内のアセットグループのgem)のインストールを避ける事が可能です。
デフォルトのマッチャーはコンパイルするためのファイルに、pplication.js
とapplication.css
と、
全てのjs、cssでは無いファイルを含みます。(これは画像アセットを自動的に全て含めます。)(翻訳に自身なし)
[ Proc.new { |path| !%w(.js .css).include?(File.extname(path)) }, /application.(css|js)$/ ]
マッチャー(と、その他のプリコンパイル配列のメンバー:下記を参照)は、最後にコンパイルしたファイル名を適用します。
これは、JS/CSSを除いた
例えば、.coffee
と.scss
ファイルは、JS/CSSへのコンパイルに自動的には含まれません。
The matcher (and other members of the precompile array; see below) is applied to final compiled file names.
This means that anything that compiles to JS/CSS is excluded, as well as raw JS/CSS files;
for example, .coffee and .scss files are not automatically included as they compile to JS/CSS.
もし、他のマニフェストがある、または個別のCSSとjavaScriptファイルを含めるのであれば、
config/application.rb
内のprecompile
配列に、それを追加することが出来ます。
config.assets.precompile += ['admin.js', 'admin.css', 'swfObject.js']
また、下記のように全てのアセットをプリコンパイルすることを選ぶことも可能です。
# config/application.rb
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
puts "including asset: " + full_path
true
else
puts "excluding asset: " + full_path
false
end
else
false
end
end
これは、SassまたはCoffeeScriptファイルをprecompile配列に追加したいとしても、 常に最後がjsかcssで終わるコンパイル済みのファイル名を指定します。
rakeタスクはまた、全てのアセットをのリストと各フィンガープリントを含むmanifest.yml
も生成します。
典型的なマニフェストファイルは下記のようになります。
これは、Sprocketsにマッピングのリクエストを送り返すのを避けるために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
マニフェストが置かれるデフォルトの場所は、config.assets.prefix
('assets'がデフォルト)に指定されている場所のルートになります。
本環境で、もしプリコンパイル済みのファイルが見つからなければ、必要となるファイル名を示す
Sprockets::Helpers::RailsHelper::AssetPaths::AssetNotPrecompiledError
例外が発生します。
4.1.1 Far-future Expires ヘッダー
プリコンパイル済みのアセットがファイルシステム上に存在し、Webサーバーから直接それが提供されます。 これらはデフォルトでは、far-futureヘッダーが付かず、フィンガープリントのメリットを得られないので、 サーバー設定を下記を追加して、更新する必要があります。
Apacheなら、
# The Expires* directives requires the Apache module `mod_expires` to be enabled.
<Location /assets/>
# Use of ETag is discouraged when Last-Modified is present
Header unset ETag
FileETag None
# RFC says only cache for 1 year
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サーバーは通常、適度な比率の圧縮を行うように設計されていますが、 プリコンパイルが行われていれば、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は豊富な設定が可能な一方、複雑になりがちなので注意が必要です。これらの情報はGoogle検索で調べてみて下さい。 (Apacheの良い設定例があれば、このガイドを更新する手助けをお願いします)
4.2 ローカルでのプリコンパイル
ある理由で、アセットをローカル上でプリコンパイルしたいというケースがあるかもしれません。 例えば、
- 本環境下のシステムファイルへの書き込みアクセスが出来ない場合
- 複数のサーバーでデプロイの必要があり、重複する作業を避けたい場合
- アセットの変更を含まないが、頻繁にデプロイが必要な場合
ローカル上でコンパイルを行うことで、そのコンパイル済みのファイルをコミットして、 通常通りのデプロイを行うことが出来るようになります。
ただし、下記の2つの注意点があります。
- Capistranoのアセットをプリコンパイルするデプロイタスクを実行してはいけません。
- 下記2つのアプリケーション設定を変更しなければいけません。
config/environments/development.rb
内に、下記の行を追加してください。
config.assets.prefix = "/dev-assets"
また、application.rb
内にこの設定も必要です。
config.assets.initialize_on_precompile = false
prefix
の変更は、Railsに開発環境下で、アセットを提供するURLを異なるものを使用させるようにし、
全てのリクエストをSprocketsに渡します。
この変更を行わないと、アプリケーションは開発環境下でpublic/assets
からプリコンパイル済みのアセットを提供しようとし、
再びアセットをコンパイルするまで、ローカルで行った変更を確認することが出来なくなります。
initialize_on_precompile
の変更は、プリコンパイルのタスクをRailsの起動無しで実行するように指定します。
プリコンパイルのタスクは、本環境下ではデフォルトで実行し、指定したproductionデータベースへの接続を試みるからです。
ローカルでこのオプションを指定してコンパイルする際に、パイプラインのファイル内で、
Railsのリソースに依存した(データベースを使用するような)コードを書けないことに注意してください。(翻訳に自信なし)
また、任意の圧縮・連結も開発環境下で利用可能であるか確認しておく必要があります。
実際に、これでローカル上でワーキングツリーのこれらのファイルをプリコンパイルが出来るようになり、 そのファイルを必要に応じてコミットします。 開発環境は期待したとおりに動作するでしょう。
4.3 ライブコンパイル
状況によっては、ライブコンパイルを使用したいケースがあるかもしれません。 このモードは、パイプラライン内のアセットへの全てのリクエストを、Sprocketsによって直接扱われます。
有効にするには、下記のようにオプションを設定します。
config.assets.compile = true
最初のアセットへのリクエストは、上述したようにコ開発環境下では、コンパイル、キャッシュされ、 ヘルパーがマニフェスト名を使用してMD5ハッシュを含む名前に切り替えます。
Sprocketsはまた、Cache-Control
のHTTPヘッダーをmax-age=31536000
に設定します。
これは、サーバーとクライアント側のブラウザの間に、そのコンテンツ(提供されたファイル)を1年間キャッシュするように指示することになります。
これは、サーバからのアセットへのリクエスト数を減らすことに繋がり、
アセットはローカルブラウザ内のキャッシュ、またはその間に介在する何らかのキャッシュになる機会を得る事になります。
このモードは、デフォルトに比べてメモリー消費が多く、パフォーマンスが低下するため、推奨されません。
本環境のアプリケーションをjavaScriptランタイムが存在しないシステム(Linux等)にデプロイする際は、 Gemfileに描きを追加する必要があるかもしれません。
group :production do
gem 'therubyracer'
end
4.4 CDN
もし、アセットがCDNによって提供されるのであれば、開発者側がそのキャッシュについて気にする必要はなくなりますが、
これは問題を引き起こす可能性があります。
もし、config.action_controller.perform_caching = true
を使用している場合、
Rack::Cacheは、アセットの格納にRails.cache
を使用します。
これは、すぐにキャッシュ溢れを引き起こしてしまうかもしれません。
全てのキャッシュは異なるため、CDNがどのようにキャッシュを扱うのかを評価し、パイプラインで問題なく動作するか確認してください。 もしかしたら、指定した設定との関連で不都合なことがあるかもしれませんし、無いかもしれません。 例えば、nginxをデフォルトで使用する場合は、HTTPキャッシュの使用で何も問題は出ないはずです。
5. パイプラインのカスタマイズ
5.1 CSS圧縮
現在、CSS圧縮のオプションの1つにYUIがあります。 YUI Compressorは、圧縮処理を提供してくれます。
下記の行でYUI Compressorを有効にし、yui-compressor
を必要とします。
config.assets.css_compressor = :yui
config.assets.compress
は、CSS圧縮を有効にするために、true
>にしなければいけないことに注意してください。
5.2 JavaScript圧縮
javaScriptの圧縮オプションで使用可能なものに、:closure
、:uglifier
、:yui
があります。
それぞれ、closure-compiler
、uglifier
、yui-compressor
のGemが必要になります。
デフォルトで、Gemfileはuglifierを含んでいます。
このGemは、UglifyJS(Node.jsのために書かれました)をRubyでラップしています。
これを使った圧縮では、コードからホワイトスペースを除去します。
また、if
とelse
条件を可能であれば、三項演算子に書き換えるような最適化も含みます。
次の行は、javaScript圧縮のためのuglifier
を呼び出します。
config.assets.js_compressor = :uglifier
config.assets.compress
は、javaScript圧縮を有効にするために、true
にしなければいけないことに注意してください。
uglifier
を使用するために、
ExecJSがサポートしているランタイムが必要になります。
もし、Mac OSXまたは、Windowsを使用しているのであれば、OSにはjavaScriptランタイムがインストールされています。
ExecJSのドキュメントで、
サポートされているjavaScriptランタイムの情報を確認してください。
5.3 オリジナルのCompressorの使用について
CompressorのCSSとjavaScriptの設定も、任意のオブジェクトで行います。
このオブジェクトは、文字列の引数を1つ受け取って、文字列を返すcompress
メソッドを持たなければいけません。
class Transformer
def compress(string)
do_something_returning_a_string(string)
end
end
これを有効にするために、application.rb
内の設定オプションに、
new
したオブジェクトを渡してください。
config.assets.css_compressor = Transformer.new
5.4 アセットパスの変更
デフォルトでSprocketsが使用する公開パスは、/assets
です。
これをそれ以外のものに変更することが可能です。
config.assets.prefix = "/some_other_path"
このオプションは、古いプロジェクトの更新時に、例えばアセットパイプラインは使用していなかったがこのパス自体は既に使用されている、 または新しいリソースのためにそのパスを使用したいといった場合に便利です。
5.5 X-Sendfile Headers
X-Sendfileヘッダーは、アプリケーションからのレスポンスをWebサーバーに無視させるためのディレクティブで、 ディスクから指定されたファイルを代わりに提供します。 このオプションはデフォルトでOFFになっていますが、サーバーがサポートしていれば有効にすることが出来ます。 有効にすると、Webサーバーにファイルを提供する役割を渡し、それによって高速化を行います。
Apacheとnginxはこのオプションをサポートし、config/environments/production.rb
で有効にすることが出来ます。
# config.action_dispatch.x_sendfile_header = "X-Sendfile" # for apache
# config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for nginx
もし、既存のアプリケーションをアップグレードして、このオプションを使用するような場合、
このオプションの設定はproduction.rb
と本環境の振る舞いをすると定義した環境にだけ、
ペーストしてください。(application.rb
にペーストしないでください)
6. アセットのキャッシュストア
デフォルトのRailsキャッシュストアは、Sprocketsによって開発環境と本環境でアセットをキャッシュするのに使用されます。
これは、config.assets.cache_store
で変更することが可能です。
config.assets.cache_store = :memory_store
アセットのキャシュストアを受け入れるオプションは、アプリケーションのキャッシュストアの場合と同じです。(翻訳に自信なし)
config.assets.cache_store = :memory_store, { size: 32.megabytes }
7. Gemにアセットを追加する方法
アセットはまた、Gemの形式で外部のソースから提供されることもあります。
良例としてjquery-rails
のGemが挙げられ、これはRailsの標準javaScriptライブラリのGemとして提供されます。
このGemは、Rails::Engine
を継承したエンジンクラスを含みます。
これにより、このGemのディレクトリがアセットを含み、
このエンジンのapp/assets
、lib/assets
、vendor/assets
ディレクトリが、
Sprocketsの検索パスに追加された可能性があることをRailsに通知されます。
8. ライブラリまたはGemのプリプロセッサの作成
SprocketsはTiltを、
異なるテンプレートエンジンの共通のインターフェースとして使用するため、作成するGemにTiltのテンプレートプロトコルを実装すべきです。
通常は、Tilt::Template
のサブクラスにし、最終的な出力を返すevaluate
メソッドを再実装します。
テンプレートのソースは、@code
に格納されています。
より詳しく知りたければ、Tilt::Template
のソースコードを確認してください。
module BangBang
class Template < ::Tilt::Template
# Adds a "!" to original template.
def evaluate(scope, locals, &block)
"!"
end
end
end
Template
クラスの準備が出来たら、テンプレートファイルの拡張子の関連付けを行いましょう。
Sprockets.register_engine '.bang', BangBang::Template
9. 古いバージョンのRailsからのアップグレード
アップグレードする際に、少し気をつけなければいけない事があります。
まず、public/
から新しい場所へファイルを移動する必要があります。
ファイルの種類に応じて正しい場所に配置するためのガイダンスについては、
上述のアセットの構成を参照してください。
次にjavaScriptファイルの重複を避ける必要があります。
Rails3.1以上で、jQueryがデフォルトのjavaScriptライブラリになり、自動的に含まれるため、
app/assets
にjquery.js
をコピーする必要はありません。
3つ目は、様々な環境ファイルを正しいデフォルトのオプションに更新することです。 下記の変更は、バージョン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"
development.rb
です。
# Do not compress assets
config.assets.compress = false
# Expands the lines which load the assets
config.assets.debug = true
そして、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
# Precompile additional assets (application.js, application.css, and all non-JS/CSS are already added)
# config.assets.precompile += %w( search.js )
test.rb
を変更する必要はありません。
テスト環境のデフォルトは、config.assets.compile
がtrue
で、
config.assets.compress
、config.assets.debug
、
config.assets.digest
がfalse
です。
また、下記もGemfile
に追加する必要があります。
# Gems used only for assets and not required
# in production environments by default.
group :assets do
gem 'sass-rails', "~> 3.2.3"
gem 'coffee-rails', "~> 3.2.1"
gem 'uglifier'
end
もし、Bundlerでassets
のgroupを使用している場合、
config/application.rb
に、下記のBundlerのrequire
が指定されているか確認してください。
# 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)
下記は、代わりに生成されたバージョンです。
# Require the gems listed in Gemfile, including any gems
# you've limited to :test, :development, or :production.
Bundler.require(:default, Rails.env)
© 2010 - 2017 STUDIO KINGDOM