こんにちは、キャスレーコンサルティングSI部の清水です。

Railsでは、Rails 3.1よりクライアントサイド開発の言語としてCoffeeScript/SCSSを標準サポートしています。
CoffeeScript/SCSSは簡単に言うとJavaScript/CSSをより便利に、シンプルにするための言語で、
必ずしも使用しなければならないということはありませんが、生産性の高さから考えるとぜひ押さえておきたい言語です。
今回はAsset Pipeという機能を活用し、簡単なCoffeeScriptを試してみたいと思います。

Sprocketsについて

まずは、JavaScriptやスタイルシートなどのアセット(※)をページにインポートする方法からお話しします。
※:アセットとはJavaScript、スタイルシート、画像などアプリケーションで扱うリソースの一式の総称。

Railsではアセットを読み込むためにSprocketsというライブラリを利用します。
Sprocketsでは、あらかじめ用意されたマニフェストに従いアセットを読み込みます。
マニフェストファイルはデフォルトで以下が用意されています。

  • /app/assets/javascripts/application.js
  • /app/assets/stykesheets/application.css

application.js

~中略
//= require jquery
//= require jquery_ujs
//= require turbolinks
//= require_tree .

application.css

~中略
 *= require_self
 *= require_tree .

「require_tree」は、指定されたフォルダpathの配下を再帰的にインクルードする意味があり、両ファイルにある「require_tree .」はカレントフォルダ配下をすべて読み込む読み込みなさいという意味になります。
従って、javascriptファイルであれば「/app/assets/javascripts/」に、スタイルシートであれば「/app/assets/stykesheets/」にファイルを配置すればSprocketsによって自動読み込みされることとなります。

Asset Pipelineについて

Asset Pipelineとは、Sprocketsライブラリによって提供される仕組みで、アセットを管理し、より効率的に返す機能を提供します。
次にAsset Pipelineによる処理の流れを説明し、特徴(機能)を挙げていきます。

① CoffeeScript/SCSSをコンパイル

CoffeeScript/SCSSはそのままでは一般的なブラウザでは使用できず、コンパイルする必要があります。
Asset PipelineではCoffeeScript/SCSSをJavaScript/CSSに自動的にコンパイルするため、開発者がコンパイルを意識する必要がありません。

② CoffeeScript/SCSSを各々統合

JavaScript、スタイルシートなどのアセットを各々一つのファイルに統合することによって、ブラウザでのリクエスト回数を削減することができます。

③ CoffeeScript/SCSSを圧縮

JavaScriptやCSSを圧縮(スペースやタブ、改行を取り除く)をし、ファイルサイズを減らして負荷軽減するとこができます。

④ アセットにダイジェストを付加

アセット各ファイルに「ダイジェスト」と呼ばれる、コード内容からハッシュ値を算出した値をファイル名の末尾に付与します。
この処理はブラウザの予期せぬキャッシュを回避するためで、コード修正時に毎回ダイジェスト値が変わることによりキャッシュを回避できます。

実行環境によるAsset Pipelineの動きの違い

Asset Pipelineは実行環境により処理内容が変わってきます。
development環境(開発環境)ではデバッグ効率を考えるとコードはオリジナルのまま残しておきたいため②~④の処理は行いません。
また、production環境(本番環境)では②~④の処理は行われますが、①はオーバヘッドが高い処理なので処理をスキップされてしまいます。
prodution環境でアプリケーションを動作させるには以下のrakeコマンドを実行し、あらかじめアセットをプリコンパイルする必要があります。

rake assets:precompile

CoffeeScriptについて

CoffeeScriptはJavaScriptのコードを生成するためのコンパクトな高級言語です。
CoffeeScriptを利用することでJavaScriptでは冗長になりがちな部分をよりシンプルに安全なコーディングができます。
ここでは、CoffeeScriptとコンパイル後のJavascriptを比較しつつの基礎をご紹介していきます。

基礎構成

CoffeeScript

###
CoffeeScriptのサンプル  # (5)
###

a = 1  # (1)、(2)

if a is 1  # (4)
  widow.alert '変数aは1'  # (3)

JavaScript

/*
JavaScriptのサンプル
*/

var a;

a = 1;

if(a == 1) {
  widow.alert('変数aは1')
}

① 行末のセミコロンは不要
Javascriptと違って行末のセミコロンは必要ありません。ただし、一行で複数、文を書きたい場合は下記のようにセミコロンで区切ります。
widow.alert ‘apple’; window.alert ‘orange’

② 変数宣言にvarは不要
変数宣言にvarキーワードは不要です。変数のスコープも最初に値が代入された位置で決まります。

