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'

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

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

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

1.1 主要な機能

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

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

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

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

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

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

アセットファイル名は内容が変わると必ず変化するので、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.enabledtrueに設定されていると、publicディレクトリ以下に置かれているあらゆるアセットはアプリケーションまたはWebサーバーによって静的なファイルとして取り扱われます。プリプロセスが必要なファイルはapp/assetsディレクトリの下に置く必要があります。

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

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

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

たとえばProjectsControllerを生成すると、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 で参照できます。

2.2 アセットの編成

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

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

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

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

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-turbo-track'オプションが利用できます。これはアセットが更新されてページに読み込まれたかどうかをTurboがチェックします。

<%= stylesheet_link_tag "application", media: "all", "data-turbo-track" => "reload" %>
<%= javascript_include_tag "application", "data-turbo-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ヘルパーを利用できるようになります。

document.getElementById('logo').src = "<%= asset_path('logo.png') %>"

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

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

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

新規作成した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されているとみなされます。

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

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

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

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

Railsは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ファイルをadmin.jsマニフェストに記載し、CSSファイルをadmin.cssマニフェストに記載できます。

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

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

2.5 プリプロセス

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

developmentモードの場合、あるいはアセットパイプラインが無効になっている場合は、これらのアセットへのリクエストはsass gemが提供するプロセッサによって処理され、通常の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/application-728742f3b9daa182fe7c831f6a3b8fa87609b4007fdc2f87c134a07b19ad93fb.js"></script>

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

sprockets-rails 3.2.0移行を使っている場合は、アセットの探索時に何も見つからなかった場合の挙動を設定できます。以下のようにunknown_asset_fallbackfalseにすると、アセットが見つからない場合にエラーをraiseします。

config.assets.unknown_asset_fallback = false

unknown_asset_fallbacktrueにすると、エラーをraiseせずにパスを出力します。アセットのフォールバック動作はデフォルトでtrueです。

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

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

config.assets.digest = false

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

3.3 ソースマップをオンにする

config/environments/development.rbに以下を記述すると、ソースマップ(Source Map)を有効にできます。

config.assets.debug = true

デバッグモードを有効にすると、Sprocketsはアセットごとにソースマップを生成します。このソースマップによって、ブラウザの開発コンソールで個別のファイルをデバッグできるようになります。

アセットは、サーバー起動後の最初のリクエストを受けてコンパイルされ、キャッシュされます。Sprocketは、以後のリクエストでコンパイルのオーバーヘッドを減らすために、Cache-Control HTTPヘッダーにmust-revalidate`を設定します。ブラウザは、これらのリクエストでHTTP 304(Not Modified)レスポンスを受け取ります。

リクエストとリクエストの間にマニフェスト内のファイルが変更されると、サーバーは新たにコンパイルしたファイルを用いてレスポンスを返します。

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" 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 )

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

このタスクによって、すべてのアセットファイルのリストとそれに対応するフィンガープリントを含む.sprockets-manifest-randomhex.jsonファイルも生成されます(randomhexは16バイトのランダムな16進文字列です)。これは、マッピングのリクエストが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.2 ローカルでプリコンパイルする

場合によっては、productionサーバーでアセットをコンパイルしたくないことがあります。たとえば、producionファイルシステムへの書き込みアクセスが制限されている場合や、アセットを変更しないデプロイが頻繁に行われる場合などが考えられます。

そのような場合は、アセットをローカルでプリコンパイルできます。つまり、production向けの最終的なコンパイル済みアセットを、production環境にデプロイする前にソースコードリポジトリに追加するということです。この方法なら、productionサーバーにデプロイするたびにproductionで別途プリコンパイルを実行する必要はありません。

以下を実行すると、production向けにプリコンパイルできます。

$ RAILS_ENV=production rails assets:precompile

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

  • プリコンパイル済みのアセットが配信可能な状態になっていると、元の(コンパイルされていない)アセットと一致していなくてもプリコンパイル済みのアセットが配信されてしまいます。これはdevelopment環境でも同じことが起きます

    developmentサーバーが常にアセット変更のたびにオンザフライでコンパイルし、常に最新のコードが反映されるようにするには、development環境ではproductionと異なるディレクトリにプリコンパイル済みアセットを保存する設定が必要です。そうしないと、production用のプリコンパイル済みアセットがdevelopment環境でのブラウザ表示に影響を与えてしまいます(つまりアセットを変更してもブラウザに反映されなくなります)。

    この設定は、config/environments/development.rbファイルに以下の行を追加することでできます。

    config.assets.prefix = "/dev-assets"
    
  • Capistranoなどの開発ツールで行われるアセットプリコンパイルを無効にしておく必要があります。

  • アセットの圧縮や最小化に必要なツールをdevelopment環境のシステムで利用可能にしておく必要があります。

4.3 動的コンパイル

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

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

config.assets.compile = true

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

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

このモードはデフォルトよりもメモリ消費が多くパフォーマンスも落ちるため、通常はおすすめできません。

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

group :production do
  gem 'mini_racer'
end

4.4 CDN

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.asset_hostを以下のように設定します。

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

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

この値は、以下のように環境変数でも設定できます。環境変数を使うと、stagingサーバー(訳注: 検証用に本番サーバーを模したサーバー)の実行が楽になります。

config.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ヘッダをデバッグする

このヘッダが正しくキャッシュされているかどうかを確認する1つの方法は、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であり、これは1年に相当します。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圧縮方法の1つです。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の圧縮オプションには、:terser:closure:uglifier:yuiのいずれかを指定できます。それぞれ、terser gem、closure-compiler gem、uglifier gem、yui-compressor gemが必要です。

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

以下の設定により、JavaScriptの圧縮にterserが使われます。

config.assets.js_compressor = :terser

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

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

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

config.assets.gzip = false # gzipアセットの生成を無効にする場合

gzip形式のアセットの配信方法については、利用しているWebサーバーのドキュメントを参照してください。

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 APIドキュメントを参照してください。

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を使います。詳しくはSprocketsのREADME「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

フィードバックについて

Railsガイドは GitHub の yasslab/railsguides.jp で管理・公開されております。本ガイドを読んで気になる文章や間違ったコードを見かけたら、気軽に Pull Request を出して頂けると嬉しいです。Pull Request の送り方については GitHub の README をご参照ください。

原著における間違いを見つけたら『Rails のドキュメントに貢献する』を参考にしながらぜひ Rails コミュニティに貢献してみてください 🛠💨✨

本ガイドの品質向上に向けて、皆さまのご協力が得られれば嬉しいです。よろしくお願いします。

YassLab 株式会社
https://yasslab.jp/

支援・協賛

Railsガイドは下記のサポーターから継続的な支援を受けています。Railsガイドへの支援・協賛にご興味あれば info@yasslab.jp までお問い合わせください。

  1. Star
  2. このエントリーをはてなブックマークに追加