2012年12月15日土曜日

Express アプリケーションを CoffeeScript で書く

実行環境
OS : 10.8.2
node : v0.8.6
express : v3.0.4
coffee-script : 1.4.0

以前の記事で、クライアントサイドの JavaScript を CoffeeScript 化しましたが、今回はサーバサイドの JavaScript を CoffeeScript で書けるようにします。

CoffeeScript をインストール


CoffeeScript は npm で、-g オプション を付けてインストールします。
$sudo npm install -g coffee-script

これで、コンソールから coffee コマンドが使えるようになります。
$ coffee -v
CoffeeScript version 1.4.0

Express が生成する JavaScript を CoffeeScript に変換する


既存の JavaScriptファイル(以下 jsファイル)を CoffeeScriptファイル(以下 coffeeファイル)に変換するには、js2coffee を使います。
$ npm install -g js2coffee

これで、コンソールから js2coffeeコマンドが使えるのようになるので、Expressが自動生成した jsファイルを、coffeeファイルに変換します。

$ express coffeeTest && cd coffeeTest && npm install

$ js2coffee app.js > app.coffee
$ js2coffee routes/index.js > routes/index.coffee

アプリケーションを起動


後は、app.coffee を 
$ coffee app.coffee

で実行すれば、Expressアプリケーションが起動します。
もちろん、app.coffee 以外のファイルもすべて CoffeeScript で記述できます。


コンパイル済み jsファイルを保存したい場合


上記の方法だと、コンパイル済みの jsファイルはファイルシステムに保存されません。今回は、単純に coffeeファイルと同じ場所に jsファイルを生成してもよいのですが、管理しやすいように、ファイルの配置を工夫してみます。

例えば、 『src ディレクトリに coffee ファイルを、app ディレクトリにコンパイル済み jsファイルを保存する』 場合は、ディレクトリ構成は下記のようになると思います。(app.coffee を移動すると、view と static のパスを書き換える必要があるので注意してください。)

ディレクトリ構成例
coffeeTest/
  |_ app/ <= コンパイル済みの js ファイルはすべてここに生成される
  |
  |_ src/ <= coffeeScript はすべてここに保存する
  |  |_ app.coffee
  |  |_ routes/
  |    |_ index.coffee 
  |
  |_ node_modules/
  |_ public/
  |_ views/
  |_ package.json
src ディレクトリ以下の coffeeファイルを appディレクトリ以下にコンパイルするには、下記コマンドを実行します。 ( -b は関数のラッパーをはずす、-o はコンパイル先ディレクトリを指定するオプションです。)
$ coffee -bco ./app ./src
後は app.js をターゲットにしてアプリケーション起動します。
$ node ./app/app.js
もしくは、アプリケーションのルートに server.js などのファイルを作成し、そこで require('./app/app') してしまっても良いかもしれません。

CoffeeScriptの変更を監視する


coffeeファイルを変更するたびにいちいち coffeeコマンドを実行するのは面倒なので、 coffee コマンドを -w オプションつきで実行して、src ディレクトリに変更があるたびに自動でコンパイルを実行するようにします。
$ coffee -wbco ./app ./src
さらに、このコマンドをCakefileにtask登録しておけば、一発で監視を開始できるのでおすすめです。

Cakefile
spawn = require('child_process').spawn
task 'watch', ->
  watch = spawn('coffee', ['-wbco', './app', './src'])

  watch.stdout.on 'data', (buffer)->
    if(buffer)
      console.log buffer.toString().trim() 

  watch.stderr.on 'data', (buffer)->
    if(buffer)
      console.log buffer.toString().trim() 
実行
$ cake watch

テンプレートモジュールを使う


以上、手動で CoffeeScript の設定を行ってきましたが、 express-coffeeモジュールを使うと、CoffeeScript で記述する前提でExpressアプリケーションのひな形を生成してくれます。testスクリプトもCoffeeScriptでかけるようになっているので、是非チェックしてみてください。

2012年12月9日日曜日

[node.js, undersore.js]複数の非同期処理の完了を待ち受ける

実行環境
node.js v0.8.3
undersore.js 1.4.3

node.js のプログラムで、複数の非同期処理の終了を待ってから後続の処理を実行する場合、underscore.js の after 関数がとても便利だったので紹介します。

underscore.js のインストール
$ npm install underscore

after 関数

underscore.js の after 関数では、第1引数で渡した数値回、戻り値で返す関数が実行されると、第2引数のコールバックが実行されます、、といってもよくわからないので、
例として、複数のファイルを非同期で削除し、すべてのファイルの削除が完了したらメッセージを表示するプログラムを作成しました。

//カレントディレクトリの、foo, bar, hoge というファイルを非同期で削除する。
//すべてのファイルの削除が完了したら、コンソールにメッセージを表示する。
var fs = require('fs'),
     _ = require('underscore')

var targets = ['foo', 'bar', 'hoge'];

//ファイルの削除を待ち受ける関数
var displayResult  = _.after(targets.length, function(){
  console.log('ファイルの削除が完了しました。');
});

