2012-11-30

[Node.js] async を使って複数のファイルを同時に fs.stat する

非同期処理をもっと楽しくする async の async.parallel を使うと複数の処理を並列に行い、全部処理が終わった時点もしくはいずれかの処理でエラーが発生した時点で、代表して一度だけコールバック関数が呼ばれるようにできます。

処理完了もしくはエラー終了の時点で、cb を cb(err, result) の形式で呼び出します。エラーがない場合は err を null とすれば OK です。

var async = require('async'),
    fs = require('fs');

async.parallel([
    function (cb) {
        fs.stat('b.txt', function (err, stat) {
            if (err) { return cb(err); }
            cb(null, +stat.mtime)
        });
    },
    function (cb) {
        fs.stat('b.txt', function (err, stat) {
            if (err) { return cb(err); }
            cb(null, +stat.mtime)
        });
    }
], function (err, results) {
    if (err) { throw err; }
    console.log(results);  // [ 1354174306000, 1354112137000 ]
});

fs.stat するコードが同じことを繰り返している点が格好良くないので一つにまとめます。

var async = require('async'),
    fs = require('fs');

async.parallel(['a.txt', 'b.txt'].map(function (file) {
    return function (cb) {
        fs.stat(file, function (err, stat) {
            if (err) { return cb(err); }
            cb(null, +stat.mtime)
        });
    };
}), function (err, results) {
    if (err) { throw err; }
    console.log(results);  // [1354174306000, 1354112137000 ]
});

async.parallel の第一引数をオブジェクトにすると結果 results もオブジェクトにもできることを用いて、さらに underscore の便利な関数 _.object (二要素の配列の配列もしくは同じ長さの配列2つからオブジェクトを作る関数) を活用して以下のようにできます。

var async = require('async'),
    fs = require('fs'),
    _ = require('underscore');

async.parallel(_.object(['a.txt', 'b.txt'].map(function (file) {
    return [file, function (cb) {
        fs.stat(file, function (err, stat) {
            if (err) { return cb(err); }
            cb(null, +stat.mtime)
        });
    }];
})), function (err, results) {
    if (err) { throw err; }
    console.log(results);  // { 'a.txt': 1354174306000, 'b.txt': 1354112137000 }
});

2012-11-29

[windows] 復元ポイントに保存されたレジストリを閲覧する (Windows XP用)

レジストリを壊してしまった場合は「システムの復元」という機能で直近の復元ポイントの状態まで戻すのが安全かつ確実でお勧めできる方法ですが、少し昔のレジストリがどうなっていたかだけを知りたい場合には、C:\System Volume Information\_restore{GUID}\RPxxx\Snapshot の中に入っているレジストリハイブのスナップショットを使えばよいです。

RPxxx (xxx は連番, RP=RestorePoint) はそれぞれ一つの復元ポイントを表していて、更新時刻を見ることでどのくらい過去の状態か分かります。

これらのレジストリハイブはバイナリで保存されていてそのままでは中が見れません。しかしながらレジストリエディタで「ハイブの読み込み」という機能を使うと過去の任意のレジストリハイブをマウントして閲覧でき、ついでにエクスポートなどもできます。

ハイブの読み込みについて、具体的には、HKEY_LOCAL_MACHINE を選択した後、ファイル>ハイブの読み込み、でレジストリファイルを読み込み、適当な名前 system_rp123 などを付けて読み込み、不要になったらアンロードすれば良いです。

関連情報
システムの復元とは - Windows 7
System Volume Information フォルダへアクセスする方法
レジストリにハイブを読み込む

2012-11-27

[blog] ソースコード表示を SHJS に乗り換えました

ブログのコードを見やすいように予約語や文字列リテラル、数値リテラルなど文法に沿って色付きにしてくれるスクリプトとして長らく google-code-prettify を使っていましたが、Internet Explorer 10 で改行が消滅して表示がおかしくなっている (2011年6月版で改行が LF のみの場合に変になることを確認) のを発見したので、代わりに SHJS を使ってみることにしました。

SHJS には google-code-prettify と違って言語の自動解析機能が付いていない所が気に入りました。対応言語も一通り揃っていて、付属のカラーテーマも色々あって楽しいです。行番号を付加する機能は付いていません。

使い方本体と言語定義ファイルを読み込み、コードを pre の中に書いて class を言語定義ファイルに合わせて sh_LANGUAGE とするだけです。例えば JavaScript なら sh_javascript とすればよいです。

このブログでは現在カラーテーマは whitengrey になっています。
SHJS を SHJS で表示した例:

