Net::Twitterでtwitter認証をスマートに行う

CPANのNet::Twitterを用いると簡単にtwitterの認証周りを実装することができますが、
いろいろなブログを見ると認証のurl取得には

my $callback_url = 'http://foo.bar.com/twtr_callback';
my $auth_url = $nt->get_authorization_url(callback => $callback_url);

として認証用のURLを取得すると書いてあります。
ただ、この get_authorization_url メソッドで返ってきたURLにユーザを
リダイレクトさせると毎回twitterの認証画面が表示されてしまいます。

すでにユーザによって認証されているときは必要な情報だけを取得して $callback_url に
リダイレクト、認証されていないときは認証画面を出して $callback_url にリダイレクトさせたい
ときは get_authentication_url メソッドを用いると、その処理を行なってくれて
スマートなtwitter認証をユーザに提供できます。

my $callback_url = 'http://foo.bar.com/twtr_callback';
my $auth_url = $nt->get_authentication_url(callback => $callback_url);

これ以外の、詳しい実装方法はNet::Twitter::Role::OAuth - Net::Twitter role that provides OAuth instead of Basic Authentication - metacpan.orgのEXAMPLESに書かれていますので、
そちらを参考にして下さい。

node-seq編集

node.js以前の記事 node.jsを使ったチャットアプリケーションとnode-seqの紹介 - wktkWebDiary でnode-seqの紹介をしましたが、

そこでGitHub - substack/node-seq: Chainable asynchronous flow control for node.js with sequential and parallel primitives and pipeline-style error handlingの出番です。
このモジュールを使うとコールバックのネストが次のようにすっきりします。
ただこのままだとエラーをcatchしたあとに続く処理も行われてしまうため、一工夫する必要みたいです。※改良中

というわけで、forkして編集しました。
上記以外のバグも修正した安定版のstableブランチにまとめてあります。
https://github.com/YaaaaaSuuuuu/node-seq
GitHub - yasuoza/node-seq: Chainable asynchronous flow control for node.js with sequential and parallel primitives and pipeline-style error handling (user.name変えました)

この編集で、エラーをキャッチした所で次のチェーンには進まなくなりました。
これで次のように安心してエラーハンドリングできそうです。

var Seq = require('seq');

function lib (args, cb) {
  var error = null;
  if (args == undefined) {
    error = new Error('args is neccesary');
  }
  cb(error, args);
}

Seq()
       .seq(function () {
            lib(undefined, this);
       })
       .catch(function (err) {
            console.log(err);
       })
       .seq(function () {
            throw new Error('You must edit node-seq');   // This step is ignored.
       })
;

テストもexpressoだったものを最近増えてきているmochaで書き直しました。

インストール方法は
node_modulesディレクトリで

$ git clone git://github.com/YaaaaaSuuuuu/node-seq.git
$ npm install -d 

で行えます。

テストはstableブランチにcheckoutした後に

$ mocha

をして下さい。

Chrome Extension - Fast Note

作業をしているときに一時的にメモを取っておきたい時が結構あります。これまでは普段使いのvimに新しくバッファを作ってそこに書くか、いちいちメモ帳を起動してそこに書いていました。
でも、この作業が限りなく面倒だったのでChromeのエクステンションとしてメモ帳があったらいいのになーと思って探したらやっぱり2、3ありました。

でも、実際試してみるとアイコンをクリックしてからポップアップが表示されるまで結構時間がかかる。
メモを取りたいと思ってからメモを書き込めるまでに時間がかかるのはかなりイケていないので、自分で作ってみました。

https://chrome.google.com/webstore/detail/jehbplmdjbmcbbdlghcphdhfakcgaiaj
Fast Note - Chrome Web Store

データの保存にはlocalStorageを使っています。
起動を速くするためにmanifest.jsonでbackground_pageというオプションを有効にして、常に裏でページが開きっぱなしの状態にしています。
他のメモエクステンションはアイコンがクリックされてからページを構成するので、表示までに時間がかかっているみたいでした。
僕の作ったエクステンションはメモリ容量よりもポップアップ表示に重きをおいた形です。
といっても、エクステンションのHTMLやJSはかなり軽く作ったつもりなので、ほとんど無視出来るメモリの消費量だと思いますが。

