Sprockets memo

rails の asset pipeline で使われている Sprockets の https://github.com/sstephenson/sprockets を読みながら各章毎にメモ。(=ちゃんとした翻訳ではないです)

ざーっと書いていきます。入門文書ではないかも。

ちなみに、Sprockets では複数の javascript や stylesheet を require や include で一つにまとめる事が出来るけれど、そのまとめる処理ないしまとめた結果を Bundle と読んでいる様子。以下の記述に於いてお同じ。

概要

assets をコンパイルしたり送出するための Ruby ライブラリ。

Sprockets Environemnt の理解

Sprockets::Environemnt は Rack アプリケーションで、受け取った設定値に従って assets (js や css その他)をHTTP 経由でレスポンスするためのオブジェクト。Rack アプリなので特定のパスにマウント可能。

Sprockets::Environment のインスタンスには、Rails 3.1 以降では YourApp::Application.assets でアクセスできる。これは設定済みの Sprockets::Environemnt のインスタンス。Rack ベースのアプリケーションでは config.ru で作成する。

ロードパス

Load Path は Sprockets が assets を検索するためのディレクトリのリスト。複数のディレクトリを指定すると、assets の検索にそれらのディレクトリが使われる。このディレクトリはアプリケーション外のものであっても良い。これらのディレクトリ一つの仮想ディレクトリにまとめられたようになる(例えば rails だとHTTP経由で assets にアクセスする場合、デフォルトでは /assets 以下に JavaScript ファイルも CSS ファイルもまとめられたように見える)。

Load Path の変更

appned_path や prepend_path を使う。ディレクトリのリストの順番には意味があり、assets の検索順序に影響する(最初のほうにあるディレクトリが先に検索される)

Assets へのアクセス

load_path を設定すると environemnt (Sprockets::Environemnt のインスタンスのことだと思う)は Rack アプリケーションとしてマウントでき、HTTP 経由でリクエストできる。また、自身のアプリケーションから programmatically にアクセスできる。

論理パス

論理パスは asset のソースファイルに対する、load path からの相対パス。例えば load path が app/assets/javascripts だと以下のようになる

asset source file 論理パス
app/assets/javascripts/application.js application.js
app/assets/javascripts/models/project.js models/project.js

load path に登録されたディレクトリは全て仮想ファイルシステムのようにマージされた扱いとなり、論理パスを指定した場合には絵全ての load path が走査されるようだ。

HTTP での Assets の送信

environemnt をマウントすると、全ての assets には、マウントポイント以下の論理パス経由でアクセス出来る。 envirnemnt を /assets にマウントして /assets/application.js にアクセスすると、Sprockets は load path を走査して application.js を見つけ、送信する。

Rails 3.1 以降では environemnt は自動的に /assets に舞うtのされる。Rack アプリケーションと共に Sprockets を使う場合、自身でマウントする必要がある。config.ru に対して以下のようにすると良い(らしい)

require 'sprockets'
map '/assets' do
  environment = Sprockets::Environment.new
  environment.append_path 'app/assets/javascripts'
  environment.append_path 'app/assets/stylesheets'
  run environment
end

map '/' do
  run YourRackApp
end
Programmatically に assets にアクセスする

Sprockets environment から asset を受け取るには、find_asset メソッドを利用する(alias は [])。このメソッドに論理パスを和つぃてやると Sprockets::BundleAsset のインスタンスが得られる。

これ、Rails だと YourApp::Application.assets.find_asset ですね。

得られた Sprockets::BundleAsset のインスタンスに to_s をコールするとその中身が得られ、length では byte のサイズが得られ、mtime では最終更新時刻が得られ、pathname ではファイルシステム上の絶対パスが得られる(やってみたら Pathname のインスタンスでした)。

Engines の利用

assets は SCSS や CoffeeScript 等の別の言語で記述することができる。これらは Sprockets によって自動的に CSSJavaScriptコンパイルされる。これら別言語のコンパイラは engines と呼ぶ。

Engines は asset ソースファイル名に追加した拡張子で指定する。例えば SCSS エンジンを利用した CSS なら layout.css.scss となる。CoffeeScript エンジンを利用した JavaScript なら dialog.js.coffee など。

Sass と SCSS によるスタイリング

Sass は CSSコンパイルされる言語で、幾つかのフィーチャーを追加する。ネスト、 変数、ミックスイン、セレクタの継承など。

sass の gem が有効であれば CSS を記述するために Sass が使える。

Sprockets は Sass の記法も SCSS の記法も利用できる。 アセットとして利用する際には、Sass ならば拡張子に .css.sass を利用し、SCSS の場合には .css.scss とする。

LESS によるスタイリング

LESS は 変数、ミックスイン、オペレーション(?)、関数などの動的な振る舞いを CSS に対して拡張する。

less gem が有効であれば利用できる。注意として、LESS コンパイラJavaScript で記述されており、less の gem は therubyracher に依存しており、、これは V8 JavaScript ランタイムを Ruby に付与する。

CSS assets を LESS で記述する場合、拡張子として .css.less とする。

CoffeeeScript によるスクリプティング

CoofeeScript は JavaScript の "good parts" にコンパイルされる言語で、配列の comprehensions (?)、クラス、関数バインディングなどのよ り綺麗なシンタクスを持つ。

