GradleでAsset Pipelineを使おう

この記事は[G*Advent Calendar(Groovy,Grails,Gradle,Spock...) Advent Calendar 2014 - Qiita](http://qiita.com/advent-calendar/2014/gastah)の18日目です。

Asset PipelineはRuby on Railsによって生み出された仕組みです。Grailsでは、このRailsの仕組みを参考にしたAsset Pipelineプラグインが提供されています。

このAsset Pipelineプラグインは、Javascript、スタイルシート、画像といった静的なリソースを管理するプラグインです。アセットとは日本語で「資産」という意味で、Javascript、スタイルシート、画像といった静的なリソースを指しています。

静的リソースを管理するプラグインは、Grails 2.3まではResourcesプラグインがデフォルトのプラグインでしたが、Grails 2.4からはこのAsset Pipelineがデフォルトのプラグインになっています。

# Asset Pipelineの機能

このプラグインが提供する主な機能は次のようなものがあります。

* アセットの結合、最小化、圧縮
* フィンガープリント
* CoffeeScriptやSASSといったアセットのコンパイル

アセットの結合は、複数のJavascriptやスタイルシートのファイルを、1つのJavascript、スタイルシートのファイルにまとめる機能です。ファイルを1つにまとめることで、リソースを取得するためのHTTPリクエストの数が減るため、不要なオーバーヘッドが削減され、Webアプリケーションのフロントエンドのパフォーマンス向上に繋がります。またアセットの最小化と圧縮によって、さらにファイルサイズを小さくできます。

フィンガープリンティングはファイル内容に応じて、自動的にファイル名にフィンガープリントを付与してくれる仕組みです。フィンガープリントはファイル内容が異なれば、異なるフィンガープリントが付与されます。Asset Pipelineでは、フィンガープリントはファイル内容のハッシュ値です。このハッシュ値はファイル名の末尾に挿入されます。たとえば、application-5dca4dc3f1f721f16daeb7ba450f1500.jsといったような形です。

フィンガープリンティングはキャシュバスティング (Cache Busting) またはキャッシュバスター (Cache Buster) と呼ばれるブラウザにキャッシュしたリソースを使わせないようにするための仕組みです。たとえばアプリケーションを再デプロイしたときに、リソースが更新されているにもかかわらず、更新したリソースのファイル名が更新前と同じファイル名の場合は、ブラウザが新しいリソースではなくキャッシュされたデータを使ってしまい不具合が起きてしまいます。フィンガープリンティングを使うと、ファイル内容が変われば自動的に別のファイル名になるため、ブラウザは別のファイルとして認識してキャシュしたデータを使うことなく、新たにリソースを取得しにいきます。

# Gradleで使う

ここからが本題です。

Grailsのプラグインとして提供されていたAsset Pipelineプラグインですが、2.0からコア機能がasset-pipeline-coreとしてプラグインから独立した形で提供されるようになりました。これによりGrailsに依存することなく、様々なJVMプロジェクトで使用可能になっています。

それでは早速Gradleで使ってみましょう。

### build.gradleの準備

`build.gradle`を以下のように書きます（最新のバージョンやオプションは[本家のドキュメント](https://github.com/bertramdev/asset-pipeline-core)を参照してください）。

```
buildscript {
  repositories {
    mavenCentral()
  }
  dependencies {
    classpath 'com.bertramlabs.plugins:asset-pipeline-gradle:2.0.19'
  }
}

apply plugin: 'asset-pipeline'

assets {
  minifyJs = true
  minifyCss = true
}
```

### アセットの格納

アセットは`assets/javascripts`、`assets/stylesheets`、`assets/images`ディレクトリに格納します。名前の通り`images`には画像ファイル、`javascript`にはスクリプトファイル、`stylesheets`にはスタイルシートといったアセットを配置します。必要に応じて`javascripts/module/my.js`といったように、更にディレクトリ階層を作ることもできます。

`images`、`javascripts`、`stylesheets`といったassetsディレクトリ直下のサブディレクトリは特別な意味を持っています。それは、Asset Piplineがアセットを処理したあとは、これらのサブディレクトリは存在しなくなるということです。これらのサブディレクトリは、同じ種類のファイルを管理しやすいようにする目的で存在しています。

例えば`assets/javascript/my.js`と`assets/stylesheets/my.css`といったファイルがあったとします。これら`my.js`、`my.css`はAsset Piplineがリソースを処理したあとは同じディレクトリ中に格納されます。

また、assetsディレクトリ配下に、独自のサブディレクトリも作成することもできます。この場合も他のサブディレクトリと同じように振る舞います。


### マニフェストとディレクティブ

Asset Pipelineではマニフェストとディレクトリという仕組みが用意されています。マニフェストは複数のリソースを纏めるための定義で、ディレクティブはそのマニュフェストの中でリソースを指定するための記法です。

マニフェストはassets/javascripts、assets/stylesheets配下に格納するリソースファイルのコメントとして記述します。コメント形式はリソースごとに異なります。たとえば、JavaScriptの場合は次のように記述できます。

```
// JavaScriptのマニュフェスト定義には//=を使います。
//
//= require jquery
//= require app/models.js
//= require_self
//= require_tree views

console.log("This is application.js");
```

スタイルシートの場合は次のように記述できます。

```
/* スタイルシートのマニュフェスト定義には*=を使います。
*
*= require_self
*= require header
*= require_tree .
*/

body {
    background: #fff;
}
```

マニフェストファイルは、マニフェストファイルであると共にリソースファイルでもあります。そのため、JavaScirptのファイルであればJavaScriptを記述でき、スタイルシートであればスタイルがそのファイルの中で定義できます。

マニフェストの中で`require`、`require_tree`、`require_self`といったキーワードがディレクティブです。このディレクティブを指定することで、外部のリソースファイルをこのファイルに読み込めます。

ディレクティブには次の種類が存在します。

* `require`
    - 指定されたリソースを読み込みます。
      リソースの拡張子は省略することもできます。
* `require_tree`
    - 指定されたパスのリソースを再帰的に読み込みます。
      自身のアプリケーションのリソースだけが対象になり、プラグインのリソースは対象になりません。
* `require_self`
    - 自身のリソースを読み込みます。
      省略した場合は自動的に最後に読み込まれます。
* `require_full_tree`
    - 指定されたパスのリソース再帰的に読み込みます。
      require_treeと異なり、アプリケーションのリソースだけでなくプラグインのリソースも対象になります。
* `encoding`
    - 処理するファイルのエンコーディングを指定します。

リソースの拡張子は省略できます。たとえばJavaScriptの場合、`require jquery`は`require jquery.js`と記述したのと同じです。

### コンパイルしてみよう

Gradleでアセットをコンパイルするには`assetCompile`タスクを実行しまう。

```
gradle assetCompile
```

コンパイルが成功すると`build/assets`配下にコンパイル結果が生成されます。

# まとめ

ここまでGradleでの使い方をみてきましたが、実はGrails 3からビルド環境がGradleで一新される関係で、同じようにGradleのプラグインでアセットが処理されるようになります(Grailsプラグインの依存は別途必要ですが)。また、コア機能が独立したことにより他のJVMアプリケーションからも使えるようなった関係でSpring Bootから使用できるサポートなんかも入っています。Spring BootではAsset Pipelineの機能を提供しないなんて話もあるので、このプラグイン使うのもひとつの手ではないでしょうか。

ではでは。