2012-03-01

Node.jsでメソッドベースイベント駆動のためのモジュール event-transceiver

2012-03-03 追記

本モジュール (event-transceiver) は、仕様をそのままに event-sign へと名称変更して再リリースしました。
本モジュールが提供する機能内容を考慮すると、旧名称では適切ではないと判断しての変更です。参考にされた方々へはご迷惑をお掛けします。

追記ここまで



最近書く node.js コードは、EventEmitter を使ってのイベント駆動でやるようしている。
それに伴って、イベント発生やリスナー設定を行う際のイベント名の指定を、文字列ではなくてメソッドで行うことができるモジュールを作った。

すなわち、

emitter.emit('done', value);
emitter.on('done', function (value) {
    console.log(value)
});

を、次のように実行できるように。

transmitter.done(value);
receiver.done(function (value) {
    console.log(value)
});

世間でもメソッドでイベントリスナーを登録するモジュールがあったりするが、それ自体を実現するためのモジュールというのは見つけることができなかったので、書いてみてリリースした次第。


イベントをメソッドベースで書きたい理由は単純で、「文字列で指定していると本当にペアになっているかいまいち分かりづらいから」。

イベント駆動でよくあるのが、起きるつもりだったイベントが起きなくて実行に失敗するという状況。もちろんテストでカバーしているつもりなものの、もしイベントがメソッドならば、実行時のイベント名ミスマッチが undefined function などで簡単に検出できるようになる。

通常の on('event') や emit('event') ならばコードを一目見ただけでイベント駆動だと分かるというメリットがあるので好みは分かれると思うが、個人的にはそれよりも「予想外の実行結果時に何が起きているのか分からない」シチュエーションを出来る限り回避できる方法を優先したい。


利用法としては、モジュールの define メソッドでイベントを定義して、イベント仕様を決定。コンストラクタ関数が返される。
それを使ってイベントのインスタンスを作成。インスタンスは transmitter と receiver という2つのオブジェクトのプロパティを持っていて、それぞれがイベント発生、リスナー登録の役割を持つ。そして、その2つのオブジェクトは先ほど定義したイベント名のメソッドを持つという仕組み。

var EventTransceiver = require('event-transceiver');
var MyEvent = EventTransceiver.define(['done', 'error']);

var event = new MyEvent();

event.transmitter.error(Error('このようにつかうのだ'));


より具体的なサンプルなどは上記 github の readme をどうぞ。

2011-12-09

Node Ninja に挑戦 2


前回初めて利用してみた Node Ninja に挑戦の第2弾でござる。

今回はオリジナルの Node プログラムを Git リポジトリに準備してそれをデプロイすると共に、リポジトリ更新によるサーバ自動更新までの動きを確認してみる。

Node Ninja の特徴の1つとして WebSocket に対応している……とは公式サイトには全く書いていないが、Twitter で @node_ninja のニンジャがそう言っているので問題はないだろう。そこで今回は Socket.IO を使って、WebSocket を利用するシンプルなサーバプログラムを準備してデプロイすることにする。


Socket.IO サンプルプログラム

今回の挑戦のために、上記リポジトリのようなつまらないサーバプログラムを準備した。
Socket.IO を使って断続的に通信が発生するため、そのままサーバを更新した際にどの程度ダウンするのかを確認することができる。


Node Ninja Machine へデプロイされるプログラムのルール

Machine 上で動作する Node サーバプログラムとして、現在のところいくつかのルールが設けられている。
  • (おそらくトップレベルに) server.js というファイル名でエントリポイントを準備する
  • config.json というファイル名で実行する Node バージョンを指定できる
  • package.json が機能するので、デプロイ時にモジュールを指定してインストールできる

個人的には、package.json が機能するのだから、それに従って (モジュールとして) サーバが動作するようにできるのではないかと思う。


Git リポジトリを指定してのデプロイ

さて、実際の Machine へのデプロイは以下の簡単なステップで完了する。
以下の例は、前述の Github 上のリポジトリを対象に行ったもの。Git リポジトリ URLは git://github.com/kumatch/sample-socketiopulse.git (読み込み専用) になる。
  1. Git Setup の Private タブで Add を押して、Git リポジトリURLを登録する
  2. 登録したアプリケーションが Private アプリケーション一覧に表示されるので、Install を押す
  3. Machine にデプロイが開始されて稼働開始

