Hatena::ブログ(Diary)

エウレカ技術部 RSSフィード

2011-07-19

やさしいCoffeeKupの使い方


今回はCoffeeKupを使ってみます。
CoffeeKupはCoffeeScriptで書くテンプレートエンジンです。なぞこれを選ぶかといえば、coffeescriptだからです。それ以上の理由はありません。
で、今回はサンプルコードをcoffeescriptのみで書きます。

やること


Expressでリクエストを受け取り、HTMLを返します
res.send("Hello World!"); としていた部分で、HTMLを構築して返してやればいいわけです。

インスコ

例のごとく

npm install coffeekup


概念

基本的にすべてのタグの名前がCoffeeScriptの関数として扱われます
基本的にフルスタックのCoffeeScriptが動きます

たとえば こういうふうに書きます

textarea type:"text", name:"content", id:"content"


括弧を省略せずに書くとこうです

tag_name({attr1:'hoge','attr2':'fuga',...}),-> "TEXT CONTENT"

タグの属性をobjectで、タグの中身をコールバック関数としてネストします

Express で 使う

テンプレートエンジンをexpressに登録します

express = require 'express'

app = express.createServer()
app.register '.coffee', require 'coffeekup'
app.set 'views', __dirname + '/views'
app.set 'view engine', 'coffee'

app.get '/', (req, res)->
  res.render 'index.coffee'
    locals:
      title : 'test message'

app.listen 3000



expressの規格で、基本的にlayout.*ファイルが先に呼ばれます
views以下のディレクトリを、テンプレートの置き場所に指定します


views/layout.coffee

doctype 5
html ->
  head ->
    title 'coffee app test'
    meta charset: 'utf-8'
    script src: 'http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js'

  body ->
    header ->
      h1 a href: '/', -> 'MyApp'
        ul ->
          for item in ['Home', 'Logout']
            li -> a href: "/#{item.toLowerCase()}", -> item

    div id: 'main', ->
      @body



HTML5で宣言しています
これはlayout.coffeeはすべてのリクエストで共通に呼ばれる部分です。@body のところに、それぞれのファイルが挿入されます


次に views/index.html を書きます。"/"を踏んだ時に呼ばれる本体です。

h2 "#{ h title }"
coffeescript ->
  $(document).ready ->
    $("body").click (e)->
      console.log 'click body'

h が組み込みのエスケープ関数です。
coffeescriptの部分は、coffeescriptで書いたスクリプトがjsにコンパイルされ、そのまま埋めこまれます。

  res.render 'index.coffee'
    locals:
      title : "myblog"

これは変数として title= "myblog"を渡して、index.coffeeをベースに index.htmlを構築して返せ、ということになりますね。


エスケープ

ユーザーからの入力として受け取ったものをそのまま表示するのは、ウェブアプリケーションだとご法度です。やっちゃいけません。
たとえば入力データとして text = "<script>alert('yahho-');</script>" などとデータをもらうと、text がそのままHTMLに埋め込まれた場合、任意のデータが実行されてしまいます。
それで、ユーザーの個人情報を扱っているサービスだと、「ユーザーデータを別のサーバーに送信する」みたいなことをやられる恐れがあるわけですね。

回避


HTMLには「実体参照」という機能があります。HTMLを構成する文字としてのデータとは別に、記号には記号を指すデータがあるわけです。
たとえば   と書くとHTML上では半角スペースになります、
つまり、HTMLの属性をなす記号を実体参照に置き換える、そういうことをやってるわけですね

escape関数は記号データを実体参照に入れ替えて、タグとして認識しなくする、という目的で使われるわけです。



だいたいこんな感じです
というわけで、CoffeeScriptがメインなら選択肢に入るんじゃないでしょうか

2011-07-14

node.jsの勘所


mizchiです。今日は、結局なんでJavascriptなのか、みたいなことを書きます。

ロジックと手続き

ロジックは論理的な手順を記述します。複雑なものはアルゴリズムなどと呼ばれますが、それは数学的な構造を含むアプローチであることが多いです。
それに対して、手続き的な記述は、決められた手順を踏むことが多いです。これに関しては「ググれ」というしかありません。ただ、言語に精通することで中身を想像できるようになり、実装が早くなります。
で、他人が作ったものを使い回すことでだいたいものは実装できるので、基本的にコーディングって手続き的なコーディングの連続になることが多いわけですね。ゲームプログラミングならともかく。