_.each(targets, function(target){
  fs.exists(target, function(exists){
    if(exists){
      fs.unlink(target, function(err){
        if(err) throw err;
        console.log(target + " を削除しました。");
        displayResult(); //ファイルの削除を通知
      });
    } else {
      displayResult(); //ファイルの削除を通知
    }
  });
});

console.log('最初にここが実行されます。');

実行結果

最初にここが実行されます。
hoge を削除しました。
bar を削除しました。
foo を削除しました。
ファイルの削除が完了しました。

想定通りの順番で処理が実行されました!

しかし、上記のサンプルを見てもお分かりの通り、こんなに短いプログラムでも、処理が行ったり来たりしていて、もうスパゲッティ化の兆候が現れはじめてます。。
複数の非同期処理を制御する場合は、かなり慎重にプログラミングする必要がありますね。

でも、after だけでなく、underscore.js には、便利な関数(extend, each, map など)がたくさん用意されているので、積極的に利用していこうと思います。

まとめ


  • 複数の非同期処理の完了を待ち受ける場合は、 underscore.js の after 関数が便利。
  • コードのスパゲッティ化には十分注意する!

2012年12月1日土曜日

[node.js + express]クライアントサイドの JavaScript を CoffeeScript化 & 複数ファイルを結合 & 縮小化する

(2013/1/14 追記) サーバーサイドのプログラムをCoffeeScript で記述する方法をこちらにまとめました。

実行環境
OS: Mac OS X 10.8.2
node: v.0.8.3
express: v3.0.3
connect-assets: v2.3.3

CoffeeScript の導入


クライアントサイドの CoffeeScript のコンパイルや、複数の JavaScript ファイルの結合、縮小化(Rails の Asset Pipleline 機能と同等の機能)をするには、connect-assets モジュールが便利です。
今回の記事では、 connect-assets で JavaScript を扱う基本的な方法を説明します。(なお、このモジュールでは css も同様に扱えますが、この記事では触れていません。)


connect-assets の設定


require してミドルウェアを設定します。
app.use(require('connect-assets')());


CoffeeScript(JavaScript) ファイルの保存場所


アプリケーションの直下に asset/js というディレクトリを作成し、ここに .coffee(.js) ファイルを保存します。
myApplication/
  |_ assets/
      |_ js/
          |_application.coffee
          |_jquery.js


view から JavaScript をロード


view からコンパイル済みの JavaScript をロードするには、 js() 関数にファイル名を渡します。
この関数はコンパイル済みの JavaScript を指す <script> タグを文字列で返すので、jade で実行する場合は下記のように != で呼び出す必要があります。
!= js('application')

レンダリング結果は下記のとおり
<script src="/js/application.js"></script>


ファイルの依存関係


CoffeeScript(JavaScript)ファイルの依存関係は、コメントで定義します。例えば、以下のようなファイル構成で、
myApplication/
  |_ assets/
      |_ js/
         |_ application.coffee
         |_ foo.coffee
         |_ bar.coffee

application.coffee が foo.coffee と bar.coffee に依存している場合は、application.coffee で以下のように依存関係を定義します。(JavaScript の場合は //= )

application.coffee
#= require foo.coffee bar.coffee

この状態で jade で != js('application') を実行すると、下記の結果が得られます。
<script src='/js/foo.js'></script>
<script src='/js/bar.js'></script>
<script src='/js/application.js'></script>

特定のディレクトリ以下のファイルをすべて依存ファイルとして指定するには、下記のように記述します。
#= require_tree ./


結合・縮小化


アプリケーションを production モードで実行している場合は、依存ファイルの結合と、ファイルの縮小化が実行されます。結合・縮小化済みファイルは、デフォルトでは builtAssets ディレクトリに保存されるので、あらかじめ assets ディレクトリと同じ構成で builtAssets を作成しておく必要があります。
myApplication/
  |_ assets/
  |  |_ js/
  |     |_ application.coffee //foo.coffee と bar.coffee に依存
  |     |_ foo.coffee
  |     |_ bar.coffee
  |
  |_ builtAssets/
     |_ js/

この状態で production モードでアプリケーションを実行し、下記 jade ファイルを実行すると、
!= js('application')

以下のように結合・縮小化されたファイルが生成されました。
myApplication/
  |_ assets/
  |  |_ js/
  |     |_ application.coffee
  |     |_ foo.coffee
  |     |_ bar.coffee
  |
  |_ builtAssets/
     |_ js/
        |_ application-38a0083f4a868f2797aba944e5ce3ad6.js

js() が生成する <script> タグは、src 属性でこのファイルを指定します。
結合前のファイルに変更があるたびに、新しいファイルが生成されますが、js() 関数は常に最新のファイル指定する <script> タグを返してくれます。

さらに、結合済みファイルは Expires ヘッダがかなり長く設定されるので、とても効率の良い JavaScript の配布が可能となります。

まとめ


  • connect-assets モジュールを使うと、JavaScript の結合と縮小化、 CoffeeScript での記述が可能になる。
  • .js, .coffee ファイルは、 assets ディレクトリに配置する。
  • view からは、js() 関数で JavaScript をロードする。
  • アプリケーションを production モードで実行すると、コンパイル、結合、圧縮済みのファイルが、 builtAssets ディレクトリに生成される。