tumblr

tumblr(タンブラー)は、メディアミックスブログサービス。ブログとミニブログ、そしてソーシャルブックマークを統合したマイクロブログサービスである。アメリカのDavidville.inc(現: Tumblr, Inc.)により2007年3月1日にサービスが開始された。

node.js入門と自分的勘所 -nvmインストールからhello worldまで-


一ヶ月ほど前に少しだけ入門して、昨日から再入門したらいろいろ忘れてたし、クライアントサイドJSにはなかった慣れない部分を調べてみたので覚書。

node.jsでアプリを作ってみる

まずはnvmをインストールします。nvmはnode.jsのバージョン管理システムです。
node.jsはものすごい勢いで開発が進むことから、情報が陳腐化しやすいです。あるバージョンのnodeで動くアプリを作っても、数ヶ月後にその時の最新バージョンで試して動かないことも多々あるでしょう。そのため、node自身のバージョン管理、どのバージョンを有効にするかなどの管理が容易にできるのがこのnvmです。

$ git clone git://github.com/creationix/nvm.git .nvm

で.nvmディレクトリを作成し、その中にcloneします。

$ . .nvm.nvm.sh

でインストール完了。
あとはこのnvmを使って最新版のnode.jsをいれます。今現在の最新版は0.6.6です。
コマンドの引数にインストールしたいバージョンを入れれば、そのバージョンのものがインストール出来ます。

$ nvm install v0.6.6

インストールができたら、次回の起動時以降勝手にnvmを立ち上げるよう.bashrcに設定を追加します。

$ vim ~/.bashrc

. ~/.nvm/nvm.sh
nvm use "v0.6.6"


nvm,node.jsがインストールできたらnpmというパッケージ管理システムを使って適当にパッケージをインストールしてみましょう。npmは今はnode.jsに標準装備されているので、特別に何かが必要なことはありません。

$ npm install socket.io

socket.io@0.8.7 ./node_modules/socket.io
├── policyfile@0.0.4
├── redis@0.6.7
└── socket.io-client@0.8.7


上記のコマンドを打ち込んだディレクトリ内にnode_modulesというディレクトリが作成され、その中にモジュールがインストールされます。

  • gオプションを付けることで、.nvmディレクトリ以下にモジュールがインストールされ、しかもそのインストール先にパスが通るようになります。

$ npm install express -g


あとは何か適当に作ってみます。とりあえずExpressをつかってみましょう。Hello World表示するだけなアプリです。

var express = require("express"),                                                                                                                            
    app = express.createServer(),
    port = process.env.PORT || 3000;


app.configure(function(){
  app.use(app.router);
});

app.get("/", function(req, res){
  res.end("hello world!!");
});

app.listen(port);

ファイル名をapp.jsとして作成できたら、以下のように立ち上げます。あとはhttp://localhost:3000/にアクセスすれば「hello world!!」が表示されるはずです。

$ node app.js

node.jsの僕的勘所

自分的に引っかかったところ、慣れないところなど。


EventEmitter

node.jsがイベント駆動バリバリなのはどこでも言われてることだと思います。
登場するオブジェクトがほとんどonとかemitというイベントハンドラを持っていて、これはDOMで言うところの、HTMLElementsが持ってるaddEventListenerみたいなもんなのかと思いきや、組み込みのhttpとかfsモジュールのインスタンスはonとかemitとかって名前ではないイベントハンドラもっているので、なんか統一性なくて気持ち悪いなと思っていたのです。


で、よくよく調べてみたところ、このonとかemitとかっていうハンドラ以前に、イベントを生成するオブジェクトは全て組み込みのevents.EventEmitterのインスタンスだとのことです。なのでhttpやfsが持つイベントハンドラなどは、そevents.EventEmitterのメソッドをラップしてるもののようでした。
自分で独自のイベントを作成するモジュールを作る際も、このインスタンスを使用することになります。

参考:http://dl.dropbox.com/u/219436/node.js/handson/build/html/intro/programming_model.html


モジュール

クライアントサイドjsと違うポイントの1つにモジュールの読み込みができることがあります。
モジュールはrequireメソッドで読み込みが出来ます。

/**
 * counter module
 */

var counter = 0;

export.count = function(){
  console.log("counter: " + counter);
  counter++;
}

上記のようなモジュールを考えます。モジュール内ではexportsというオブジェクトを自動で用意します。このオブジェクトはrequireの戻り値になります。なので、このexportsにメソッドなど追加すれば、読み込み側でもそれが使えるようになるわけです。逆にexportsに追加しなかったものは読み込み側からはアクセスできません。この場合、countメソッドを呼び出してcounterの値をインクリメントできますが、直接counterという変数にアクセスはできません。


また、自作のモジュールを以下のように呼びだそうとしたときに失敗しました。

require("counter");

呼び出し側とモジュール自体は同じディレクトリにあり、counter.jsというモジュールの名前も間違っていません。これも色々調べてみたところ、モジュールの読み込みルールは以下のようになっているとのことでした。

require() では常にコアモジュールの識別名を優先的に解釈します。 例えば require('http') は、例え同名のファイルが存在していたとしても、常にビルトイインの HTTP >モジュールを返します。


指定された名前のファイルが見つからなかったら、 Node は指定されたファイル名に .js、.json、または .node を付けたものを読み込もうとします。

.js ファイルは JavaScript ファイルとして解釈され、 .json ファイルは JSON ファイルとして解釈されます。 一方 .node ファイルはコンパイル済みのアドオンモジュールとして解釈され、 dlopen を使って読み込まれます。

'/' から始まるモジュールは、ファイルへの絶対パスと見なされます。 例えば、 require('/home/marco/foo.js') は /home/macro/foo.js を読み込みます。

'./' から始まるモジュールは、 require() を呼んだファイルからの相対パスになります。 すなわち、 foo.js から require('./circle') によって circle.js を読み込むには、 circle.js は foo.js と同じディレクトリに存在していなければなりません。

'/' や './' が先頭になければ、モジュールは "コアモジュール" であるかもしくは node_modules フォルダから読み込まれることになります。


もし require() に渡されたモジュール識別子がネイティブモジュールではなく、かつ '/' や '../' や './' から始まらないならば、 Node は現在のモジュールの親ディレクトリに '/node_modules' を付与してそこからモジュールを読み込もうとします。

そこに見つからない場合はさらに親ディレクトリに移動し、モジュールが見つかるか root ディレクトリに到達するまで同様のことを繰り返していきます。

例えば '/home/ry/projects/foo.js' の中で require('bar.js') を呼んでいた場合、 Node は下記の位置を上から順番に見ていきます。

http://nodejs.jp/nodejs.org_ja/docs/v0.6/api/modules.html


僕が試した方法だと./がないために行けなかった模様。少しはまりました。