手続きを経て入手してきたデータを、他のパーツと差し替えて自分のやりたい結果に導いていく、その過程がコーディングだとも言えるでしょう。

というのがプログラミングの一般論。

javascriptと関数と関数オブジェクト


まず 普通の関数

var f1 = function(n){
     return n*2;
};

これは n を入力値とし、n の二倍の数を返すf1という関数です。f1が呼ばれると、どこからでもf1の処理経路を通り、nの二倍という実行結果を返すわけです。
(n)が関数の入り口、return n*2 が出口にあたります。ここで、returnを書かなければ処理の受け渡しをすることが出来ません。


javascriptでは、関数を変数に代入して受け渡しすること出来る言語です。f1という変数に登録された関数のオブジェクトなわけです。
ここでf1という名前で「関数オブジェクト」をf1という「変数」に登録したあと、()はイベントを実行するトリガーの役目を果たします。仮にここで関数オブジェクト以外を渡すと()が呼び出せずエラーで終了するわけです。

このように受け渡すものが何かわからないまま処理する言語を「動的型付」と呼び、JavaやCのように事前に宣言しないといけないものを「静的型付」と呼びます。動的型付の言語で大切なのは、受け渡し時に動作が保証されるように検証しておくことで、それには沢山アプローチがあるのですが、面倒なのでここでは解説しません。

「非同期」とは何か

プログラムの実行結果は、通常、上から下へ流れる「線」で表すことが出来ます。
どんなに複雑なプログラムでも、原理的にはプログラムのはじまる「単一点」から、そこから一行づつ解釈していくと、原理的には理解することが可能です。実際にはサンプルコードをわからないまま使うことも多いですが、良いプログラムほど「抽象化」されており、わからないことをわからないまま扱うことが可能であるほど、良いコードといえます。

この線を「スレッド」といいます。プログラム開始の単一点からはじまるスレッドを、「メインスレッド」と呼びます。
しかし、スレッドは、自分以外のスレッドを作成して走らせることができます。これはスレッドの分岐点として表すことができます。イメージできますか?


新しいスレッドは、現在の処理と並行的に処理されます。ここで問題となるのが、スレッド同士は、基本的には相手の状態と関係なく処理が進んでいくのです。
AとBというスレッドが、お互いにXを参照していて、Xを書き換えていると、Xがどんな状態を経て変わっていくかA、B単独では推定することはできません。

node.jsと非同期


node.jsの実行本体は、無限ループだと捉えることが可能です。

こういうものを想像してください

while(true){ // 無限ループ
     doMostRecentTask();//処理待ちイベントを一件だけ処理
}

僕らが眼に見える部分では、ひたすらに処理したいイベントを、無限ループの処理待ちとして登録してると言えます。
次に、実際のスクリプトで考えてみましょう

var fs = require('fs'); // ライブラリ呼び出し  同期的
var fs.readFile(
     'myfile.txt', // ファイル名  一つ目の引数
     function(err,file){   // 呼び出しが終わったあとに呼ばれる関数。非同期。
          console.log( file.toString() ); 
     }
);

標準的なnodejsのファイル読み込みです。

ここで登録されるイベントは二つ。fs.readFileが非同期関数で、ファイルの読み込みが終わった時、fs.readFileの二つ目の引数として、実行結果を引数として関数が呼び起こされます。これをコールバック関数と言います。

これによって何が可能になるかというと、「ファイルの読み込みが終わった」ことを保証し、コールバック関数は「読み込みが終わったファイルを表示する」ことが可能になったわけです。この間、メインのスレッドは中断されずに実行されます。

理由

なぜこんな面倒な挙動なのか?ということには、ちゃんとした理由があります。
ネットワークプログラミングにおける最大の敵は、通信の遅延です。正直今挙げたファイルの読み出しなんてのは、数ミリ秒程度終わるでしょうが、ネットワークは、インターネットでご存知の通り、体感できてしまうほどの遅延があるわけです。この間、コンピュータの計算資源を待機させておく、なんて勿体無いことは、可能ならば避けたい。
だから、スレッドは待機せずに次のイベントを発行して、処理を終わった順に実行してたいわけです。


