2013-12-08

browserify を使い始めるためのファーストステップ 2013 冬

browserify は、ブラウザ JavaScript のためのコードを、Node スタイルのコード(Node 向けの JavaScript コードを書いているかのような形)で記述しておいて、ブラウザ上で利用できるような Javascript コードとして出力してくれるツール。
require() で利用しているモジュール群をすべてアナライズして、それらモジュールコードまですべて都合よい形で同梱してくれる。

リリース当初(3年くらい前)は Node で HTTP サーバを建ててブラウザ用 Javascript コードを返すような仕掛けから始まったはずで、そこから静的ファイルを出力する仕組みが追加されて、現在ではむしろ後者の方がメイン扱いになっている感じになっている。
そうなった背景として、Grunt の登場でその辺りの機能が有効的に参照利用できるようになったり、実際に Grunt を使ってのビルド環境がカジュアルに行われるような土壌が出来上がったからというのが大きいのかもしれない。

今から browserify の利用を始めるべく browserify 側の情報をみても、低レベルなところが中心で戸惑ってしまい躊躇してしまうかもしれない。どんなものかを知るために動かすならば、簡単に利用できる Grunt 経由側のものを知ってしまったほうがよいと思う。
ということで、簡単なサンプルを紹介。

今回利用するファイルとツリーの状態は以下の通り。



------------

まずは環境作りのための package.json。
モジュールは grunt と、grunt で browserify するための grunt-browserify。
あとはサンプル用スクリプトで使うモジュール例として async.js を指定している。



記述後、npm install を実行する。

続いて、ブラウザ上で動作させるための JavaScript コードを Node スタイルで記述。
以下のサンプルでは、core モジュールと npm で入れたモジュールを require() で利用しつつ、エントリポイントとなるスクリプトから自前のファイルを require() して読み込んでいる。





browserify を実行する Gruntfile.js を用意。



grunt を実行すると、build.js が出力される。
中身を覗いてみると、browserify が頑張って構築してくれたブラウザ用 JavaScript コードとして、先ほど自分で記述したコードやアナライズした結果同梱する必要ありと判断された util.fomat や async.js などが含まれている。

適当な HTML ファイルを用意して、この build.js を script タグで読み込むようにする。



実行デモ


先ほど browserify v3 がリリースされたばかりで、記念的な感じで。

上記例は v2 系を動かした結果 (grunt-browserify がまだ v2 を使うため)  なものの、恐らくただ単純に使う分には v3 以降も違いなくいけるんじゃないかと(個人的な期待)。

2013-02-19

MongoDB イン・アクションを頂きました


2012年末、勉強会後の懇親会で玉川竜司さん (@tamagawa_ryuji) と初めてお会い/お話することがあり、その際に話の流れで

「NodeでのストレージにはMongoDBを使う事が多くて触ってるんですけども、小規模なら簡単でも、中大規模になると途端に難しくて…」

みたいなことを言っていたら、

「今度 MongoDB の翻訳本が出るから、これもなにかの縁だし送ってあげるよ」

と言われ、そんなこと初めてだし、わーそうなったらいいなあなどと思っていたら、本当に郵送で送っていただきました。やたー!



……などと、年末の話だったにも関わらず、年明け早々いろんなことがあったせいで今頃のエントリで大変申し訳ございません…。

 
 