③ メソッドの括弧は省略可能
メソッドの引数を表す括弧は省くことができます。ただし、メソッドの引数がメソッドであるなど表記が分かりずらい場合はカッコで括っておくほうが無難です。

④ 制御文などのブロックを{}の代わりにインデントで管理
Coffeeではif,while,switchなどのブロックはインデントで表記します。

⑤ コメントは#と### ~ ###の2種類
コメントはJavascriptだと//と/**/ですが、CoffeeScriptでは#と### ~ ###で表記します。

リテラル表現

CoffeeScript

fruit1 = 'apple'  # (1)
fruit2 = "orange"
arr1 = [1  # (2)
2
3]
arr2 = [1..10]  # (2)
flg = on  # (3)

if flg is off
  widow.alert 'false!!'

JavaScript

  var arr1, arr2, flg, fruit1, fruit2;

  fruit1 = 'apple';
  fruit2 = "orange";
  arr1 = [1, 2, 3];
  arr2 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
  flg = true;

  if (flg === false) {
    widow.alert('false!!');
  }

① 文字リテラル
文字リテラルは、Javascriptと同様にダブルクォート(“)、またはシングルクォート(‘)で括ります。

② 配列
配列宣言はJavaScriptと同様に宣言することができますが、改行も区切り文字として認識されます。
また、[a..b]という記述で、aからbまでの要素の取り出しを行い配列に設定することができます。

③ 真偽リテラル
Javascriptと同じく「true」、「false」を利用することもできますが、ほかに「on」、「off」と「yes」、「no」が使用できます。

制御構文

条件分岐(if文等)

coffeeSctipt

if a is 1  # (1)
  alert 'OK'
else
  alert 'NG'

if a is 1 then alert 'OK' else alert 'NG'  # (2)

alert 'OK' if a is 1  # (3)

unless a is 1  # (4)
  alert 'OK'

Javascript

  var a;

  if (a === 1) {
    alert('OK');
  } else {
    alert('NG');
  }

  if (a === 1) {
    alert('OK');
  } else {
    alert('NG');
  }

  if (a === 1) {
    alert('OK');
  }

  if (a !== 1) {
    alert('OK');
  }

① CoffeeScriptにおいてのif、else分はこのような表記になります。
以下、CoffeeScriptの演算子

② 条件式と実行分の間をthenで区切って表記すれば1行でも表記できます。

③ 対象の処理が1行であれば、if句を命令の後方に記述する後置構文も利用できます。

④ unlessを利用することにより、否定の条件分岐を表現できます。
unlessにelseを利用することもできますが、否定の否定となりコードが読みにくくなるため原則としては利用すべきではありません。

多岐分岐(switch文)

CoffeeScript

switch number
  when 1
    alert 'high'
  when 2, 3
    alert 'mid'
  else
    alert 'low'

JavaScript

  switch (number) {
    case 1:
      alert('high');
      break;
    case 2:
    case 3:
      alert('mid');
      break;
    default:
      alert('low');
  }

CoffeeScriptにおけるswitch文の特徴は以下の通りです。

  • case文の代わりにwhen句、default句の代わりにelse句を利用する。
  • when句にはカンマ区切りで複数の値を指定できる。
  • 条件句を明示的に抜けるbreak文は不要。

条件式による繰り返し処理(while文、for文)

CoffeeScript

while a < 5  # (1)
  alert a
  a++

until a > 5  # (2)
  alert a
  a++

for a in [3..5]
  alert a

arr = ['apple', 'orange', 'banana']  # (3)
for fruit in arr
  alert fruit

JavaScript

  var a, arr, fruit, _i, _j, _len;

  while (a < 5) {
    alert(a);
    a++;
  }

  while (!(a > 5)) {
    alert(a);
    a++;
  }

  arr = ['apple', 'orange', 'banana'];

  for (a = _j = 3; _j <= 5; a = ++_j) {
    alert(a);
  }

  for (_i = 0, _len = arr.length; _i < _len; _i++) {
    fruit = arr[_i];
    alert(fruit);
  }

① while命令は条件式がtrueの間、処理を繰り返します。

② until命令は条件式がfalseの間、trueになるまで処理を繰り返します。

③ 指定された回数だけ処理を繰り返します。たとえば for a in [3..5] であるならば、aが3から5の間、処理を繰り返します。

④ 配列を繰り返す場合は、for…inを使います。for…inブロック中では配列から取り出した変数(仮変数)を介して配列要素にアクセスできます。

実践してみよう

それでは、CoffeeScriptを利用した簡単なWebアプリを実際に作成し、production環境で動かしてみましょう。
Ruby on Railsの環境設定、プロジェクトの自動生成等の詳細は前回のブログ(こちら)を参照してください。

プロジェクトの作成