その上で、Git の Post-Receive Hooks を利用して、リポジトリが更新された (push された) 際にサーバを自動的を更新する仕組みが備わっている。
  1. Git Setup の AutoSync タブで、Git リポジトリの URL を入力して Create
  2. Post-Receive 用の専用 URL が発行される
  3. (例えば Github 上で) Post-Receive Hooks の設定として、先ほど発行された URL を指定する

これでデプロイ元となるリポジトリへ push すれば、Node サーバアプリケーションは自動更新される。
Git リポジトリ側の Post-Receive Hook がどのタイミングで行われるかにもよるが、push 直後に再デプロイが始まり、ほんのわずかなダウンタイムの後に新しいプログラムで稼働したことを確認できた。

デプロイした Machine の URL はこちら。

アクセス後、Chrome/Safari の開発ツール、ないし Firefox の Firebug のコンソール上で断続的に通信されているのが確認できる。IE は知らないが F12 を押そう。


どうやら現在のところ立ち上げることができる Machine は1つのアカウントで1つまでのようで、上記 URL を機能させ続けるならば、もう別の Node アプリケーションをデプロイすることができない。恐らく、後ほど上記 URL の Machine は停止させて頂くことになるだろう。



2011-11-30

Node Ninja に挑戦

この前の Node 勉強会で Node PaaS である Node Ninja (https://node-ninja.com/) のアカウントを頂いたにも関わらずさっぱり利用していなかったものの、それはさすがに勿体ないし、可能であればレポートを書いてねとも言われていたので、このたび挑戦してみることに。

実は PaaS 自体はこの Node Ninja が初体験。
稼働までの準備等は Web ブラウザによる設定コンソールにて行っていく。


Machine の作成

アプリケーションを稼働させるために、最初は Machine を準備する。正式サービスが展開されればおそらく性能を選択するようなことになるだろうけども、現在は固定になっている。今は指定項目としては Machine につける名前のみ。ここでは「kumasrv」とつけた。これで Machine の準備は完了。


アプリケーションのデプロイ

この作成した Machine に Node アプリケーションを設定する。現在のところ以下の4つの方法がある。

1. オフィシャルに提供されるアプリケーションから選択してインストールする。
現在はサンプルアプリケーションが準備されているが、今後は Node の有益なアプリケーションがボタン1つでデプロイできて Ready になるのだろうと予想できる。

2. 利用者による Shared アプリケーションから選択してインストールする。
オフィシャルではないものの、他のユーザによって提供されるアプリケーションパッケージの位置づけ。

3. プライベートアプリケーション。選択ではなく、自身で Git リポジトリを指定してでデプロイする。また、ここで追加したアプリケーションを Shared 化することができる(最初はちょっとわからなかった)

4. AutoSync。Git リポジトリから展開した上で、そのリポジトリが更新されると自動的に git pull してくれる。逆にいえば、これまでの3つの方法ではデプロイ後は基本的に「そのまま」となる。ただし、先に設定した SSH key で Machine に接続して、コンソール上でコードを変更したりサーバの起動をしなおしたりといったことは可能。


オフィシャルアプリケーションをデプロイ

今回は、オフィシャルなアプリケーションを展開してみる。hello-http という、おそらく hello world なアプリケーションがあったので、それを選択。Logs 画面に遷移した後、Machine が準備される様子(ログ)が追加されていく!

完了後、kumasrv.node-ninja.com へブラウザで繋いでみる。あれ、繋がらない。どういうことなの、ってことで Machine に SSH で接続する。プロセスを確認すると、Nodeプロセスは存在するものの httpd のようなものは存在していない。
そういうものなのかなーニンともカンともとか言いつつ、実行プログラムの中身を覗いてコードを確認。8080 番で動いていることがわかったので、 kumasrv.node-ninja.com:8080 で接続。無事「Hello World」が表示される。

その後アプリケーション選択ページを見てみると、説明文に「ポート 8080 でHTTPのリクエストを待ち受け」ってちゃんと書いていた。てめえがニンともカンともだった。


別のオフィシャルアプリケーション (express + giraffi) も試す。こちらは普通に80でアクセスすることができた。こちらもコンソールからソースコードを覗くと、たしかにそのまま 80 番で待ち受けるよう記述されていた。ユーザにはそういう権限がある模様。

Machine Logs ページ上ではこのアプリケーションのデモとして、アプリログの様子が記録されているのが分かる。どうやらこれがカラクリの中身らしい。



その他雑多なところ

  • Machine 上には mongodb が動いている。情報はないがデモでは普通に使っているし CLI でも接続可能。
  • 作成した Machine を削除して再度別名で作成した際、同じ IP が割当てられたにも関わらず名前割当ては古いままだった。切り替わるのに結構な時間を要したのでこの辺はまだまだ調整が必要そう。


今度は自前アプリケーションのデプロイに挑戦する予定。

2011-10-03

node.js のモジュール配置について 2011 秋

今のところこれでいこうという形ができたので、現時点のものを一旦まとめるというお話。


モジュールの読み込み優先度

http://nodejs.jp/nodejs.org_ja/docs/v0.4/api/modules.html#loading_from_node_modules_Folders

この項に書いている話ですべてではあるものの改めて書くと、
ある js ファイル上で require() を使ってモジュールを読み込もうとする際、指定した path が「/」「./」「../」といった指定の仕方ではないならば、その js ファイルからルートまでのすべてのパス上に存在する *node_modules* というディレクトリ内がモジュール読み込み対象となる。もちろん、深いレベル (読み込み元 js ファイルに近い) ほど優先度が高い。

/path/to/app/app.jp で require() したならば、次に示す順のディレクトリ内モジュールが検索される。

  1. /path/to/app/node_modules
  2. /path/to/node_modules
  3. /path/node_modules
  4. /node_modules

もちろん、require() で指定する path が階層構造でも問題なし。上の例で、require('my/app/foo_module.js') という指定をしたならば、モジュールとしてのファイルは以下のように検索される。

  1. /path/to/app/node_modules/my/app/foo_module.js
  2. /path/to/node_modules/my/app/foo_module.js
  3. /path/node_modules/my/app/foo_module.js
  4. /node_modules/my/app/foo_module.js

基本的にはこれさえ分かっていれば、容易に共有/固有のモジュール領域が作れる。


ソフトウェアごとのモジュール配置

私は現在のところ、Nodeで開発しているソフトウェア (ex: foo_project) のモジュール配置は次のように行っている。

- foo_project
    - app
        - app.js
        - node_modules
            - (ソフトウェア固有ドメインのモジュール)
    - node_modules
        - (npm 等で導入される共有モジュール)
- (node_modules)

foo_project として実装されたソースは app ディレクトリ以下に配置、実行するようにし、直下にそのソフトウェア固有ドメイン向けのモジュール領域を作成する。開発したドメインモデル等はすべてこの app/node_modules 以下に設置する。
一方、npm 等で配布されているパッケージや会社資産などのライブラリモジュール (これも package として社内配布するとよい) は、先ほどの固有ドメイン向けの領域より一段上に配置する。本質的には読み込み優先順位を下げる必要はないが、ツリー自体の可読性と固有ドメインとの分別説明のために明確な分離を行う。
なおこれ以上分離が必要な場合は、さらにその上位にモジュールディレクトリを準備する。分離したい数だけ、上位ディレクトリに node_modules ディレクトリを作成すればよい。制限はただ一つ、ルートディレクトリにたどり着くまで。



npm による任意モジュールディレクトへのパッケージ導入

目的の node_modules ディレクトリへ移動した後に npm install を行えば、そのディレクトリへパッケージを導入できる。
npm は現在のところ対象となる node_modules 内パッケージディレクトリの内容のみで管理しているように思われるため、目的のディレクトリ内におけるパッケージの追加や削除も容易に行うことができる。


「require.paths」の削除

以前は require.paths という組み込み配列に任意のパスを push することで、モジュールの読み込み対象ディレクトリを増やすことができた。しかし、現在ベータバージョンである v0.5 系では削除されている。元々 v0.4 系リリース時点のドキュメントで「いつか消すから止めとけ」という明記がされていて、いつ完全削除されるものかと思っていたものの、案外早く対処されたという印象。

コード上から容赦なくパスを操作できてしまうのはトラブルの元だし不安定な挙動を引き起こす可能性が高いということで、なくなって正解だったと思われる。何より、現在の仕組みはシンプルながらも強力で融通も利く。

2011-02-08

Windows 版 Safari の file drop event がヘン

以下のような、ブラウザ外からファイルを Drop することを想定しているページがあるとする。
ここでは、ファイルを Drop すると drop イベントとして「dropped.」と、利用したファイル名を表示。

<html>
 <head>
  <title>Drag & Drop test.</title>
  <script type="text/javascript">
    window.addEventListener('dragover', function(event) {
        event.preventDefault();
    }, false);
    window.addEventListener('dragenter', function(event) {
        event.preventDefault();
    }, false);

    window.addEventListener('drop', function(event) {
        event.preventDefault();
        alert('dropped.');

        var file = event.dataTransfer.files[0];
        if (file) {
            alert('file = ' + file.name);
        }
    }, false);
  </script>
 </head>
 <body>
  <div>Drop a file.</div>
  <br />
  <div>If you use safari on windows, drop a unders dummy link first, and drop a file next.</div>
  <div>
    <a href="#">a dummy link.</a>
  </div>
 </body>
</html>

Firefox, Chrome, Safari で動作することを想定しているけども、Windows 版 Safari ではファイルを Drop することができない。

しかし、同じページ内にあるリンク (もしくは画像があればそれでも OK) を掴んでページ内へ Drop すると Drop イベントが発生し、さらに何故か以降のファイル Drop が成功するようになる。

実際にその動きが確認できるページはこちら。
http://hatotech.org/labo/drop.html


ちなみにこの現象は他の「ファイル Drop 機能を提供しているページ」でも見ることができる。例えば Google Docs。

何が起きてるんですかね。ブラウザと Javascript に詳しい人教えてください。

2010-09-07

Mac における円とバックスラッシュ

非 Mac 環境から Mac 環境へ移ると、いろんな差異が発生しますね。
Emacs で正規表現を書いていたところ、なぜかエスケープシーケンスがちっとも効かない。

/(.+)[¥s]+(.+)/ # 実際はすべて半角

こんなシンプルな抽出なのにちっとも機能しない。どういうことだと色々悩んでいたところに、1つの疑惑が。

「ひょっとすると円とバックスラッシュは別コードで示される文字なのでは。」

別所(ターミナル)ではそのまま表示されているバックスラッシュをコピーして Emacs に持ってきたところ、しっかりと画面上にはバックスラッシュが掲載される。そのまま正規表現に利用すると見事マッチ。

で、改めて調べたところ、Mac の日本語キーボードでは以下のような入力になるとのこと。

  • 円は「¥」キー
  • バックスラッシュは Option +「¥」

Windows, Linux で今まで当たり前のように使っていた「エスケープ等に利用するためのバックスラッシュ」は円とイコールの扱いだったし、キー入力も¥キー1つでOKだった。それが Mac ではこの違い。そして何が驚いたって、これが Mac ユーザには「バックスラッシュは Option +¥ で入力すべき文字」ということが当然のことだったという。

「じゃあ円(¥)は何に使うの?」と聞くと、「少なくともコードには必要ない」ということだったので、既に導入済みのキーボードカスタマイズソフト KeyRemap4MacBook で¥キーでバックスラッシュが入力されるように変更したのでした。Windows の窓使いの憂鬱並に、既にマストソフトウェアです。

2010-09-03

株式会社フィードテイラーに入社しました

ご無沙汰しておりました。
以前から表立っての活動やアウトプットがめっきりなくなってしまっていましたが、このたび記事を書くべき報告がありまして帰ってきました。

9月1日から株式会社フィードテイラーで働くことになりました。

同社は iPhone/iPad アプリケーションの開発を主力事業としてやっておりますが、私は主にサーバサイドのシステム構築、実装に携わることになります。これまで行ってきたWebアプリ開発の経験を活かしつつ、これからも日々邁進していくことで成長し、それが会社自身の成長に繋がればと想う次第です。


本当に近頃どうしているのかさっぱり分からない状況ではありましたが、これからは少しは何かしら表立っての活動があるかもしれません(?)

振り返れば色々あった時期もありましたが、もう少し気楽に、楽しいことが続けられれば、仕事もプライベートも充実した日々が送れるかなと。きっと。それに先駆けて自宅の開発マシン用にと MacBook Pro を買いました。はじめての Mac です。知らないソフトは多いですが結局 Unix マシンなので楽しく使っております。

それでは、今後ともよろしくお願いします。