ソースコードも公開しています。
小学3年生くらいでも書けそうなHTMLとJSで構成されています。
https://github.com/YaaaaaSuuuuu/chrome-extension-FastNote

ぜひ使ってみて下さい。

chrome-devtools-autosaveを使って楽にCSSを編集しよう

HTMLコーディングをしているときにChromeのDeveloper Toolsを使って、Chrome上で編集したスタイルがそのままCSSファイルに反映されるという、これまでのCSSコーディングを大きく変える(個人の感想です)node.jsで動く開発ツールの紹介です。node.jsとver.15以上のChromeが必要です。

作者のデモ:

githubリポジトリ
GitHub - NV/chrome-devtools-autosave: Auto-saving CSS and JavaScript changes from the Chrome Developer Tools

使うまでにやることは多いですが、簡単なことばかりなので多分インストールに失敗することはないと思います。

  1. Node.js をインストール
  2. chrome://flags/ にアクセスして、Experimental Extension APIsをオンにします。Chromeを日本語で使っているときは試験的拡張APIみたいな項目があるので、それを有効にしてください。
  3. Chromeを再起動します。
  4. エクステンションをインストールします。Что есть чтобы предотвратить сахарный диабет - Сахарный диабет

※エクステンションをインストールするときにExperimental Extension APIsがオンになっていないとエラーが出てインストールが出来ません。

ここまできたら、下準備は終わりです。次にnpmでautosaveを -g オプションを付けてインストールします。

npm install -g autosave

インストールしたら

autosave

で実行できるはずです。
npmやautosaveは実行する前にnodeのPATHを通しておく必要があります。
macLinuxなら.zshrcや.bashrc(.bash_profile)などに

source /path/to/node

もしくは、

$PATH = /path/to/node/:$PATH

などと書いてパスを通しておいてください。

windowsの場合はWindowsの環境パスを通す(path)などを見ながら通してください。
autosaveのためだけにnode.jsを使うときは、nodejsディレクトリにautosaveのダウンロードファイルDownloads · NV/chrome-devtools-autosave · GitHubの中身をそのまま置き、コマンドプロンプトでnodejsディレクトリまで行き、

autosave

を実行してください。

DevTools Autosave 0.3.2 is running on http://127.0.0.1:9104  

が表示されればautosaveが動いています。

先ほどのデモを再現しましょう。インストールしたautosaveディレクトリのexampleディレクトリに入っているindex.htmlをドラッグアンドドロップしてブラウザで表示させ、ChromeのDeveloper ToolsでCSSの値を変えてリロードしてください。
きっと変更が保存されているはずです。

ここまで来て、僕もスゲーってなりましたが、これデフォルトではfile://で始まるものしかCSSの編集を受け付けてくれません。
つまり、phpファイルとかは無理っていうことです。

でも、chrome-devtools-autosave-server/README.mdown at master · NV/chrome-devtools-autosave-server · GitHubを見たら、http://から始まるファイルもautosaveで編集できるように設定することができるらしい。ということでやってみました。

これもやることは多いですが、簡単なので多分大丈夫。

  • Chromeのエクステンションの設定画面を開いてDevTools autosaveエクステンションを探す

  • optionsをクリックして、DevTools Autosaveの設定画面を開く
  • Add ruleをクリックして監視するルールを追加できるようにする
  • Resource に ^http:// を Post to には ^file://と同じ http://127.0.0.1:9104/save を記入

これでChrome側の設定は終わりです。設定画面等は閉じてOK。
次はサーバ側の設定です。

自分の好きなところに次のような内容のroutes.jsを作ります。
URLとパスの値は必要に応じて編集してください。

exports.routes = [
    {
        from: new RegExp('^http://foobar\.local/'),
        to: '/Volumes/server/var/www/foobar.local/'
    }
];

fromの行は正規表現なので、ピリオドをエスケープする必要があります。
toの行は文字列なので、ピリオドをエスケープする必要はありません。

これで設定は終わりです。autosaveを--configオプションを付けて実行します。