登録したイベントは、すぐには処理されません。もちろん、最近のコンピュータは充分に早いですから、一瞬で終わったようにはみえるでしょう。ただ、零に近い時間でも、前後関係は保証されないわけですから、1つづつステップを踏んで処理する仕組みが必要で、それが「そのメソッドの手続きが完了したとき、最後に実行されることが保証される」というのがコールバック関数なのです。


jsそのものは実行速度は他の言語と比べて早いほうだとはけして言えません。複雑なロジックを書くのには向いていませんが、それは外部のプログラム呼び出しておいて、実行結果を非同期で返す、そういうアプローチが必要となるわけです。


ちなみに、サンプルとしてみせたreadFileにはreadFileSyncという同期的な関数があります。

var fs = require('fs'); // ライブラリ呼び出し  同期的
var text = fs.readFileSync('myfile.txt');
);


これは見慣れた手続き型の関数ですね。もちろんファイルを読み込んでる間はスレッドの処理がブロックされます。

スクレイピングの解説をすることと、モジュールの想像力

っていうところで、ここで前に載せたスクレイピング用のモジュールを解説します

//sc.js
var request = require('request'),
      jsdom = require('jsdom');

fetch$ = function(url , next_func){
  request({ uri:url }, function (error, response, body) {
    jsdom.env({html: body,scripts: ['http://code.jquery.com/jquery-1.5.min.js']}, function (err, window) {
      next_func(window.jQuery);
    });
  });
};

//使ってみる
fetch$( "http://www.google.com", function($){
    // ここからブラウザでの処理と同じ
    console.log($('p').text());
}); 


1.fetch$ という関数を定義する。入力データとして、jQuery化したい対象のurlと、処理が終わった後に呼ぶnext_funcを与える
2.requestのメソッドにuriとして受け取ったurlを渡し、エラーが起きたか通知するerror,通信が正常に行われたかという状態を返すresponce,実際に返されたデータbodyの三つを受け取る関数を登録
3.jsdom.env に受け取ったbodyをhtmlとして、jQueryを読み込んで次のコールバック関数を登録
4.最後に、与えられたnext_funcに対して、jQuery化されたオブジェクトをコールバック関数に対して返す


というメソッドの流れになってます。
fetch$ は urlを受け取り、コールバック関数がjQuery化されたオブジェクトを受け取ったのを確認してからメソッドが始まるわけです。


で、このメソッド書いた後にnode.ioとnode-scrapingってライブラリを見つけたんですが……

mape/node-scraper - GitHub

var scraper = require('scraper');
scraper('http://search.twitter.com/search?q=javascript', function(err, jQuery) {
    if (err) {throw err}

    jQuery('.msg').each(function() {
        console.log(jQuery(this).text().trim()+'\n');
    }); });

chriso/node.io - GitHub

require('node.io').scrape(function() {
    this.getHtml('http://www.reddit.com/', function(err, $) {
        var stories = [];
        $('a.title').each(function(title) {
            stories.push(title.text);
        });
        this.emit(stories);
    }); });


たぶん皆考えることほとんど同じで、結果として似たような関数になってるわけです。自分は面倒臭がってエラーハンドリングしてないですが、それがあるぐらいですね。
入力データとコールバック関数与えて、入力データを加工したデータを元にコールバック関数を呼ぶ、というのが基本的な発想なわけで、この考え方に慣れれば自分でどんどん非同期の関数を作っていけるわけです。


node.jsになれてコールバック関数使いまくりましょう

CoffeeScript で function(a,b,...) が (a,b,...)-> になってる理由


functionって文字自体が長いし、(){}多用するとどこでタグとじてんのかわからんので、考えるのをやめたい

fetch$ = (target,next_func)->
  request {uri:target}, (err, response, body)->
    jsdom.env {html: body,scripts: [jquery_source]}, (err, window)->
       next_func window.jQuery

2011-07-12

MongoDBの基本 node.jsでMongoDBを扱ってみる