Railsコマンドを利用し、プロジェクトのベースを作成します。

mkdir C:\rails_apps
cd C:\rails_apps
rails new coffeescript_test

コントローラとビューの自動生成

cd coffeescript_test
rails generate controller Market index

Viewファイルを編集する

C:\rails_apps\coffeescript_test\app\views\market配下にある、index.html.erbを下記の通り修正します。

What do you buy?<br><br>
<input type="radio" name="kind" value="vegetable" onClick='select_kind("vegetable");'>vegetable<br>
<input type="radio" name="kind" value="fruit" onClick='select_kind("fruit");'>fruit<br><br>
Color<br><select name="color" onChange='select_color();'></select><br><br>
Item<br><select name="item"></select><br><br>
<BUTTON type="button" onClick='buy();'>Buy</BUTTON>

・CoffeeScriptファイルを編集する

C:\rails_apps\coffeescript_test\app\assets\javascripts配下にある、market.js.coffeeを下記の通り修正します。

this.select_kind = (kind) ->
  $('[name="color"]').empty()
  if kind is "vegetable"
    colors = ["","green", "red"]
  else
    colors = ["","yellow", "pink", "purple"]

  for col in colors
    $('[name="color"]').append($('<option>').val(col).text(col))

this.select_color = ->
  $('[name="item"]').empty()
  col = $('[name="color"]').val()

  switch col
    when "green"
      items = ["Cabbage","Lettuce", "Cucumber"]
    when "red"
      items = ["Carrot","Tomatoes"]
    when "yellow"
      items = ["Lemon","Banana", "Pineapple"]
    when "pink"
      items = ["Peach"]
    when "purple"
      items = ["Grape"]
    else
      alert 'no selected'
      return

  for item in items
    $('[name="item"]').append($('<option>').val(item).text(item))

this.buy = ->
  item = $('[name="item"]').val()

  unless item is null then alert item + " please!!"

assetをコンパイルする

アプリを本番環境で本番環境で実行するため、コマンドを実行し、assetをコンパイルする必要があります。

rake assets:precompile

コマンド実行後にはC:\rails_apps\coffeescript_test\public\assets配下に変換済みのコードファイルが作られます。
また、サーバをWEBrickで起動する場合はC:\rails_apps\coffeescript_test\config\environments\production.rbの以下の行をlコメントアウトしてください。

  # config.serve_static_assets = false

実際にCoffeeScriptから変換された下記のJavaScriptからmarket.js.coffeeをコンパイルした部分をを確認してみましょう。
C:\rails_apps\coffeescript_test\public\assets\application-b5852dc1e48b4c46f4dcedb3d9432c36.js

(function() {
  this.select_kind = function(kind) {
    var col, colors, _i, _len, _results;
    $('[name="color"]').empty();
    if (kind === "vegetable") {
      colors = ["", "green", "red"];
    } else {
      colors = ["", "yellow", "pink", "purple"];
    }
    _results = [];
    for (_i = 0, _len = colors.length; _i < _len; _i++) {
      col = colors[_i];
      _results.push($('[name="color"]').append($('<option>').val(col).text(col)));
    }
    return _results;
  };

  this.select_color = function() {
    var col, item, items, _i, _len, _results;
    $('[name="item"]').empty();
    col = $('[name="color"]').val();
    switch (col) {
      case "green":
        items = ["Cabbage", "Lettuce", "Cucumber"];
        break;
      case "red":
        items = ["Carrot", "Tomatoes"];
        break;
      case "yellow":
        items = ["Lemon", "Banana", "Pineapple"];
        break;
      case "pink":
        items = ["Peach"];
        break;
      case "purple":
        items = ["Grape"];
        break;
      default:
        alert('no selected');
        return;
    }
    _results = [];
    for (_i = 0, _len = items.length; _i < _len; _i++) {
      item = items[_i];
      _results.push($('[name="item"]').append($('<option>').val(item).text(item)));
    }
    return _results;
  };

  this.buy = function() {
    var item;
    item = $('[name="item"]').val();
    if (item !== null) {
      return alert(item + " please!!");
    }
  };

}).call(this);

元のCoffeeScriptファイルと比較してみても、いかにCoffeeScriptで書いた方がシンプルでコード量が少なく作れることがわかります。

せっかくなので本番環境(production環境)で動かしてみましょう。

rails server -e production

下記のURLでアクセスできます。
http://localhost:3000/market/index

最後に

いかがでしたでしょうか。
今回作ったWebアプリはわざわざRuby on Railsを使うまでもない内容でしたが、CoffeeScriptとJavaScriptを比較することで、いかにCoffeeScriptがシンプルにコーディングできるかを知っていただけたと思います。

ご観覧いただき有難うございました。