RailsのAsset Pipelineを試してみる

Aiming study#6pdfこちらのスライドを見て、そうかRailsを使わなくてもAsset Pipelineを使うのはありだなーと思ったので、試してみました。

Rails3.1のAsset Pipelineと仲良くなる – a newcomer!
ASCIIcasts – “Episode 279 – Asset Pipelineを理解する”
アセットパイプライン(Asset Pipeline)in Ruby on Rails|シフルIT
ここを読んでだいたい理解した。

つまり、アセット系のファイルをconcatして、minifyしてくれると。

つい最近書いたCoffeeScript・Sassをgruntでコンパイルするこれと同等のことをRailsのAsset Pipelineを使ってやってみました。
Rails3.1からの機能のようですが、現時点で入れると「Rails 3.2.8」が入るので大丈夫ですね。

gem install rails

application.jsファイルを見ると分かるのですが、jquery・jquery_ujs・その他のすべてのJavaScriptファイルをひとつのapplication.jsにまとめるようです。
そこでためしに、application.jsではなく、その他にもうひとつJavaScriptファイルを作るようにしてみました。

JavaScript – CoffeeScript

ファイルの配置。

assets/javascripts
├── application.js
├── foo.coffee
├── hoge.js.coffee
└── index.js

index.jsがapplication.jsと同じ役割。
中身は以下。
重要なのは、一番下の3行。

// This is a manifest file that'll be compiled into application.js, which will include all the files
// listed below.
//
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
// or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
//
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// the compiled file.
//
// WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD
// GO AFTER THE REQUIRES BELOW.
//
//= require_self
//= require hoge
//= require foo

require_selfは自分自身を指しています。
あとは、個別に2個のファイルを指定、.jsは省略可能。

CSS – SCSS

同じようにCSSファイルも。

assets/stylesheets/
├── application.css
├── index.css
└── style.css.scss

index.cssがapplication.cssと同じ役割。
中身は以下。

/*
 * This is a manifest file that'll be compiled into application.css, which will include all the files
 * listed below.
 *
 * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
 * or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.
 *
 * You're free to add application-wide styles to this file and they'll appear at the top of the
 * compiled file, but it's generally better to create a new file per style scope.
 *
 *= require style
 */

production.rbにindex.jsとindex.cssを追加する

config/environments/production.rb」に以下のように個別でコンパイルしたいファイルを追加する。

  # Precompile additional assets (application.js, application.css, and all non-JS/CSS are already added)
  config.assets.precompile += %w( index.js index.css )

コンパイルする

rake assets:precompile

上記のようにrakeすると、「public/assets」配下に出来上がる。

assets/
├── application.css
├── application.css.gz
├── application.js
├── application.js.gz
├── index.css
├── index.css.gz
├── index.js
├── index.js.gz

こんな感じになる、余計なファイルは省略しています。

gruntでコンパイルするのもいいけど、こうゆうのもありですね。

Ruby on Rails 3 ポケットリファレンス
山田 祥寛
技術評論社
売り上げランキング: 73001

CoffeeScript・Sassをgruntでコンパイルする

コンパイルしたりconcatしたりminifyしたりといたれりつくせりなgruntをためしてみました。
今後これでやっていこうと思います。

ぼくはなぜかgruntjs/grunt-contribがうまくロードできなくて、

.zshrcで

npm_dir=${NVM_PATH}_modules
export NODE_PATH=$npm_dir

とやっているのに、モジュールが見つからないと!

なので、taskは自作してそれを呼び出しています。

task

追加したタスクたち

  • coffee
  • sass
  • compass

grunt.js

