2012-09-19

フロントエンドパッケージマネージャ bower のメモ


Twitter が先日リリースした、Javascript や CSSなどのフロントエンド向けパッケージマネージャ Bower (http://twitter.github.com/bower/) についての簡単な下調べを行ったときのメモ。


■ Git リポジトリを指定しての導入


bower install コマンドで指定できるのは名前だけじゃなくて、git リポジトリの URL を指定することができる。

  $ bower install git://github.com/kumatch/asyncall.git

末尾にシャープ付きでバージョンを指定すると、ちゃんと git タグのバージョンが使われる(シェルによっては # をバックスラッシュでエスケープすること)。みんなでちゃんとバージョン付けをしよう。

  $ bower install git://github.com/kumatch/asyncall.git#0.1.1


これら方法で導入したパッケージも、bower update で最新のものに更新されることを確認した。



■ component.jsonを使ってのパッケージ管理


component.json ファイルを自身のプロジェクトリポジトリに置いて、そのプロジェクト内で利用しているパッケージ群を定義することができる。

{
  "dependencies": {
    "jquery": "~1.7.2",
    "asyncall": "git://github.com/kumatch/asyncall.git"
  }
}

component.json のあるディレクトリ(あるいは bower 側で辿ることができる場所。マニュアル参照。)で bower install を実行すれば、定義しているパッケージ群を一気に導入できる。


先ほどのように、git リポジトリ URL でのバージョン指定も OK。
注意点としては、dependencies に定義するキーの値は、導入パッケージ側の "name" 要素で指定されている名前とあわせること。そうでないと、導入後の list コマンドでおかしなことになった。



■ bower で導入されることを想定しているコンポーネントの依存性定義


例として、次のような component.json を含んだ git リポジトリがあるとする。
(ここでは git://github.com/kumatch/bower-samples-nop.git とする)

{
  "name": "sampels-nop",
  "version": "0.1.0",
  "main": "./lib/index.js",
  "dependencies": {
    "asyncall": "git://github.com/kumatch/asyncall.git"
  }
}


一方、自身のプロジェクト側の component.json で、このパッケージを利用するよう定義。

{
  "dependencies": {
    "sampels-nop": "git://github.com/kumatch/bower-samples-nop.git"
  }
}


bower install を実行すると、samples-nop パッケージと一緒に、依存している asyncall が導入される。

$ bower list
/path/to/bower-test
├── asyncall#0.1.2
└─┬ sampels-nop#0.1.0
  └── asyncall#0.1.2


$ bower list --paths
{
  "asyncall": "components/asyncall/asyncall-0.1.2.min.js",
  "sampels-nop": "components/sampels-nop/lib/index.js"
}


bower-samples-nop.git リポジトリの component.json を package.json としていても、問題なく(依存性を解決して)導入することができた。従って、npm パッケージとして用意されていて、かつブラウザでも利用可能なもの (Underscore.js とか) は普通に依存パッケージとして指定することができて、導入時にも問題なく解決される。



■ 導入パッケージのエントリポイントをスクリプトで得る


次のようなコードで paths にオブジェクトで受け取ることができた。まだ何も試していないけども、色々と自動化したいならば必要になってくると思う。

var bower = require('bower');

bower.commands.list({ paths: true }).on('data', function (paths) {
    // console.log(paths);
    // …
});

2012-09-11

asyncall.js

https://github.com/kumatch/asyncall


ブラウザ、および Node.js 上で javascript 関数の非同期実行を行うライブラリです。
いろんな環境下でも変わらず使えるものが欲しかったので、簡単ながら書きました。

引数に関数を与えると、たた単純に非同期で実行されます。

asyncall(function () {
    console.log(1 + 2);
});

次にあげる順の方法を使って実行します。

  1. setImmediate (IE 10, および Node v0.9 以上)
  2. process.nextTick (Node v0.8 以下)
  3. MessageChannel (WebKit 系)
  4. setTimeout (それ以外)


網羅できるテストが思いつかないので、テストが書けてないです。

2012-03-04

いかにしておっぱい画像をダウンロードするか〜2012 for Node.js


いかにしておっぱい画像をダウンロードするか〜2012 の Node.js 実装です。
こういう動機で頑張るっていうのはいつまでも少年時代であろうとしていていいよね!うちの場合もそれをゲームへ向けたわけだし。

動作には npm で request と async を導入する必要あり。
Node.js の場合、何も考えずに書いてしまうと逆に並列処理が走り過ぎて落ちて終わってしまうという。
ということで async を使って並列処理数の制限を行っている。

2012-03-06 追記
ファイルが既に存在している際に callback が呼ばれずに次のタスクへ進まないという不具合があった。コード修正。


var request = require('request');
var querystring = require('querystring');
var async = require('async');
var crypto = require('crypto');
var fs = require('fs');
var path = require('path');

var appid = '';
var uri = 'http://api.bing.net/json.aspx?';
var dir = './data';

var page_count = 0;
var download_count = 0;

var md5hex = function (str) {
  var md5 = crypto.createHash('md5');
  md5.update(str, 'utf8');
  return md5.digest('hex');
};

(function Oppai() {
  var offset = page_count * 50;
  var params = querystring.stringify({
    Appid : appid,
    Version : '2.2',
    Markert : 'ja-JP',
    Sources : 'Image',
    'Image.Count' : 50,
    'Image.Offset' : offset,
    Adult : 'off',
    Query : 'おっぱい'
  });

  request(uri + params, function (err, res, body) {
    if (err) throw err;

    var ref = JSON.parse(body);
    if (ref.SearchResponse.Image) {
      var results = ref.SearchResponse.Image.Results;
      async.forEachLimit(results, 10, function (result, callback) {
        var url = result.MediaUrl;
        if (url.match(/\.jpg$/)) {
          var filename = md5hex(url) + '.jpg';
          var filepath = dir + '/' + filename;

          path.exists(filepath, function (exists) {
            if (!exists) {
              var output = fs.createWriteStream(filepath);
              output.on('close', function () {
                callback();
              });

              download_count += 1;
              request(url).pipe(output);
              console.log(download_count + ' : Download... ' + url);
            } else {
              callback();
            }
          });
        }
      });

      page_count += 1;
      process.nextTick(Oppai);
    }
  });
})();

2012-03-03

[おわび] Node.js 用モジュール event-transceiver は event-sign へ名称変更して再リリースします


先日公開した、Node.js でイベント駆動の支援モジュールである event-transceiver を、event-sign という名称へ変更して再リリースすることにします。

https://github.com/kumatch/node-event-sign

このモジュールが提供する機能として、以下の特徴が根本にあり、全てでした。
  • ある関数によって取り扱われるイベント名を事前に定義する
  • 定義イベントをメソッドベースで扱うと共に、非定義イベントは利用できない
こういった特徴を考慮した場合、transceiver という「送受信」をイメージするような名称は本モジュールには適切ではなく、むしろイベント定義を行った上で仕様に基づく振る舞いを厳密化するという特徴をイメージできるものへ改名するほうが良いと判断しました。

npm 提供パッケージは名称がかなり重要なものですので、その名称を変更するのならば、一般利用されている可能性が最も低いであろう今のほうがよい、早いに越したことはないと判断し、この再リリースに至りました。
実際のところこのモジュールがこれ以上機能拡張されることは考えにくいので、別に前の名称のままフリーズでも良い気もしましたが、前述の通り時間が経てば経つほど変更できない状況になりますので。

既に旧モジュールをチェックされていた方にはご迷惑をおかけします。


なお、本当に旧モジュールが既に組み込まれてリリースされたような箇所は自分以外のものではないと思いますが、再リリースにあたっての仕様変更はありません。念のため。

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 が割当てられたにも関わらず名前割当ては古いままだった。切り替わるのに結構な時間を要したのでこの辺はまだまだ調整が必要そう。


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