coffee-script gem が有効な場合に利用できる。注意として、CoffeeScript コンパイラJavaScript で記載されており、ExecJS でサポートされたランタイムが利用するのに必要となる。

JavaScript assets を CoffeeScript で記述する場合、拡張子を .js.coffee とする。

EJS と Eco による JavaScript のテンプレート化

Sprockets はクライアントサイドでの文字列やマークアップレンダリングのための JavaScript テンプレートをサポートする。JavaScript テンプレートは特別な拡張子である .jst を利用し、これらは JavaScript 関数にコンパイルされる。

ロードされると JavaScript テンプレート関数はその論理パスをプロパティのキーとして持つグローバルな JST オブジェクトによってアクセスできる。テンプレートを文字列としてレンダリングするためにテンプレート関数を呼び出すことが出来る。結果の文字列はDOMに挿入することができる。

<!-- templates/hello.jst.ejs -->
<div>Hello, <span><%= name %></span>!/div>

// application.js
//= require templates/hello
$("#hello").html(JST["templates/hello"]({ name: "Sam" }));

Sprockets は2つの JavaScript テンプレート言語を利用できる。JavaScript のための EJS、エンベデッド CoffeeScript のための Eco。どちらの言語もロジックを埋め込む為に <% ... %> に類する記法を利用する。

ejs gem が有効であれば Sprockets で EJS を利用できる。利用する場合の拡張子は .jst.ejs。

eco gem が有効であれば Eco Template を Sprockets で利用できる。利用する場合の拡張子は .jst.eco。注意として、eco gem は CoffeeScript コンパイラに依存する。このため上記に記述した CoffeeScript と同様の注意事項を承諾する必要がある。

ERB による Ruby の呼び出し

Sprockets は ERB エンジンによるプリプロセッシングを提供する。CSSJavaScript の assets のファイル名に .erb を付与すると ERB エンジンが有効化される。

注意:Sprockets は拡張子による複数のエンジン処理を右から左に向けて行う。例えば、CoffeeScript による asset を最初に ERB で処理したい場合、拡張子は .js.coffee.erb となる。

assets に埋め込まれた Ruby コードは Sprockets::Context のインスタンスのコンテキストで評価される。ERB埋め込みの主な用途は以下のようなものを含む:

  • Base64エンコードした別の asset を data: URI で埋め込む(asset_data_uri ヘルパを利用できる
  • Sprockets Rails pluginで提供される asset_path を利用して他の asset の URL を埋め込む。
  • その他のアプリケーションリソースを埋め込む。例えば JSON を利用してローカライズされた文字列データを表現した JavaScript asset など
  • 別ファイルからロードしたバージョン定数を埋め込む

Sprockets::Context での ERB でできるより詳しい情報は Helper Methods セクションを参照のこと(と、書いてあるけどそのセクションはないみたい…

文字列挿入のシンタクス

もし asset 内で Ruby にアクセスしたくても ERB の <% ... %> シンタクスが利用できない場合、Sprockets は Ruby での #{ ... } シンタクスも利用できる。これは .str 拡張子を付与することで行える。

依存性の管理とバンドリング

The Directive Processor

  • コメント直後の = に続く単語は directive として扱われる
  • directive の後に続く語は arguments として扱われる

Sprockets Directives

asset の依存性を記述するために下記のディレクティブが利用できる。

ディレクティブには論理パスか相対パスを指定する。相対パスは ./ から始まる形式で 指定する。これは現在のファイルからの相対パスとして扱われる。

require ディレクティブ

require {path} は指定された path の asset ソースファイルのコンテンツを Bundle に挿入する。複数回 同じファイルが require されても挿入するのは1度だけ。

include ディレクティブ

include {path} は require と同じように働く。但し 以前に require/include されていようとも同じだけコンテンツを挿入する。

require_directory ディレクティブ

require_directory {path} は path で指定したディレクトリで、同じフォーマットの ファイルを全て require する。require の順序はアルファベット順。

require_tree ディレクティブ

require_tree {path} は require_directory と似た働きをするが、path で指定された ディレクトリのサブディレクトリ以下のファイルについても再帰的に全て requireする。

require_self ディレクティブ

require_self はカレントのファイルの内容を挿入する位置を指定する。(デフォルトでは各 require や include の後に自身の内容を Bundle に挿入する。)

depend_on ディレクティブ

depend_on {path} は path の asset を挿入することなく依存性を定義する。他のファイルの変更に応じて asset のキャッシュを期限切れにしたい場合有用。
(例えば depends_on 'hoge.txt' とすると hoge.txt が更新されるとこれまでの Bundle は有効期限切れとなり再作成されるみたい。多分 rails 環境で precompile しており config.assets.compile が false だったりすると意味ないと思うけど未検証。他のディレクティブと違い、path は拡張子まで書かないと上手く動かなかった)

stub ディレクティブ

stub {path} は依存性を除外することを可能にする。 path は有効な asset である必要があり、既にバンドルされているかもしれないし、されていないかもしれない。一度 stub されると、ブラックリストに登録され、他の require 等で戻す事はできない。Sprockets 2.2 から有効ぽいが、Rails 3.2.3 ではまだ使えないかな?