autosave --config /path/to/routes.js
DevTools Autosave 0.3.2 is running on http://127.0.0.1:9104  

が表示されたことを確認して、
http://foobar.local にアクセスして、先ほどと同じようにCSSを編集してみてください。CSSファイルが編集されていれば成功です。

このツールを使って、これまでより楽にHTMLをコーディングしましょう。

node.jsを使ったチャットアプリケーションとnode-seqの紹介

2012年3月20日追記

node-seqを編集したので、記事を書きました。
catchを使いやすくしています。
node-seq編集 - wktkWebDiary

ユーザのFacebook友達情報を引き継いで複数人とチャットが出来るアプリケショーンをnode.jsの勉強がてら作ってみました。

https://github.com/YaaaaaSuuuuu/FaceRoom


ログインのたびにユーザのフレンドリストを取得しているので、ログインの動作がかなり重くなっています。ホントならフレンドリストはcronで回してログイン時はユーザの認証だけにしたかった。。。

インストールの仕方とかはREADME.mdに書いたので、そこらへんを読めば多分動きます。
後ろでMongoDBとRedisが動いていないといけないのですが、どちらも簡単にインストールできるので、つまずくことはないと思います。
Facebookの友達情報とFaceRoomのユーザと照会をするので、テストユーザを最低2人作ってそれぞれ友達にしておいて下さい。

プログラム自体は特に難しい所とかなかったので、2週間毎日1時間くらいの時間でサクサク出来ました。
ただ、きちんとテストコードを書いていないので、そこが今後の課題です。

デブサミに参加して教えてもらったnode-seqというモジュールがすごく便利で、このアプリでも主要箇所に利用しています。
node.jsはコールバック関数のネストが問題になって、例えばDBから取得したデータを元にさらにDBから情報を取得するなんていう処理を行うと普通なら

Collection.findOne(query, function (error, docs) {
   var query_id = doc._id;
    AnotherCollection.findOne({doc_id: query_id}, function (err, target) {
           // something you want to do
    });
});

みたいにコールバック関数のネストが続きます。
まぁ上記の例はきちんとスキーマを設定してあげる事で解決できたりすることもあると思います。

そこでGitHub - substack/node-seq: Chainable asynchronous flow control for node.js with sequential and parallel primitives and pipeline-style error handlingの出番です。
このモジュールを使うとコールバックのネストが次のようにすっきりします。

var Seq = require('seq');
Seq()
      .seq(function () {
             Collection.findOne(query, this);
       })
       .seq(function (doc) {
              var query_id = doc._id;
              AnotherCollection.findOne({doc_id: query_id}, this)
       })
       .seq(function (target) {
              // something you want to do
       })
      ;

各seq実行時のエラーは

Seq()
       .seq(function () {
            // foo
        })
       .catch(function (err) {
            // bar             
        })
        .seq(function (arg){
            // foobar
        })
       ;

のようにキャッチできます。
node-seqは必ず第一引数がエラー値になるっていうのが大事です。
なので、自分でライブラリを作る場合は引数のコールバック関数の第一引数にエラー値を渡すっていう処理が必要です。

var Seq = require('seq');

function lib (arg, callback) {
  var error = null;
  if (!arg) {
    error =  new TypeError('arg is necessary');
  }
  callback(error, arg);
}

Seq()
   .seq(lib, undefined, Seq)
   .catch(function (error) {
      console.error(error);  // TypeError
   })
   .seq(function (arg) {
      console.log(arg);
   })
  ;

のようになります。
ただこのままだとエラーをcatchしたあとに続く処理も行われてしまうため、一工夫する必要みたいです。※改良中

こんな感じでnode-seqを使うとコードの見通しが良くなるし、自分でライブラリを作るときにも利用できるのでお勧めです。

来週末のNode.js4時限目がすごく楽しみです。

node.jsでファイルの読み書きをする

cgiを作っているときにサーバからのレスポンスをファイルに書きだしてデバッグするということがあると思います。
javascriptでは狭義的にファイルの読み書きはできませんがnode.jsを用いるとファイルの読み書きが可能になります。
ファイルの書き込みは追記ではなくて、呼ばれるたびに新規作成になります。

