monolithic kernel

SinatraアプリでもAsset Pipelineを使う

February 05, 2012

    Rails 3.1で追加されたAsset Pipelineという超便利な機能をSinatraでも使ってみます。

    # Railsを使ったことはないですが。

    Asset Pipelineとは

    Asset Pipelineとは何か、ざっくり言うとJavaScript/CSSの依存関係を解決したり、複数ファイルを1つにまとめてリクエストを減らしたり、ついでに圧縮したりできる機能です。詳しくは以下のページが参考になると思います。

    Asset Pipelineはsprocketsというgemを使って実現されており、Rails以外のRackアプリでも利用することができます。この記事では、Sinatraアプリでsprocketsを使ってみた例を紹介します。

    利用方法

    Sinatraからsprocketsを利用したサンプルを用意しました。

    bundle installしてrackupすれば動作すると思います。が、私はpowでしか試していません。

    $ git clone https://github.com/mono0x/sinatra-easy-template.git
    $ cd sinatra-easy-template
    $ bundle install
    $ rackup

    Gemfile

    必要なのはsprocketsだけですが、sprockets-helpersも入れておくと便利です。yui-compressorはコードを圧縮する場合に必要です。

    gem 'sprockets'
    gem 'sprockets-helpers'
    gem 'yui-compressor', require: 'yui/compressor'

    Sass(とCompass)を利用する場合は以下のgemを追加します。

    gem 'sass'
    gem 'compass'
    gem 'sprockets-sass'

    CoffeeScriptを利用する場合は以下のgemを追加します。

    gem 'coffee-script'

    application.rb

    sprocketsに関する設定を行います。

    set :sprockets, Sprockets::Environment.new
    configure do
      Sprockets::Helpers.configure do |config|
        config.environment = sprockets
        config.prefix = '/assets'

    digestをtrueにすると、生成されるファイル名にファイルのハッシュ値が含まれるようになります。ファイルの中身が変わるたびにファイル名が変わるようになるので、ブラウザのキャッシュが読み込まれてしまってうまく表示できないトラブルを避けることができます。

        config.digest = true
      end

    ファイルを置く場所のパスを指定します。

      sprockets.append_path 'assets/javascripts'
      sprockets.append_path 'assets/stylesheets'

    jscompressorとcsscompressorにコードの圧縮を行うオブジェクトを指定できます。ここでは、production環境でのみYUI Compressorを利用するよう指定しています。

      if production?
        sprockets.js_compressor = YUI::JavaScriptCompressor.new(munge: true, optimize: true)
        sprockets.css_compressor = YUI::CssCompressor.new
      end
    end

    sprockets-helpersのメソッドを利用できるようにしておきます。

    helpers Sprockets::Helpers

    config.ru

    /(root)はApplicationで、/assets以下はsprocketsで処理するようにします。

    map '/assets' do
      run Application.sprockets
    end
    map '/' do
      run Application
    end

    assets/stylesheets/application.css.scss

    application.rbで指定したassets/stylesheetsディレクトリにCSSを配置します。.css.scssのように.cssの後ろにscss/sass/lessなどをつけておくと、それぞれの言語で記述したものとして認識されます。

    sprocketsでは、ファイルの先頭に特別なディレクティブを記述すると、それを認識して動作します。以下の例では、requireディレクティブでlayout.css(.scss)を結合したあと、requiretreeディレクティブでassets/stylesheets以下のすべてのスタイルシートを結合して1つのファイルにまとめています。requireディレクティブは結合する順序が重要な場合にのみ利用するもので、基本的にはrequiretreeディレクティブでごっそり結合しておけばよいと思います。なお、結合は一度のみしか行われないため、最後のrequire_treeディレクティブでは先にrequireしたlayoutは結合されません。

    //= require layout
    //= require_tree .

    views/layout.haml

    stylesheet_pathメソッドにcssの名前を渡すことでパスを取得できます。cssがどんな形式で書かれているかを気にする必要はなく、名前のみを指定すればOKです。

    %link{rel: 'stylesheet', type: 'text/css', href: stylesheet_path('application')}

    実行結果

    /index.html

    ハッシュ値を含んだファイル名が生成されていることがわかります。

    <link href="/assets/application-92ad097f0f535b5601a1b8a69fe89ffc.css" rel="stylesheet" type="text/css">ylesheet" type="text/css">

    /assets/application-92ad097f0f535b5601a1b8a69fe89ffc.css

    YUI Compressorで圧縮されたCSSが生成されています。

    section,article,aside,header,footer,nav{display:block}#container{min-width:950px;width:80%;margin:0 auto;overflow:hidden;*zoom:1}#container>header{display:inline;float:left;overflow:hidden;width:99.974%;margin-right:1.042%}#container>section{display:inline;float:left;overflow:hidden;width:66.302%;margin-right:1.042%}#container>aside{display:inline;float:left;overflow:hidden;width:32.63%;margin-right:0}

    さらに、以下のようなHTTPヘッダが付加されてキャッシュされるようになりました。

    Cache-Control:public, max-age=3153600

    つかってみた感想など

    デフォルトでAsset Pipelineが使えるRailsと比べると準備が若干面倒ですが、設定さえしてしまえば便利だと感じました。

    # ただ、Sinatraにいろいろ自分で足して使っていると、いい加減Railsを使った方がいいんじゃないかという気がしないでもないです。Padrinoなんてのもあるみたいですけど、それならメジャーなRailsにしたほうが無難かなと思いますし……。