/*
SHJS - Syntax Highlighting in JavaScript
Copyright (C) 2007, 2008 gnombat@users.sourceforge.net
License: http://shjs.sourceforge.net/doc/gplv3.html
*/

if (! this.sh_languages) {
  this.sh_languages = {};
}
var sh_requests = {};

function sh_isEmailAddress(url) {
  if (/^mailto:/.test(url)) {
    return false;
  }
  return url.indexOf('@') !== -1;
}

function sh_setHref(tags, numTags, inputString) {
  var url = inputString.substring(tags[numTags - 2].pos, tags[numTags - 1].pos);
  if (url.length >= 2 && url.charAt(0) === '<' && url.charAt(url.length - 1) === '>') {
    url = url.substr(1, url.length - 2);
  }
  if (sh_isEmailAddress(url)) {
    url = 'mailto:' + url;
  }
  tags[numTags - 2].node.href = url;
}

SHJS - Syntax Highlighting in JavaScript

2014/08/10 追記
さらに Highlight.js に乗り換えました。

2012-11-26

[Express] Cannot GET /foobar はどこで出されているか

Express で全てのコールバックがレスポンスを返さなかった場合に表示される Cannot GET /foobar という感じの 404 エラーページは誰が出しているのかというと、express.js の依存ライブラリ Connectlib/proto.js で関数 app.handle が出力しています。

ルーターを含む全ての Middleware が next() を行って最後まで突っ走ってしまうと Cannot ... のコードに到達します。

ちなみに Middleware 内で next() をせず res.send() などでレスポンスを返すこともしなかった場合は、クライアント側からの見え方は延々と何もレスポンスが返らない状態になり、一方でサーバーもエラーを出すことのない両者にらみ合いの膠着状態になります。

2012-11-23

Windows 8 のリフレッシュ機能を試してみました

Windows 7 から Windows 8 にアップグレードしたらもともと入っていた IME 2010 がインストーラーの不具合?で削除できなくなってしまったので、興味のあった PC のリフレッシュ機能を試してみました。

リフレッシュ機能とはドキュメントや写真、音楽などのファイルを受け継ぎつつ OS を初期化する新機能で、コンピュータの復元でも回復できないとき (セーフモードでも起動しないような、起動に必要なファイルが破損している状況など) でもこれを使えばかなり強力に復活できるように見えます。

この機能にアクセスする方法は PC 設定>全般>PC をリフレッシュする、もしくは起動失敗時にブートマネージャーが自動的に出してくれる回復用画面より選択できます。

リフレッシュに伴ってレジストリと Program Files, AppData, ProgramData は C:\Windows.old 内に移動されました。Windows ストアからインストールしたアプリは自動的に再インストールされ復活しました。Windows のほとんどの設定は、システムとユーザーアカウント固有の設定ともに受け継がれるようです。

インストールされていた従来型のアプリケーションとその設定は消滅しますが、インストールされていたアプリケーションの一覧という遺書をリフレッシュ時にデスクトップに HTML 形式で保存してくれるのが役に立ちました。

詳しい説明
リフレッシュ機能は OS のインストール時に自動的に作成される C:\ESD\Windows に保存されたインストールイメージを用いて Windows 8 をデータ受け継ぎ方式で再度インストールを実行する仕組みのようです。したがってリカバリディスク作ってないや派の人や、インストールメディアを作らずアップグレードした人、自力でアップグレードしたからリカバリディスクが Windows XP しかない人でも安心して(?) OS を破壊できることになります。

レジストリや AppData が自動的に受け継がれない理由はおそらく、システムと密結合した設定が保存されるので、リフレッシュ後にも障害が残ることを危惧してこのようにクリアされるようになっているのだと思います。

リフレッシュ時に Chrome のプロファイルはリセットされますが、C:\Windows.old\Users\USERNAME\AppData\Local\Google\Chrome から必要なだけ持ってくれば復活します。

古い環境からのファイルの救出が完了したあと C:\Windows.old を削除しようとするとアクセス権限が面倒くさいことに気づくと思います。管理者なら任意のオブジェクトの所有権を奪還する特権を行使してアクセス権を再設定すれば削除できますが、簡単に削除するには Windows 付属の ディスク クリーンアップ を使うとよいです。
Windows.old フォルダーを削除する方法 - Microsoft Windows ヘルプ

C:\ESD\Windows も「Windows ESD インストールファイル」という項目で同様に削除できますが残しておくと便利だと思います。