ということで、MongoDB イン・アクション (http://www.oreilly.co.jp/books/9784873115900/) の紹介です。
海外ではいくつか出ている MongoDB の書籍ですが日本では単独でのものがなく、これまで MongoDB の日本語情報といえば利用者によるブログ、勉強会、および公式ドキュメントでの翻訳を頼りに進めてきていた感じでした。(でもそれらが比較的充実していたり、はまりやすいポイントをいち早く紹介されていたりと、比較的恵まれた環境だったと思う)

MongoDB イン・アクションでは、MongoDB の基本的なお話、特徴、および利用法について1から紹介していきます。
中身は大きく3つの部に分かれていて、「1. はじめての方のための MongoDB」「2. ショッピングサイトシステムを実例にした利用方法の紹介」「3. 本格運用する際に抑えておくべき要素」となっています。

1, 2 では所々 RDB と比較して「RDB ならばこうだっただろう、MongoDB ではこうするんだ」といった具合に解説が入るので、既に RDB を利用している人であれば、MongoDB におけるアプローチ方法や差異などを把握しながら読んでいく感じになります。リレーショナルなデータであったものについて、RDB で正規化していた事に対して MongoDB での対非正規化であったりそれに伴う設計の流儀などが、2 のショッピングサイトシステムを例として紹介されるので、2 までを読み終えた時点で、簡単な CRUD アプリケーションならば作れてしまう程度に知識はついているんじゃないでしょうか。

3 では、データベースを扱っている人ならば気になるであろう、クエリ最適化(インデックスの話)、レプリカ、シャーディング、バックアップとリストアなどを扱っています。
冒頭に書いているとおり、MongoDB は簡単なデータ構造で規模も小さいアプリケーションならば本当にシンプルに扱えるものの、取り扱うデータが大きくなったり構造が複雑になったりすると知っておかなければならない話が色々あったりします。特にクエリ最適化とシャーディングあたりが重要(そしてはまりやすい)だと思うんですが、きちんとそのあたりが構築例、動作例含めて紹介されているのが良い感じです。

 

全体的にテンポ良く読み進めていける本です。目的に対するクエリ例を逐一紹介してくれるのが嬉しい。
ちょっとだけ気になった点としては「突然紹介され始める○○」みたいなのが何回かあったような。でも変な感じではないので大丈夫。

完全に知識がつくまで、これを見ながらシステム構築する感じなりそう。



以上、MongoDB イン・アクションの紹介でした。
玉川さん、およびオライリージャパンさん本当にありがとうございました。

2013-02-06

新しい Windows PC を組んで Win8 環境にした ー 2013春


最近パソコンの普段使いは完全に Mac に移っているものの、Windows マシンはゲームのために一応持ち続けている。

もうずいぶんと昔に買ったマシンパーツから入れ替えしながらやってはきていたけど、それも 2009年1月 (Warhammer Online のためにビデオとCPUを変えた) を最後に進化は止まっていて、壊れた電源だけは変えたものの、性能的にはもうずいぶんと置いてけぼり状態だった。OS も未だ Windows XP だった。

で、シムシティの新作がこの春に出ることと、近頃の為替に比例してパーツの値段がモリモリ上がっていっているのを眺めていてもう我慢ならん!ということで久々に一機買いしてみた。
  • CPU: Intel Core i7 3770
  • Cooler: ENERMAX ETS-T40-TB
  • RAM: 8GB x 2
  • Mother: ASUS P8H77-V
  • GPU: 玄人志向 GF-GTX660-E2GHD/DF/OC
  • Storage: Intel SSD 335series 240GB
  • Case: Corsair Obsidian 550D

しめて 89,796 円。電源だけ流用。
DVDドライブは、必要なときにUSB接続 (IDE/SATA変換) すればよいと思って買ってない、というか最近はマザーに IDE 端子ついてないんだね。おじさんびっくり状態。
そのくせにデカいケース買ってどうすんの状態だけども、ケースはメンテナンスしやすいに越したことはない。最近の良いケースは本当によく出来ている。これまでの苦労が嘘のようだ。
 

OS は Windows 8。さすがにこの時期から 7 はないだろうということで。(8 が良いものかどうかは置いといて)

以下、 Windows 8 導入後に行ったときの作業メモの写しを紹介。
前述のとおり、メインは Mac で Windows はゲーム用途ということなので、実はそこまでやる事はない。


キーカスタマイズ

利用している Real force にあわせて、レジストリを操作することで低レベルでキーポジションを変更する。

参考:

HKEY_LOCAL_MACHINE/System/CurrentControlSet/Control/Keyboard Layout
という領域に、新たに Scancode Map というバイナリ値を新規作成。

以下、バイナリ値として次のように設定した。
今回は Ctrl を A の左に、および ESC と半角/全角を入れ替えるというシンプルな変更に留めている。

0000 0000 # ヘッダ
0000 0000 # ヘッダ
0400 0000 # 以下に続くエントリー数を定義。終端のNULLのぶんも含める
1D00 3A00 # 1. CapsLock を Ctrl に
0100 2900 # 2. 半角/全角 を ESC に
2900 0100 # 3. ESC を 半角/全角 に
0000 0000 # 4. NULLによるターミネータ


ショートカットキー

Emacs のショートカットが欲しいので xkeymacs をいれた。


近年ついに64bit版の対応がなされた ( http://d.hatena.ne.jp/kfujieda/20110428/1303924737 )  とのことで、本当に有り難いお話。

XP の頃は窓使いの憂鬱を利用してキー入れ替えやショートカット変更をしていたが、設定ファイルをゴリゴリ書くほどのカスタマイズはもうしないだろう(テキストを書くような作業を Windows に求めていない)ので、のどか ( http://www.appletkan.com/nodoka.htm ) や yamy ( http://sourceforge.jp/projects/yamy/ ) の採用は見送った。ブラウザのアドレスバーで C-a や C-h が効いてくれればそれでよい。


Dropbox

Dropbox のサイトからプログラムをダウンロードしてセットアップ、既存アカウントを設定。気づいたら同期完了していたので出来るやつだ。


Skype

for Windows8 を Windows Store から落とせと言われたが、レビューを見る限り使い勝手が悪いことばかり書いている。
なによりストア利用のためのアカウントを(多分持っているけど)用意するのが面倒だったので、別プログラムとして Skype サイト上で用意されていた Windows Desktop 版というものを入れた。いつもの Skype だった。


Steam

Steam のサイトからプログラムを落としてセットアップ。新しいマシンでの初ログインではメールによる確認キーを求められる。一手間かかるけども安心できる。
早速 Civ5 をいれて DirectX 11 版で起動、その速度をみて満足する。(ちゃんと遊んで、八方美人具合に度が過ぎたのかお隣の大国から喧嘩売られて散る)


Diablo3

いれてない。

2012-10-19

依存するモジュールも解決できる Node/AMD (サーバ/クライアント) 共通化モジュールを書く



このエントリは、 東京Node学園祭 2012 アドベントカレンダー 5日目の記事です。


■ 前置き - AMD とは


AMD (Asynchronous Module Definition) は、Javascript のコードをモジュールとして定義して、非同期ないし遅延ロードするための仕組みです。

http://wiki.commonjs.org/wiki/Modules/AsynchronousDefinition (現在、接続が遅い模様)

CommonJS により提唱されたものですが、昨年あたりからクライアントサイド (ブラウザ) で JavaScript モジュールを構築する仕組みとして各所で一気に取り上げられ、現在ではクライアントサイドの主要なライブラリでもサポートされてきている(AMD によるモジュールとして利用できる)状態にあります。

モジュールに依存性を指定する仕組みも用意されています。遅延ロードされますので、依存モジュールの読み込みが完了次第、目的の(依存元の)モジュールの定義処理が実行されます。
比較的カオスになりがちなクライアントサイド Javascript が、このモジュール化によりすっきりしたものになります。

また処理は非同期で行われるため、ブラウザ上での読み込みの速度向上が見込まれます。実際に、Twitter の Web ページの高速化にも一役買ったとの話があります。

Twitter Engineering Blog: http://engineering.twitter.com/2012/05/improving-performance-on-twittercom.html
Publickey による日本語紹介: http://www.publickey1.jp/blog/12/twitter51.html

クライアントサイドで AMD を利用するには、AMD 機能を提供してくれるライブラリを用います。たとえば、 RequireJS があります。

http://requirejs.org/



■ サーバサイド/クライアントサイド Javascript のモジュール共通化


もし仮に Web アプリケーションのサーバ実装を Node.js で行っていて、かつある処理をサーバ/クライアントのどちらでも行いたいとするならば、その処理を行うコードを改変することなくサーバ/クライアントどちらでも実行できればという考えが出てきます。

そこで、その処理のコードを Node モジュールかつ AMD モジュールとして実装しておき、環境にあわせて適切にモジュールとして読み込まれるようにします。


今回は例として、「その年の 1/1 から今日までの日数の回数 hello world を出力する」というつまらない機能を、Node/AMD 共通モジュールとして作ります。

この問題に対して、Lo-Dash (http://lodash.com/) と Moment.js (http://momentjs.com/) の2種類のライブラリを使おうと考えました。
Lo-Dash, moment ともに Node モジュールとして npm で導入できると共に、AMD モジュールとしても利用できるもので提供されています。

実際に実装したコードが次のものです。

(function(define) {
    define('hellodays', ['lodash', 'moment'], function (_, moment) {
        function hellodays() {
            var days = Number( moment().utc().format('DDD') );
            _.times(days, function (d) {
                console.log('Hello, world ! (' + (d + 1) + ' days)');
            });
        }

        return hellodays;
    });
})(
   // 1: AMD
   typeof define === 'function'  ? define :

   // 2: Node
   typeof module !== 'undefined' ? function(id, deps, factory) {
       module.exports = factory.apply(this, deps.map(require));
   } :

   // 3: Plain (window object)
   function(id, deps, factory) {
       var dependencies = [ this._, this.moment ];
       this[id] = factory.apply(this, dependencies);
   });


2行目で実行している define は、AMD API の仕様に基づき Javascript モジュールを定義するための関数です。引数として AMD 内モジュール名 (以後、id と呼びます)、依存モジュールのリスト (以後、deps と呼びます)、およびモジュールの構築を行う関数 (以後、factory と呼びます) を指定します。
モジュール構築関数 factory 内でオブジェクトあるいは関数を返すと、それがモジュールとして利用できるようになります。ここでは、実際に 1/1 から現在日までの日数を算出し、その回数分 hello, world を出力する処理を記述しています。

実際に Node/AMD モジュールのためのコントロールを行っているのは、1行目から定義している即時関数の引数として与えている、13 行目からのコードです。若干分かりやすくなるよう、コメントを明記しています。

もしグローバル空間に define 関数が定義されているのであれば、AMD によるモジュール機構を備えているため、そのまま define を即時関数に与えて実行させるようにします。これで、AMD モジュールとして hellodays が利用できるようになります。

AMD define 関数はないものの、module が定義されているのであれば Node のモジュール機構と見なし、define のような振る舞いをする関数を与えます。
define への引数はモジュール名 id、依存するモジュール名のリスト deps と、モジュール構築のための関数 factory が与えられるわけなので、Node の require を使って deps で指定されている依存モジュールを読み込み、得られたインスタンスを factory の引数として与えます。factory を実行すると構築された関数が返されるので、それを module.exports にセットします。これで、Node でこの hellodays モジュールを require すると、factory で作られた関数が得られます。

最後は、AMD でも Node でもないパターンです。これまでのブラウザ用 Javascript ライブラリであれば、window オブジェクトへ割り当てるくらいはしていることと思います。lodash, moment もそれぞれ _, moment という名称で参照可能になっていることでしょうから、それらを factory の引数に与えてモジュール構築を行います。名称として id が define の引数で渡ってくるので、その値で window オブジェクトの要素の1つとして定義しています。


以上、AMD > Node > plain という優先度でモジュール定義を行うことができるようになりました。
状況にあわせて依存モジュールの実体であるインスタンスが適切に factory に渡っている、factory の結果が利用されている、あたりがポイントになります。




■ 共通モジュールを利用する


サーバサイドである Node ならば次のように利用します。
普通の Node モジュールの利用となんら違いはありません。(require で読み込めるようモジュールパスが解決されている場合)
var hellodays = require('hellodays');
hellodays();

一方クライアントサイドの場合は、AMD が利用できるよう RequireJS を使います。
上記で作成したモジュールが読み込まれ、hellodays 変数として参照可能になってやってきます。
<script src="./scripts/require.js"></script>
<script>
  requirejs.config({
      paths: {
          "lodash": "./scripts/lodash.min",
          "moment": "./scripts/moment.min",

          "hellodays": "./scripts/hellodays"
      }
  });
  requirejs(['hellodays'], function (hellodays) {
      hellodays();
  });
</script>

Jam (http://jamjs.org/) などのクライアントサイドパッケージマネージャを使うと、依存先となるモジュールの導入や、AMD (RequireJS) で読み込むパスの管理などを一手に引き受けてくれるので、もう少し楽に利用できるようになります。
$ jam install lodash
$ jam install moment
<script src="./jam/require.js"></script>
<script>
  requirejs.config({
      paths: {
          "hellodays": "./scripts/hellodays"
      }
  });
  requirejs(['hellodays'], function (hellodays) {
      hellodays();
  });
</script>

最後に、クライアントサイドで AMD を利用しない場合です。必要なファイルをすべて読み込んだ後、直接 hellodays 変数の関数を実行します。利用の機会はほとんどないと思いますが、利用すること自体は問題はありません。
<script src="./scripts/lodash.min.js"></script>
<script src="./scripts/moment.min.js"></script>
<script src="./scripts/hellodays.js"></script>
<script>
  hellodays();
</script>



■ 共通モジュールとして用意する対象について


今回依存モジュールとして利用した Lo-Dash や Moment.js のような、サーバ/クライアント関係なく汎用的に利用できるライブラリなどは、共通するモジュールとして利用できると大きな価値がありそうです。他にもフローコントロールやイベント管理などは同じように共通モジュール化の価値があると考えられます。

一方、自前で提供するサービスのためのコードではと考えると、データの正規化 / 妥当性確認 (Filter / Validator) ルールの共通モジュール化に一定の価値がありそうです。
例えばユーザ情報の更新処理において、あるルールに基づいた妥当性確認をサーバサイドだけでなくクライアントサイドでも行って確認結果をいち早く掲示したい、といった要件がある場合。これまでなら同一ルールながらも異なるコードで実装したり、妥当性確認処理を非同期で逐一サーバと通信して行ったりしていたところです。
共通モジュール化されているならば、妥当性確認処理をクライアントで行い、サーバへ POST されてきた情報も同じモジュールで再度確認する、といったことができます。




■ 備考. テストについて


共通モジュールとして実装されたコードのテストをどうするか?

現在の私の場合はですが、まずは使い慣れた環境による、提供されうる機能のテストを書きます。
すなわち、Node モジュールとして提供されて、Node 上でなんら問題なく利用できるである確信のためのテストを用意します。
その上で、AMD モジュールとして読み込んで利用可能な状態になるかのテストを用意しています。

Node モジュールの amdefine (https://github.com/jrburke/amdefine) を使うことで、Node 上で define を使ったコードが一時的に利用可能になります。
これを用いてモジュールをロードし、読み込んだモジュールが不自由なく(そこまでの機能テストは省略しつつ。機能の妥当性は前述のテストコードで満たされているはずなので。)利用できれば、良しとしています。




■ 備考2. Node の RequireJS モジュールと AMD 採用の是非


実は Node 上でも RequireJS モジュールが用意され、利用することができます。

http://requirejs.org/docs/node.html

非同期にモジュールを読み込む AMD のための RequireJS ですが、Node の場合は立ち上がったサーバインスタンスが基本的に永続した上でモジュールインスタンスの永続化がコードの書き方次第で容易だったり、Node 本体の require 時におけるインスタンスの再利用性が悪くなかったりといった理由から、その Node モジュールが RequireJS に依存する、というのは必要ないのではないかと個人的には思っています。


2012-10-13

ランダム文字列生成 gachar


https://github.com/kumatch/gachar

ただのランダム文字列生成です。
最近の個人的なブームに沿って、一応フロントエンドでも使えるようにしときました。使うかどうかは別にして。

var gachar = require('gachar');

var string1 = gachar.run(128);

gacha.run(256, function (string2) {
  ....
});


実は最初 crypt なにがし系を使って適当な文字列を作る処理で書いてたんですが、C モジュールを持ってしてもただの Math.random() に速度で勝てなかったという。やっぱ暗号化処理って重いんですねー(当たり前)。

ランダム性能的にはそこまで良くなさそうですが、使い捨てなトークン値などではこれで十分かなと思ってます。
あと、名前は結構気に入ってます。

2012-10-03

runie.js

https://github.com/kumatch/runie

Node で用意された任意変数を、フロント javascript で参照、利用するためのライブラリです。
変数を一旦 DOM 要素として埋め込んで(描写して)、フロントスクリプトで改めて読み込みます。

Jade template
div
  p= title

!{ runie.tag('items', items) }

Front javascript
runie.read('items', function (items) {
    if (items.length) {
     ....
    }
});

これ以前はフロントスクリプト上から ajax で JSON を受け取って読み込んでいたりしてたので遅かったんですが、これを使うようになってから処理が圧倒的に早くなりました(当たり前)。

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


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

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 マシンなので楽しく使っております。

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

2010-03-05

ubuntu に kumofs を導入して動かしてみた

先日えとらぼより公開された、kumofs を自宅マシンの ubuntu に導入して、動くところまでを確認してみた。Key-Valueストアを利用する機会を作ろうと考えている手前、実際のところちゃんと利用したことがなかったので、まずは基本部分から。ドキュメント通りの導入ステップと、簡単なアクセスによる利用テストのみ。

なお kumofs のアーキテクチャについてはこちらで紹介されている。

今回導入した環境は以下のとおり。

  • OS: Ubuntu 9.10 (KVM)


必要パッケージの導入

コンパイルや動作のために必要となるパッケージを導入。

$ sudo apt-get install libtokyocabinet-dev
$ sudo apt-get install ruby rubygems
$ sudo apt-get install g++

MessagePack for C/C++ の導入

MessagePack の C/C++ はパッケージがないので自前でソースから導入する。この時点でのバージョンは 0.4.2。

$ wget "http://dl.sourceforge.jp/msgpack/46155/msgpack-0.4.2.tar.gz"
$ tar xzf msgpack-0.4.2.tar.gz
$ cd msgpack-0.4.2/
$ ./configure
$ make
$ sudo make install

MessagePack for Ruby の導入

一方 MessagePack for Ruby は gem で導入。

$ sudo gem install msgpack

kumofs のコンパイル

いよいよ kumofs のコンパイル。この時点でのバージョンは 0.3.1。特別やることはなかった。

$ http://github.com/downloads/etolabo/kumofs/kumofs-0.3.1.tar.gz
$ tar xzf kumofs-0.3.1.tar.gz
$ cd kumofs-0.3.1/
$ ./configure
$ make
$ sudo make install

kumofs を動かす

アーキテクチャの紹介でもともかく分散構成が基本であることを前面に出しているし、それを実現するための方法もとても簡単なのがすばらしい。でも単一のサーバ上で構築してもなんら問題なく動く。

まずはマネージャを起動。

$ sudo kumo-manager -v -l localhost &

続いてサーバを起動。

$ sudo kumo-server -v -l localhost -m localhost -s /var/kumodb.tch &

管理ツールで状態を確認してみる。VM 稼働かどうかは定かではないけども、なんか時間がおかしい。

$ kumoctl localhost status
hash space timestamp:
Thu Jan 01 09:00:00 +0900 1970 clock 0
attached node:
not attached node:
127.0.0.1:19800

マネージャにサーバを登録。

$ kumoctl localhost attach
$ kumoctl localhost status
hash space timestamp:
Tue Mar 02 23:38:08 +0900 2010 clock 124
attached node:
127.0.0.1:19800  (active)
not attached node:

なぜか時間も正常に表示されるようになったぞ。

最後に、ゲートウェイを起動。アプリケーションはこのゲートウェイに対して通信する。

$ sudo kumo-gateway -v -m localhost -t 11211 &

使ってみる。直接つっついてみるケースと、プログラムからのアクセスの両方をやってみた。

$ telnet localhost 11211
Trying ::1...
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
set foo 0 0 5
12345
STORED
get foo
VALUE foo 0 5
12345
END

$ sudo gem install memcache-client
$ sudo gem install system_timer
$ irb

require 'rubygems'
require 'memcache'

kumofs = MemCache::new 'localhost:11211'

kumofs.set 'foo', 123
kumofs.get 'foo'

=> 123

2008-07-14

さあいこう!

http://coderepos.org/share/browser/events/phpframework/piece_framework/trunk

機能をすべて実装したものの、まだまだチューニングや見えざる敵(bug)との戦いが控えています。
俺たちの戦いはこれからだ!



r15761 号をもってコミットメントは終了です。
ご愛読ありがとうございました。

2008-03-26

PHP Simple HTML DOM Parser

PHP Simple HTML DOM Parser

これ面白い。

require_once 'html_dom_parser.php';

$dom = file_get_dom('http://www.google.co.jp/search?q=%E3%83%94%E3%82%AB%E3%83%81%E3%83%A5%E3%82%A6&lr=lang_ja&ie=utf-8&oe=utf-8');

foreach ($dom->find('a') as $node) {
    $node->innertext = str_replace('ピカチュウ', 'オプーナ', $node->innertext);
    $node->href = 'http://www.opoona.com/index.htm';
}

foreach ($dom->find('b') as $node) {
    $node->innertext = str_replace('ピカチュウ', 'オプーナ', $node->innertext);
}

print $dom->save();


なんか色々思いついたので、暫らくの間これで遊びます。