ファイルを読み込むとき

var fs = require('fs');

var read = fs.createReadStream('./hello.js');

read.on('data',  function (data)      { console.log('read: data');  })
    .on('end',   function ()          { console.log('read: end');   })
    .on('error', function (exception) { console.log('read: error'); })
    .on('close', function ()          { console.log('read: colse'); })

read.on('data', function (data) {
    console.log(data.toString());
});

結果は

read: data
"Hello World!"
read: end
read: close

となります。
read.on('end')とかで読み込みが終わったときに何らかの処理を行うという非同期的な処理が出来るわけなんですね。

ファイルへの書き込みは次のように行います。

var fs = require('fs');

var write_stream = fs.createWriteStream('./output.txt');

write_stream.on('drain', function ()         { console.log('write: drain'); })
            .on('error', function (exeption) { console.log('write: error'); })
            .on('close', function ()         { console.log('write: colse'); })
            .on('pipe',  function (src)      { console.log('write: pipe');  });


var arr = [1, 2, 3, 4, 5];

arr.forEach(function (v) {
    write_stream.write(v + "\n");
});

結果は

write: drain

となり、"close"イベントが着火しません。
fs.WriteStreamを使うときは次のようにします。

var fs = require('fs');

var write_stream = fs.createWriteStream('./output.txt');

write_stream.on('drain', function ()         { console.log('write: drain'); })
            .on('error', function (exeption) { console.log('write: error'); })
            .on('close', function ()         { console.log('write: colse'); })
            .on('pipe',  function (src)      { console.log('write: pipe');  });


var arr = [1, 2, 3, 4, 5];

arr.forEach(function (v) {
    write_stream.write(v + "\n");
});
write_stream.end();

こうすると結果が

write: close

となって書き込み終了のイベントが検知できます。

'drain'イベントに関しては公式ドキュメントhttp://nodejs.org/docs/latest/api/streams.html#event_drain_だと
Event: 'drain'の項目で

After a write() method returned false, this event is emitted to indicate that it is safe to write again.

stream.write(string, [encoding], [fd])の項目では

Returns false to indicate that the kernel buffer is full, and the data will be sent out in the future.

と記載されています。

でもfs.WriteStream.end()メソッドを使わなくてもファイルに書き込みされているわけなので、ファイルの書き込みが閉じられないでカーネルのバッファがいっぱいになったということを表しているのだと思います。
そのためファイルの書き込みが終わったらfs.WriteStream.end()を呼ぶのがいいのではないでしょうか。

Redisを使ってexpressでセッション管理をする

expressでユーザのセッションを引き継いでページ遷移させたいっていうのが結構あります。
その場合、よくRedisというキーバリューストア(KVS)モデルのデータベースが用いられるようです。
これまでのWebアプリケーションがMySQL + memcachedで構成されていたことを考えると
node.jsはMongoDB + Redisで構成されることがスタンダードになっていくのでしょうか。

さて、そのRedisのインストールは本家ページ(Redis)からwgetして解凍します。

$ wget http://redis.googlecode.com/files/redis-2.4.6.tar.gz
$ tar xzf redis-2.4.6.tar.gz
$ cd redis-2.4.6
$ make

サーバの立ち上げは

$ src/redis-server

で行います。

そのあとは例によってnpmモジュールのインストール。

$ npm install redis redis-client

Redisを用いてexpressフレームワークでセッションを使うときは次のように書きます。

var express = require('express'),
      RedisStore = require('connect-redis')(express);

app.configure(function () {
    app.use(express.cookieParser());
    app.use(express.bodyParser());
    app.use(express.methodOverride());
    app.use(express.session({
        secret: 'your secret',
        store: new RedisStore,
        cookie: { maxAge: 3 * 60 * 60 * 1000,    // 3 hours
                  httpOnly: false
                }
    }));
});

これで

req.session.hogehoge = hoge;  // setter
var session = req.session.hogehoge;  // getter

みたいにしてセッションの値を保存したり参照できたりします。

また、Redisはサーバを落とさない限り、アプリを落としてもセッションが保存されているので、開発する時にも便利です。