某mizchiです。
次はデータベースを扱います。
nodejsとnpmを扱う環境が整ったでしょうか。
まだの方は「2011年最速のウェブアプリ開発環境はnode.js/CoffeeScript/Expressだ!」 http://d.hatena.ne.jp/eureka_tech/20110629/1309375362 を参考に環境を作ってください。


MongoDBとは?
-> BSONという簡単なデータ構造でデータベースを構築できます

なんでSQL使わないの?
-> 「あれ学習コスト高くね?」


MySQLとMongoDBのパフォーマンスは一長一短 だったら簡単な方から!
ついでに言えば、MongoDBで扱うBSONは拡張されたJSONで、JSONとはつまりJavascriptで書かれたデータ形式なわけで、そこらへんの相性もいいわけです


MongoDBのインストール

Homebrewを使います

$ sudo brew install mongodb

とりあえず手動で起動する

$ sudo mongod &

Macで起動を自動化したければ MacでMongoDBをDaemon化して自動起動させる - Meltdown Countdownを参考にlaunchdいじりましょう。
Linux環境ではインストール時にDaemonに登録されるので自動で起動されるので気にしなくて大丈夫です。

mongoose

node.jsからmongodbへアクセスするためのライブラリ
色々種類はあるんですが、今回は一番使われてるMongooseを使います

npmからいれる

$ npm install mongoose


データ構造

SQLではないMongoDBの構造は、データベース名 - コレクション名 - [JSONの列] となっています
データベースとコレクションというのは、二階層の名前空間だと思ってもらえれば結構です。MySQLのデータベースとテーブルに相当します。
具体的にはどのような構成になるかというと、

myapp - users - 
           {username:"mizchi",password:"password",params:["A","B"]}
           {username:"koni",password:"konisimple",params:["B","C"]}
           {username:"masall2",password:"osall2", params:["A","B","C"]}
           ...

という感じです。(実際には、列ごとに _id:ObjectId("9d8b43a234....")というような内部用のパラメータが生成されます)
JSONのカラムは、独自に日付データやバイナリデータが格納できるように拡張されたBSONというデータ構造で、詳細はドキュメントで確認して下さい。
チュートリアル - Docs-Japanese - 10gen Confluence


MySQLなどと違って、事前にデータベース名、コレクション名を登録する必要もないし、JSONのスキーマを事前に決めておく必要はありません。コレクションにアイテムを登録したときにデータベース、コレクションが生成されます。

インタラクティブから触ってみる

MongoDBのクエリは、JSONです。投げるJSONのクエリによって、条件に合致したJSONのカラムが返されます。
mongodbは、javascriptのインタラクティブシェルを持っているので、使ってみましょう

$ mongo #起動
MongoDB shell version: 1.6.3
connecting to: test
> db = connect("localhost/test") #  使うデータベースをtestに指定
> db.users.save({username:"mizchi",password:"password",params:["A","B"]});  #usersのコレクション挿入してみる
> db.users.find().count() # 中身が挿入されたか、総数をみて確認
1
> db.users.save({username:"koni",password:"konisimple",params:["B","C"]} ) #もうひとつ挿入
> db.users.find() # 空のクエリで検索(全部とってくる場合)
{ "_id" : ObjectId("4e077d13984bb3412af1a0d1"), "username" : "mizchi", "password" : "password", "params" : [ "A", "B" ] }
{ "_id" : ObjectId("4e077deb984bb3412af1a0d2"), "username" : "koni", "password" : "konisimple", "params" : [ "B", "C" ] }
> db.users.find({username:"koni"})  # クエリで条件指定して検索 username == "koni"のものq   
{ "_id" : ObjectId("4e077deb984bb3412af1a0d2"), "username" : "koni", "password" : "konisimple", "params" : [ "B", "C" ] }
> db.users.remove( { username : "mizchi" } ) # 条件に合致したものを削除
> db.users.find() # 削除されたか確認
{ "_id" : ObjectId("4e077deb984bb3412af1a0d2"), "username" : "koni", "password" : "konisimple", "params" : [ "B", "C" ] }
> db.users.drop() # 使い終わったので空にする
true                       
> db.users.find().count() # 確認
0

