NO TEST, NO LIFE. NO DOC, NO LIFE

uupaa.js や mofmof.js には {@hoge 〜 }@hoge のようなコードブロックを切り落として Minify する機能があるので、「ソースコードにテストもドキュメントも全部埋め込むことが可能だな〜」って3年程前から考えてました。

そこで、Function.prototype.spec というメソッドを追加し、これにスペックを書き貯めたらどうだろうか(?)とか考えました。

たとえば

Array.range(1,7) で [1..7] 的な連続した数値の配列を生成する Array.range 関数があったとすると

// Array.range - range generator
function Array_range(begin,    // @param Number: begin
                     end,      // @param Number: end
                     filter) { // @param Function/Number(= 1): filter or skip count
                               // @return Array: [Number, ...]
                               // @raise: Error("BAD_ARG")
                               // @see: Array#range
    var rv = [], ri = 0, i = begin, iz = end, skip = 1;

    if (Type.isFunction(filter)) {
        for (; i <= iz; ++i) {
            if (filter(i) === true) {
                rv[ri++] = i;
            }
        }
        return rv;
    }
    if (Type.isNumber(filter)) {
        skip = filter;
    }
    if (skip <= 0) {
        throw new Error("BAD_ARG");
    }
    for (; i <= iz; i += skip) {
        rv[ri++] = i;
    }
    return rv;
}

この場合、スペックは以下のような感じで記述してます。

//{@spec
Array.range.spec({
    desc:   ["Function",            "range generator"],
    begin:  ["Number",              "begin"],
    end:    ["Number",              "end"],
    filter: ["Function/Number(= 1)","filter or skip count"],
    ret:    ["Array",               "[Number, ...]"],
    raise:  'Error("BAD_ARG")',
    see:    "Array#range"
});
//}@spec

まだ荒削りだけど、

  1. Function.prototype.spec._spec にスペック一覧が保存されている
  2. Function.prototype.spec._src に関数を実行可能な形(改行やコメントも含んだ文字列)で格納している
  3. node.js 上で走らせて、html + css に落とし込めば、ドキュメントが生成できる
  4. 関数がソースコードに書かれた文字列そのままの形で取り出せるから、ドキュメント上で関数を実行するインタプリタも付けられる
  5. spec に基づき IN と OUT をチェックする assert を自動生成することも可能

とか考えてました。

jsdoc だと、かゆいところに手が届かないので、こういうことになってしまうわけですが。まぁこれはこれで

Function.prototype.spec の実装はこんな感じ

// Function.prototype.spec - add function spec
function Function_spec(hash) { // reserved:{ desc, ret, see, test }
    Function_spec._spec || (Function_spec._spec = {},
                            Function_spec._src  = {});

    var fnName = this.name, // function name
        i, iz, ary;

    // pick up function.name [IE6][IE7][IE8][IE9][Opera9][Opera10.1x]
    if (!fnName) {
        fnName = this + "";
        fnName = fnName.slice(9, fnName.indexOf("(")); // )
    }
    Function_spec._spec[fnName] = hash;
    Function_spec._src[fnName] = this + "";

    if ("test" in hash) {
        ary = hash.test;
        for (i = 0, iz = ary.length; i < iz; i += 3) {
            ary[i].test(ary[i + 1], ary[i + 2] || "");
        }
    }
}

通常利用時は {@spec 〜 }@spec コードブロックを削除して minify する感じです。

//{@spec
Function.prototype.spec     || (Function.prototype.spec     = Function_spec);
String.prototype.test       || (String.prototype.test       = String_test);
//}@spec

String.prototype.test() は類似検索と深度探索を行う、テスト君です。
http://mofmof-js.googlecode.com/svn/trunk/test/object.js.htm をブラウザで表示し、ブラウザのコンソールを見てみると、(↓)のようなセルフテストの結果が出力されます。これをやってるのが、String#test() です。

これも1つのワンソース・マルチユース。テストもドキュメントもコードに埋め込んでしまえば良いのではないのでしょうか?