2

私は次の動作に気づきました(そして黒点コードで確認しました)

class Foo < ActiveRecord::Base
  def  bar
    search_str = "foo"
    Boo.search do
      keywords(search_str)
      p self.id
      p self
   end
 end
end

上記のコードでは、DSLブロックはコンテキストで定義された変数にアクセスできます。ただし、内部ブロックは、 (クラスのインスタンスではなく )クラスselfのインスタンスを指し ます。オブジェクトのを取得する代わりに、にアクセスしようとすると、オブジェクトのを取得します。Sunspot::DSL::SearchFooself.ididFooidSunspot::DSL::Search

サンポットはメソッドでバインディングスワッピング/委任の魔法を使っていると思います Util.instance_eval_or_call

Sunspotがこれを行う理由と、ドキュメントにこの動作に関する警告がない理由に興味があります。

編集:

黒点の検索方法は、このリンクにあります

以下のコードは私のポイントを示しています。このメソッドfooには、期待どおりに動作するブロックがあります。メソッドbarでは、ブロックは動作しません。

class Order < ActiveRecord::Base  

  def foo
    p self.class.name # prints Order

    # The `self` inside the block passed to the each method
    # points to an object of type Order (as expected)
    # This is the normal block behavior.
    [1,2,3].each do |val|
      p self.class.name # prints Order
    end
  end


  def bar

    p self.class.name # prints Order

    # the `self` inside the block passed to the search method
    # points to an object of type Sunspot::DSL::Search.
    # This is NOT the normal block behavior.

    Order.search do
      keywords("hello")
      p self.class.name # prints Sunspot::DSL::Search
    end
end

注2

通常のブロック動作を変更するコードをSunspotソースツリーに配置しました。私の質問は、このようにバインディングをリギングする理由についてです。

注3

具体的にはid、ブロック側でメソッドを呼び出しているときに問題が見つかりました。メソッドは、ブロック内のsearchメソッド呼び出しをDSLオブジェクトに委任し、メソッドが見つからない場合、呼び出しは呼び出し元のコンテキストに再委任されます。Searchメソッドは、委任コードを登録する前に、DSLオブジェクトから必須メソッドを除くすべてを取り除きます。idメソッドは削除されません。これが問題の原因です。他のすべての方法では、委任は正常に機能します。

この動作は、Sunspotメソッドのドキュメントには記載されていません。

4

2 に答える 2

5

わかりました、私はそれがどのように機能するか知っています:

魔法はContextBoundDelegateutil.rbにあります。

  • 空白のスレート委任オブジェクトを作成します。
  • 委任者は、すべてのメソッド呼び出しを「レシーバー」に転送します。あなたの例では、「レシーバー」はおそらくメソッドkeywordsなどwithを含むオブジェクトany_ofです。
  • 指定されたメソッドが「receiver」に見つからない場合は、メソッド呼び出しを「context」オブジェクトに転送します。
  • コンテキストオブジェクトは、ブロックのバインディングを保持するオブジェクトです。
  • これを行うことにより、特定のブロックのコンテキストオブジェクトを見つけます。eval('self', block.binding)

理論的根拠:

したがって、このすべての効果は、ブロックが検索オブジェクト(a la instance_eval)のメソッドにアクセスできるだけでなく、ブロックの呼び出しスコープのローカルメソッドにもアクセスできることです。

もちろん、ブロックはブロックの呼び出しスコープ内のローカル変数にもアクセスできますが、これは通常の閉包動作です。

ただし、ブロックは、ブロックの呼び出しスコープ内のインスタンス変数にアクセスできません。

次のコードは、ほぼ同じ考え方に従うので便利かもしれませんが、はるかに単純で洗練されていません。2つの異なるスコープのメソッドを使用しますか?

于 2010-09-18T07:16:46.183 に答える
1

ただではないinstance_evalですか?呼び出し元のコンテキストからインスタンス変数にアクセスすることについて話しているのでない限り、これは通常の閉包動作です。

instance_eval(自己の変化)がkeywordsブロックに他の関連するメソッドを提供するために使用されると仮定しています。

于 2010-09-18T05:02:27.067 に答える