参考: シェル 概要 - Docs-Japanese - 10gen Confluence

MongooseからMongoDBにアクセスしてみる!


test_db / test_collection にデータを登録します

var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/test_db');
//コレクションのスキーマ
user_scheme = {
  name : String,
  age : Number
};

User = mongoose.model('test_collection', new mongoose.Schema(user_scheme) );

//要素の追加
item = new User();
item.name = "mizchi";
item.age = 23;
item.save(function(e){
  // ポスト成功時のコールバック関数
  console.log('add new user:'+JSON.stringify(item));
});


これぐらい面倒になるとcoffeescriptで書いたほうが簡潔で輝いてきます
(「>」が実体参照で>になってますが、適当に置き換えてください)

mongoose = require 'mongoose'
mongoose.connect 'mongodb://localhost/test_db'

# データ登録用スキーマ
user_scheme =
  name : String
  age : Number

User = mongoose.model 'test_collection', new mongoose.Schema(user_scheme)

# 要素の追加
item = new User()
item.name = "mizchi"
item.age = 23
item.save (e)-> console.log 'add new user:'+JSON.stringify(item)


# 検索
User.find {},(err,users) ->
  console.log u.name for u in users 



詳細な日本語ドキュメントがあります
Mongoose - デベロッパーズガイド 日本語訳

node.js + express + mongodb + mongoose を試してみた - ネリモノ


ではでは

2011-07-10

nodejsでjQueryスクレイピング

社内ネットウォッチャーの @mizchi です。今日はnode.jsでスクレイピングします。
一般的に言うスクレイピングとは、HTMLから自分がほしい箇所だけ抜き出すことです。それを自動更新でブン回して差分を確認したり、グラフにしたりするわけです。


node.jsでスクレイピングすると何が嬉しいんでしょうか? それはベースがjavascriptだから、みんなが大好きなjQueryが使える!ってこと

ライブラリのインストール

今回使うのは、request、jsdom。
URLをリクエストするrequest、htmlをブラウザと同じDOMエレメントのように扱えるjsdomという役回りです。

npm install request
npm install jsdom

jsdomでjQueryを読み込んで、そのjQuery化されたオブジェクトを返します。

実行

指定したURLのjQueryオブジェクトを返す非同期関数を作って、コールバック関数で処理を引き継ぎます。

//sc.js
var request = require('request'),
    jsdom = require('jsdom');

fetch$ = function(url , next_func){
  request({ uri:url }, function (error, response, body) {
    jsdom.env({html: body,scripts: ['http://code.jquery.com/jquery-1.5.min.js']}, function (err, window) {
      next_func(window.jQuery);
    });
  });
};

//使ってみる
fetch$( "http://www.google.com", function($){
    // ここからブラウザでの処理と同じ
    console.log($('p').text());
});


どうです、簡単でしょう?
jquery.jsをローカルに落としておいて、リモートから落としてきてるjQueryの代わりに指定すれば、通信のオーバーヘッドが多少減ります。

ニコ動の総合ランキングを取ってくる例

fetch$( "http://www.nicovideo.jp/ranking/fav/hourly/all", function($){                                                                                                        
    $('a.watch(').each( function(i){console.log( $(this).text()+':'+$(this).attr('href') )});
});

ニコ動は一応公式APIあるので、できるならそっち叩いたほうがいいかも。

エンコーディングについて諸注意

nodejsには、歴史が浅いゆえの欠点が在ります。基本的にbinary、または utf8で文字列を扱うのですが、euc や sjis の場合は文字のエンコードを変換してやらねばなりません。が、nodejsはデフォルトではutf8以外のエンコードを知らないので、外部のライブラリを呼び出さないといけない。で、文字コード変換のための node-iconvというライブラリがあるのですが、これが今Macで動いてないっぽい。
https://github.com/tomykaira/node-iconv


というか、自分のMacではnodeのC拡張がほとんど動いてない気がします…。Ubuntuでやるのが無難なのかな。
とりあえずはストリームのまま保存して、あとでnkfなどのライブラリで変換してやるのが無難っぽいですね。サブプロセスでもいいかも。


(あとで他のエンコーディング対応の仕方を確認します)

coffee scriptで書いてみよう!

