Rails Sprocketsとのお別れの仕方 - 最初の一歩 -

2017.02.21

こんにちは!
SmartHRのエンジニア、溝上といいます。

今回は通常の業務と合わせて行っている開発環境の改善、その一部である Sprockets 絶ちを紹介していけたらと思います。

現在進行中のため、不定期な連載になる予定です!

Sprockets とは?

Rails 3.1 から導入された gem で、アセットファイル(JS, CSS, 画像など)を効率的に管理するための Asset Pipeline という仕組みの基盤です。

主な機能は以下のような物があります。

  • アセットファイルのパス管理
  • アセットファイルの結合・圧縮
  • アセットファイルの依存関係の解決
  • アセットファイルのコンパイル
  • 最終的に出力するファイルに hash を付けてキャッシュを無効化

などなど。

なぜ Sprockets と別れたいのか?

この記事を読んでくださっている方ならそれぞれの理由があるかと思います。
SmartHR では以下のような理由がありました。

  • CoffeeScript 以外の AltJS を使いたい
  • gem で JS のライブラリを管理したくない

どうでしょう。
個人的にお別れするには十分な理由ですね。

最初の一歩はどこから?

そんなわけで Sprockets とお別れする事になったのですが、一発でさよなら!とは行きません。
段階を踏んで距離を置いていきたいわけです。

さて、どこからやるべきか….

前提となる SmartHR の状態

  • 既に大量の CoffeeScript、 SCSSなどのアセットが存在する
  • gem でフロントエンドのライブラリが管理されている
  • フロントエンドのテストが不足
  • 通常業務と並行して移行していきたい
  • etc…

これらの状況を考慮し、 SmartHR の場合、 SCSS から着手しました。
コンパイル方法などに気をつければ Sprockets での出力との比較が取りやすく、また分離させやすい箇所だったからです。
またちゃんと計測は行っていませんが Sprockets での SCSS のコンパイルは遅い、との噂もありそれも一因でした。

修正の方針

  • 最終的な目標はフロントエンドに関連する全てのファイルを Sprockets の管理下から外す
  • gulp で各ファイルを監視し、コンパイルなどを行う
  • frontend ディレクトリを作成して、フロント関連のファイルを移動していく
  • 一旦 asset:precompile と併用する
    • gulp で assets に対してコンパイルしたファイルを書き出す

上記のようなざっくりとした方針を決定し、移行に着手しました。

gulpfile.js

var gulp = require("gulp"),
    plumber = require("gulp-plumber"),
    concat = require("gulp-concat"),
    sass = require("gulp-sass");

gulp.task('scss', function() {
  gulp.src([
    './frontend/assets/source/scss/**.*',
    './frontend/assets/source/scss/mobile.scss'
  ])
    .pipe(plumber())
    .pipe(concat('bundle.css'))
    .pipe(sass())
    .pipe(gulp.dest('./app/assets/stylesheets/'))
});

よし、さっくり行けたしコンパイル速度も上がったかな?

と思ったのですが…

画像のように WebFont が読み込まれず表示崩れが…
これは asset_path を SCSS 中で利用しているため、 gulp-sass ではコンパイルが正常に行えなかったためです。

@font-face {
...
  src: font-url("~~~");
...
}

修正するには

解決策を調べたところ、拡張子を erb に変換 & asset_path に置換するという方法を見つけましたが、
SmartHR の場合、利用している WebFont や画像は比較的小さかったため、
base64エンコードして css に埋め込んでしまう方法を取りました。

var gulp = require("gulp"),
    plumber = require("gulp-plumber"),
    concat = require("gulp-concat"),
    sass = require("gulp-sass"),
    base64 = require("gulp-base64");

gulp.task('scss', function() {
  gulp.src([
    './frontend/assets/source/scss/**.*',
    './frontend/assets/source/scss/mobile.scss'
  ])
    .pipe(plumber())
    .pipe(concat('bundle.css'))
    .pipe(sass())
    .pipe(base64())
    .pipe(gulp.dest('./app/assets/stylesheets/'))
});
@font-face {
...
  src: url("~~~");
...
}

この方法で無事解決しました。

次回

Babel & ESLintの導入時のことをお伝えしようと思います!(時期未定)

atsushim

フロントエンドエンジニアとしてキャリアを積む中でハッカソンやイベント登壇なども行い、この頃に現在のKUFUのメンバー達と出会う。前職ではドワンゴにてバックエンドでモバイル端末向けWebAPIの運用・開発を担当。2016年に株式会社KUFUに参加。管理ツール・社内ツールの作成などが好きで、vimのプラグインを書いたりPebbleで遊んだりしている。
他の執筆記事はこちら