JavaScriptでコマンドフィルタっぽい事をやってみた

パイプで繋ぐUnix系のコマンドラインフィルタや、
smartyのフィルタのように次々にコマンドの戻り値を
次のコマンド(関数)に渡していくような制御を javascript でやってみる。

インターフェース(コマンドの指定方法について)

見た目の直感性が大事なので文字列を使いパイプで繋ぐコマンドを
指定する事も考えたが、今回は関数呼び出しを繰り返し、
引数にコマンドを渡すように実装。


こんな感じ。

commandFilter("テスト文字列")(escape)(nl2br)(url2link)();

実装

function commandFilter() {
  var arg = Array.prototype.slice.call(arguments, 0);
  var chains = [];
  return function wrap(){
    if (arguments.length == 0) {
      var f;
      while(f = chains.shift()) {
        arg = f.apply(null, [].concat(arg));
      }
      return arg;
    }
    else if(arguments[0] && typeof arguments[0] == "function") {
      chains.push(arguments[0]);
      return wrap;
    }
  }
}

commandFilter関数に与えた引数が実行時の最初のパラメータになる。
commandFilterが返した関数を呼び出す時にコマンドを渡すと、
その関数をラップし、再度同じ関数を返す。
引数無しで呼び出すとそれまでにラップされたコマンドを一気に実行する。


各コマンドを呼び出す時、applyを使って可変引数を渡すようになっているので、
各コマンドの返り値を配列にしてやれば、次のコマンドの呼び出しで展開して引数を渡してくれる。
(戻り値をタプルにし、そのタプルを引数に呼び出すイメージ)

使い方

以下のように使う。

(function() {
    var escapeHTML = function(str){ return str.replace(/&/g,'&') };
    var nl2br = function(str){ return str.replace(/\r|\n/,'<br>') };
    var url2link = function(str){
	return str.replace(/(https?:\/\/[-_.a-zA-Z0-9/?]+)/g, "<a href='$1'>$1</a>")
    };
    // 各コマンドの内容は省略


    var html = "ああhttp://d.hatena.ne.jp/いい\nうう&ええ";
    var result = commandFilter(html)(escapeHTML)(nl2br)(url2link)();
    alert(result);
})();

//=>ああ<a href='http://d.hatena.ne.jp/'>http://d.hatena.ne.jp/</a>いい<br>うう&amp;ええ

うん。いけてるっぽい。