やってることは全く同じです。

//sc2.coffee
request = require 'request'
jsdom = require 'jsdom'
jquery_source = 'http://code.jquery.com/jquery-1.5.min.js'

fetch$ = (target,next_func)->
  request {uri:target}, (err, response, body)->
    jsdom.env {html: body,scripts: [jquery_source]}, (err, window)->
      next_func window.jQuery

fetch$ 'http://www.google.com', ($)-> console.log $('p').text()


こっちのほうが簡潔で、僕は好きです

※ jquery_source をローカルの./jquery.jsに指定すると、$ coffee sc2.coffee でjQueryを読み込めずコケます。$ coffee -c sc2.coffee して$ node sc2.jsすると問題ないのですが、この問題がわかる方、教えてください…


参考:
jsdom + jQuery in 5 lines with node.js - blog.nodejitsu.com - scaling node.js applications one callback at a time.
node.jsとjQueryでスクレイピングするウェブアプリの作り方 | さくらたんどっとびーず

2011-06-29

2011年最速のウェブアプリ開発環境はnode.js/CoffeeScript/Expressだ!


技術をアレして遊んでたい竹馬です。

概要


みなさん、NodeJSをご存知ですか?
サーバーサイドをJavascriptでやるというアレです。

CoffeeScriptはどうでしょう?
簡易な文法で、ベストプラクティスなJavascriptのコードを生成してくれます


実際に実行するアプリケーションの、最小環境はこのようになります


### app.coffee ###

express = require 'express'
app = express.createServer()

app.get '/', (req,res) ->
  res.send 'Hello World'

app.listen 3000


これを実行して http://locahost:3000/ にアクセスすると、ブラウザ上ではHello,World! と表示されます
っていうのを元に、データベースにアクセスして、データを流してHTMLを出力すると、立派なウェブアプリケーションとなるわけです


ね、なんだか簡単なような気がしません?


ここから先は、開発環境をMac、本番環境がLinux/Ubuntuだと想定し、Macでの開発環境を構築します。

環境構築


道のりは長そうに見えますが、打つコマンドも、ハマりそうな箇所も少ないです。エディタも使いません。
基本的に、面倒なところをすべてパッケージマネージャと呼ばれるシステムに丸投げします。

Xcode           (Macの基本開発環境)
Homebrew        (MacOSX用パッケージマネージャ)
 - node.js      (サーバサイドJavascript)
   - npm        (node.js用パッケージマネージャ)
     - express         (WebApplicationFramework)
     - coffeescript    (Better Javascriptとしての言語)


Xcode

インストール:
OS付属のインストールDisc(二枚目)からDL。もしくはADCからダウンロード(要:無料のApple Developper会員登録)。
Download Xcode 4 - Apple Developer
ファイルサイズが大きいので、DLすると時間かかるかも。


ここから先はTerminalでの作業になります。
/Application/ユーティリティ/Terminal.app を起動。
(或いはiTerm2.appのインストールを推奨)
iterm2 - Development site for iTerm2 - Google Project Hosting

Homebrew

インストール:

$ ruby -e "$(curl -fsSL https://gist.github.com/raw/323731/install_homebrew.rb)"

途中でパスワードを聞かれるので入力


Homebrewでインストールしたものは/usr/local/bin以下に置かれるので、PATHに追加

$ export PATH=/usr/local/bin:$PATH

以降も適用されるように ~/.bash_profileなりに↑のexport文を記述してください

node.js

Homebrew使ってインストール

$ sudo brew install node


npm

node.js用のライブラリを管理するためにnpmを入れる

$ curl http://npmjs.org/install.sh | sh


express

npmから入れる

$ npm install express


coffee-script

npmから入れる

$ npm install coffee-script



Express ウェブアプリケーションフレームワーク


というわけで、環境が整ったらExpressを動かしてみましょう。
どこでもいいので、テキストエディタを使って以下のようなファイルを書いてください

// app.js
var express = require("express");
var app = express.createServer();

app.get('/', function(req, res){
  res.send('Hello World');
});

app.listen(3000);
console.log("server start...");

次にコマンドラインから実行

$ node app.js
server start ...

