0.11 の変更

ChangeLog には書いていないけど、以下のコードが動かなくなっています。

// _design/app/_show/test
function(doc, req){
  this["hello"] = function(){
     return "hello";
  };
  return hello();
}

eval するコンテキストの違いだとは思うのですが、これが意図した仕様の変更なのか、意図しないけど仕様が変わったのか、それともバグとして認識されるべきものなのか、わからなかったので dev の方に聞いてみた。

なんでこんな書き方をするのか、というと、グローバルレベルでモジュールを拡張/上書きしたいときに

extend(this, {
  f1 : function(){ },
  f2 : function(){ },
  f3 : function(){ },
  ...
});

とかそんなやり方をするので、CouchDBにおけるトップレベルオブジェクトはどうあるべきか、的な話になるのかなぁ。

追記

結論からいうと、show/list のfunction呼び出しのthisは変更するな!モジュール化やその呼び出しには require 使えってことです。

ですが、いろいろ開発陣からMLできけたので、それを自分用メモとして公開しておきます。CouchDB/CouchApp 詳しい人じゃないとわからないかもしれませんが。

まず、0.11 からは、this は、show/ilst の定義されているdesignドキュメントを参照するようになりました。これはCouchAppの !json マクロや !code マクロによる事前展開が不要になることを意味します。

function(doc, req){ 
  // !json t.foo.bar
  return template(template.foo.bar);
}

とかやって、couchapp push 時に app/t/foo/bar.html などのテンプレートをshow/list 関数に展開して埋め込んでいましたが、

function(doc, req){ 
  return template(this.t.foo.bar);
}

という形で参照できるようになったのです。プログラミングモデル上は変化はありませんが、デザインドキュメントの関数が小さくまとまるのでデバッグがかなり楽になりました。

で、今後は、this は readonly で考えた方がいい、というのも distributed な環境で平行してshow/list を走らせる場合に、global 参照するといろいろ問題になるでしょ*1、ということが聞き出せました。つまり、JavaScript部分も分散実行、平行実行を視野にはいれている、ということでしょう。

this がなぜ同じコンテキストの最中に変わってしまうのかは、JS側のソースコードをみないとわかりませんが、少なくとも、this を使ってglobal stateを保持させる、というやり方は、CouchDBJavaScriptではバッドノウハウになった、と考えた方が良さそうです。

*1:this は呼び出しもとを参照するので