C:\ESD\Windows を削除してしまっていたり、ブートマネージャーも含めて壊れてしまっているときはインストールメディアで起動すれば修復機能が使えます。

参考
PC を復元、リフレッシュ、または初期状態に戻す方法 - Microsoft Windows ヘルプ

2012-11-20

[heroku] 最新の Node.js を使ってアプリを動かす設定

Heroku に Node.js で動くアプリケーションを置くことまでは出来たけれど Node.js のバージョンが v0.4.7 になることにがっかりしている人はこの記事をご覧ください。

どのバージョンの Node.js を使うかを指定するには公式ドキュメント Specifying a version of Node.js or npm に書かれている通り、package.json の engines プロパティに最新版を使用する旨を記述すればよいです。

デプロイする度にとにかく最新になることを望む edgy な方や遊びのプロジェクトは単に x と指定しておけば最新の stable release になります。

もし製品版をデプロイするときは自動的に壊れてしまうことを防ぐため、依存モジュールも含めてバージョンを全て固定することを強くお勧めします。

とにかく最新版になる package.json の例:

{
    "name":"適当な名前",
    "version":"0.0.1",
    "dependencies":{
        "express":"",
        "underscore":"",
        "mongoose":"",
        "request":""
    },
    "engines":{
        "node":"x",
        "npm":"x"
    }
}

[dotcloud] 新しい Perl を使うように設定してみました

基本的には設定ファイル dotcloud.yml を 公式ドキュメント dotCloud - Perl Service にある通り perl_version: v5.16.x などと設定するだけで OK のはずだと思っていました。

しかし最近追加されたインクリメンタルなビルド機能 (前回のビルド結果を受け継いで変更のない依存モジュールをそのまま使いビルド時間を短縮する) が Perl のバージョンの更新を察知してくれないせいで、バイナリのリンクがだまって壊れました。

この問題を解決するためにはクリーンなビルド (前回の結果を受け継がないビルド) を行うようにデプロイを

$ dotcloud push --clean

と --clean オプション付きで実行する必要があります (2012/11/19 時点)。

2012-11-17

パスワードの管理について

メインのメールアドレスに設定しているパスワードを他のサイトのパスワードに設定しないことが何より肝心だと思います。

うちでは使用頻度の低いサイトのアカウントにはランダムな文字列のパスワードを設定して、それをパスワード管理ソフト KeePass を使ってマスタパスワードで暗号化して保存しています。そして保存した最新のパスワードが他のパソコンでも使えないと不便なのでそのファイルを Dropbox を使って同期しています。

非公式ですが公認されている Android 版のクライアント KeePassDroid も緊急用として活用しています。

[Node.js] request を使って Facebook の Graph API にアクセスする

Facebook Graph API など REST で OAuth 2.0 な API にアクセスする際は素朴な HTTP クライアントがあれば十分と思うので Node.js では最近は request を使っています。

このクライアントを使うにあたり中間者攻撃などを防ぐためオプション strictSSL を true として SSL の証明書の検証を行うことを有効にすることが大切だと思います。しかし残念なことに Node.js v0.8.14 では graph.facebook.com の wildcard 証明書について壊れていますw (#4255) Node.js v0.8.16 で直っているように見えます。

request について、パラメータの与え方が GET の場合は qs、POST の場合は form になることに注意します。また、オプション json を true にしておくとレスポンスを JSON としてデコードしてくれます (正確にはリクエストボディ body の Content-Type を application/json に設定する機能も含みます)。

さらに詳しくは 公式ドキュメント をご参照ください。

Graph API には具体的に以下のコードでアクセスできます。

var request = require('request').defaults({
    strictSSL: true
});

function getIdentity() {
    request.get('https://graph.facebook.com/me', {
        qs: {
            access_token: ACCESS_TOKEN
        },
        json: true
    }, function (err, resp, data) {
        if (err) { throw err; }
        if (resp.statusCode !== 200) { throw new Error(JSON.stringify(data)); }
        console.log(data);
    });
}

function writeWall() {
    request.post('https://graph.facebook.com/me/feed', {
        form: {
            message: MESSAGE,
            access_token: ACCESS_TOKEN
        },
        json: true
    }, function (err, resp, data) {
        if (err) { throw err; }
        if (resp.statusCode !== 200) { throw new Error(JSON.stringify(data)); }
        console.log(data);
    });
}

蛇足ですが request には OAuth 1.0 用のリクエスト署名機能も内蔵されているので oauth パラメータを渡すことで Twitter - REST API にも追加モジュールなしでアクセス可能です。