ここで http://localhost:3000/にアクセスしてください。 Hello,World! と表示されましたか?もし駄目だった場合は、環境構築に失敗している可能性があるので見直してください。Expressは意外と日本語のドキュメントが充実しているので、参考になります
Express - node Webフレームワーク | 日本語ドキュメンテーション


Hello, CoffeeScript!

CoffeeScriptとは、PythonやRubyのような文法を取り入れた、よりシンプルに記述できる構文を持ったJavascriptを生成するための言語
coffeescriptで書いたプログラムを、コンパイルしてJavascrptのファイルを生成します。
何ができるかは、次のページを見てもらうのが早いです CoffeeScript


公式ドキュメントに次のようなサンプルコードが載っています

# Assignment:
number   = 42
opposite = true

# Conditions:
number = -42 if opposite

# Functions:
square = (x) -> x * x

# Arrays:
list = [1, 2, 3, 4, 5]

# Objects:
math =
  root:   Math.sqrt
  square: square
  cube:   (x) -> x * square x

# Splats:
race = (winner, runners...) ->
  print winner, runners

# Existence:
alert "I knew it!" if elvis?

# Array comprehensions: cubes = (math.cube num for num in list)

Javascriptを知っていれば、何をやっているかおぼろげに理解できるでしょう。タイプ数の多い「function」や末尾セミコロンなどの冗長な部分が省かれ、オブジェクトが簡単に宣言できるようになっています。
それでいて、Javascript本体の特徴を失わないようになっており、既存のJavascript資産とそのまま組み合わせられるので、Better Javascript として注目されています。
何が便利かっていうと、Javascriptで実装しにくいクラスと継承をCoffeeScript側がで全部やってくれて、このためだけでも使う価値があります。

class Animal
  constructor: (@name) ->

  move: (meters) ->
    alert @name + " moved " + meters + "m."

class Snake extends Animal
  move: -]]>
    alert "Slithering..."
    super 5

class Horse extends Animal
  move: -]]>
    alert "Galloping..."
    super 45

sam = new Snake "Sammy the Python"
tom = new Horse "Tommy the Palomino"

sam.move() tom.move()


これによって巨大な構造をもつプログラムが書きやすくなりました。

ためしてみる

次のようなファイルを保存します

### hoge.coffee ###

myfunc = (x)->
     (i*i for i in [1..x])

console.log myfunc(5)

coffeeコマンドで、coffeeのファイルをそのまま実行できます

$ coffee hoge.coffee
 [1,4,9,16,25]


なにをやってるとというと…
関数の中で一番最後に評価されたものが return されます。明示的にreturn することもできます。(Rubyっぽい)
(i+".alphabet" for i in ["a","b","c"]) は 要素をそれぞれ操作した配列を返します。(Pythonっぽい)
(param)-> は paramを引数としたfunctionを表します。(簡単)
[1..n]は1からnまで配列を返します(素晴らしい!)


コンパイル

$ coffee -c hoge.coffee

このようにすると hoge.jsファイルが作成され、これはcoffeescriptに依存しないjsファイルなので、簡単に他のプログラムに組み込むことが出来ます。

よくわからないけど挙動を確認したい!

まあ、とはいっても最初はどういう言語かわかりにくいので、実際に触って確かめるのがいいです。
CoffeeScriptの上段メニューにある TRY COFFEESCRIPT には、リアルタイムでコンパイル結果を返してくれるインタプリタがあって、とても便利。


参考: 今日から始めるCoffeeScript | tech.kayac.com - KAYAC engineers’ blog

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

で、これがやりたかったんですけど、ExpressをCoffeeScriptで書くとどうなるか、ってことなんですが、それが冒頭のコードです。

# app.coffee #
express = require 'express'
app = express.createServer()

app.get '/', (req,res) ->
  res.send 'Hello World'

app.listen 3000


実行する

$ coffee app.coffee

今回、これだけではあまりありがたみが感じられませんが、学習コストが低く拡張性が高いCoffeeScriptを使うことで、大量のコーディングが必要になるであろうコーディングの負担を大幅に減らすことができます。
次の記事からはサンプルコードを CoffeeScriptで記述します。

募集!

エウレカではNodeJSで遊んでみたいプログラマを募集しています!