愛と勇気と缶ビール

ふしぎとぼくらはなにをしたらよいか

node.jsで「メタプログラミングRuby」水曜日のredflagを実装する

はい、つられましたね。あなた、node.jsっていう流行り言葉につられてこのリンクを踏んでしまいましたね。

ここはバーボンハウスだ。ゆっくりしていってくれたまえ。



















というような冗談は置いといて。

最近、「メタプログラミングRuby」っていう本を読んだのですが、その本の第三章「水曜日」のとこに、redflag.rbっていうRubyでDSLを実装する際のサンプルコードがあります。

具体的には、カレントディレクトリにある*event.rbに書かれてある次のようなDSLを実行して、event関数に渡したブロックの条件式が満たされなかった場合に"ALERT: hyde is shorter than 157cm!"という出力をするためのコードをかけ、みたいなお題です。

event "hyde is shorter than 157cm!" do
  @hyde < 157
end

setup do
  puts "hyde is 156 cm tall!"
  @hyde = 156
end

で、せっかくなんでJSでやってみましょうか、と思ったのですが普通のブラウザ上で動くJSだと当然ローカルのファイルとか読み込めなくてくやしいのでnode.jsを入れてやってみましたとさ。


以下DSLを実現するためのコード

(function(){

  var setups = [], events = {};

  global.event = function(name, func) {
    events[name] = func;
  };  

  global.setup = function(func) {
    setups.push(func);
  };  

  global.each_event = function(func) {
    for ( name in events ) { 
      func(name, events[name]);
    }   
  };  

  global.each_setup = function(func) {
    for ( var i, l = setups.length; i < l; i++ ) { 
      func(setups[i]);
    }   
  };

  var sys = require('sys');
  var fs = require('fs');
  var Script = process.binding('evals').Script;

  fs.readdir('./', function(err, files) {

    var processFile = function(file) {
      var data = fs.readFileSync(file);
      Script.runInThisContext(data);
    };

    if ( err ) { 
      console.log(err);
    }
    else {

      for ( var i, l = files.length; i < l; l++ ) { 
        if ( /events\.js$/.test(files[i]) ) processFile(files[i]);
      }

      each_event(function(name, event) {
        var env = {}; 
        each_setup(function(setup) {
          setup.call(env);
        }); 
        if ( event.call(env) ) sys.print("ALERT: " + name + "\n");
      });

    }

  });

})();


実行するDSLのコード( node.js v.0.2.3 上で動作することを確認 )

event('hyde is smaller than 157cm!', function() {
  return this.hyde < 157;
});

setup(function() {
  this.hyde = 156;
});


そこまでポイントとかないんですが、

  • Rubyのhoge.instance_evalの代わりにhoge.callを使ってコンテキスト(というかthis)を切り替える
  • fs.readFile()だと非同期ファイル読み込みになっちゃってこの場合はめんどいのでfs.readfileSync()を使う


みたいな感じでしょうか。「その書き方はないわ〜」っていうのがあったらツッコミください。


node.jsをまだそこまで触ってないのであんまり詳しくないのですが、今のところの感想としては

  • JSがサーバサイドで動く、と聞くとサーバ実装とかの方にばかり気が行くけど、いわゆる普通のプログラミング言語としても使える
  • とにかくevent driven、非同期実行。そんなに非同期にして大丈夫か?
  • けっこう低レイヤのことができる
  • 今までのJSとは別のAPIをもつ一つの「言語」としてみると、色々面白い点がある

「これでもか」「これでもか」とばかりにAPIがイベント志向なので、イベント駆動の書き方にある程度慣れた上で気をつけて書かないとウフンアハンなコードになってしまうかも。


クライアントサイドのJSを書くときって「これはブラウザの上で動くもんで全てはwindow様の支配下にあるのだ」という無意識に枷をかけた上で考えているので、JSから何でもできるとたーのしー、っていうのは確か。