/*global module:false*/
module.exports = function(grunt) {

  // Project configuration.
  grunt.initConfig({
    // ------------------------------------------------------------------------
    meta: {
      version: '0.1.0',
      banner: '/*! PROJECT_NAME - v<%= meta.version %> - ' +
        '<%= grunt.template.today("yyyy-mm-dd") %>\n' +
        '* http://PROJECT_WEBSITE/\n' +
        '* Copyright (c) <%= grunt.template.today("yyyy") %> ' +
        'hisasann; Licensed MIT */'
    },
    // ------------------------------------------------------------------------
    coffee: {
      app: {
        src : ['coffee/index.coffee'],
        dest : 'Resources/js/'
      },
      sample: {
        src : ['coffee/sample/*.coffee'],
        dest : 'coffee/sample/'
      }
    },
    // ------------------------------------------------------------------------
    compass: {
      sample: {
        src : ['scss'],
        dest : 'Resources/css'
      }
    },
    // ------------------------------------------------------------------------
    concat: {
      sample1: {
        src: ['<banner:meta.banner>', 'coffee/sample/sample1.js'],
        dest: 'coffee/sample/sample1.js'
      },
      sample2: {
        src: ['<banner:meta.banner>', 'coffee/sample/sample2.js'],
        dest: 'coffee/sample/sample2.js'
      },
      sampleall: {
        src: ['coffee/sample/*.js'],
        dest: 'coffee/sample/sample-all.js'
      }
    },
    // ------------------------------------------------------------------------
    min: {
      sample: {
        src: ['coffee/sample/sample-all.js'],
        dest: 'Resources/js/sample/sample-all.min.js'
      }
    },
    // ------------------------------------------------------------------------
    watch:{
      coffee:{
        files:['coffee/**/*.coffee'],
        tasks:'coffee concat min'
      },
      compass:{
        files:['scss/**/*.scss'],
        tasks:'compass'
      }
    },
    // ------------------------------------------------------------------------
    jshint: {
      options: {
        curly: true,
        eqeqeq: true,
        immed: true,
        latedef: true,
        newcap: true,
        noarg: true,
        sub: true,
        undef: true,
        boss: true,
        eqnull: true,
        browser: true
      },
      globals: {}
    },
    uglify: {}
  });

  // Default task.
  grunt.registerTask('default', 'coffee compass concat min');

  // --------------------------------------------------------------------------
  //
  // register custom tasks and helpers.
  //

  var log = grunt.log;
  var exec = require('child_process').exec;

  grunt.registerHelper('exec', function (opts, done) {
    var command = opts.cmd + ' ' + opts.args.join(' ');
    exec(command, opts.opts, function (code, stdout, stderr) {
      if (!done) return;
      if (code === 0) {
        done(null, stdout, code);
      } else {
        done(code, stderr, code);
      }
    });
  });

  var handleResult = function handleResult(err, stdout, code, done) {
    if (err) {
      log.writeln(stdout);
      done(false);
    } else {
      log.writeln('complete!');
      done(true);
    }
  };

  // task: coffee
  (function (grunt) {
    grunt.registerHelper('coffeec', function (fromdir, dest, done) {
      var args = { cmd:'coffee', args:[ '--compile', '--output', dest, fromdir ] };
      grunt.helper('exec', args, function (err, stdout, code) {
        handleResult(err, stdout, code, done);
      });
    });

    grunt.registerMultiTask('coffee', 'compile CoffeeScript', function () {
      grunt.helper('coffeec', this.data.src, this.data.dest, this.async());
    });
  }(grunt));

  // task: sass
  (function (grunt) {
    grunt.registerHelper('sassc', function (from, dest, done) {
      var args = { cmd:'sass', args:[ from + ':' + dest] };
      grunt.helper('exec', args, function (err, stdout, code) {
        handleResult(err, stdout, code, done);
      });
    });

    grunt.registerMultiTask('sass', 'compile sass', function () {
      grunt.helper('sassc', this.data.src, this.data.dest, this.async());
    });
  }(grunt));

  // task: compass
  // gem install compass
  (function (grunt) {
    grunt.registerHelper('compassc', function (fromdir, destdir , done) {
      var args = { cmd:'compass compile', args:[ '--sass-dir ' + fromdir + ' --css-dir ' + destdir + ' --boring' ] };
      grunt.helper('exec', args, function (err, stdout, code) {
        handleResult(err, stdout, code, done);
      });
    });

    grunt.registerMultiTask('compass', 'compile sass', function () {
      grunt.helper('compassc', this.data.src, this.data.dest, this.async());
    });
  }(grunt));

};

gist

CoffeeScript・Sassをgruntでコンパイルする — Gist

これで、CoffeeもSassも楽しくいじれる!

watchしたかったら

grunt watch

いいね!

一応githubに全体をpushしておきました
hisasann/CoffeeSass-GruntCompile

追記:2012/12/21 17:42:24
grunt.loadNpmTasks(‘grunt-compass’);を使うほうに修正。

参考リンク

実践JS サーバサイド JavaScript 入門
井上 誠一郎
技術評論社
売り上げランキング: 42782