1

私は本質的にActiveRecordクエリから返されたレコードを変更するRails 3 gemを構築しています。私がやっていることの1つは、method_missingrespond_to?のメソッドをオーバーライドしていますが、私のrespond_to?の定義の結果、 "SystemStackError:stack level too deep"エラーが発生する無限ループが発生しているようです。ここでObject.respond_to?無限ループに詰まっています

は、これらの方法の私の元の定義は以下のとおりです。この無限ループが発生した理由を学ぶことを試みることに

def respond_to?(name, *args) 
    super(name, *args) || parent_association.respond_to?(name) 
end 

def method_missing(name, *args, &block) 
    if parent_association.respond_to?(name) 
    parent_association.send(name, *args, &block) 
    else 
    super(name, *args, &block) 
    end 
end 

def parent_association 
    send(parent_association_name) # Essentially yields another ActiveRecord 
           # instance (e.g.: instance of User), but 
           # never returns itself. 
end 

、私はそれが立ち往生だ場所を確認するために、いくつかの「前」と出力「後」とrespond_to?を再編しました。

before (_run__374051839217347232__initialize__1707831318230746190__callbacks) 
after 
before (_run__374051839217347232__validation__1707831318230746190__callbacks) 
after 
before (_run__374051839217347232__validate__1707831318230746190__callbacks) 
after 
before (_run__374051839217347232__save__1707831318230746190__callbacks) 
after 
before (_run__374051839217347232__create__1707831318230746190__callbacks) 
after 
before (created_at) 
after 
before (created_on) 
after 
... 

しかし、いつでも私がいることを、検索のコールバックを参照してください。

def respond_to?(name, *args) 
    return true if super(name, *args) 
    puts "before (#{name})" 
    result = parent_association.respond_to?(name) 
    puts "after" 
    result 
end 

ランニング

が、予想通り、様々なコールバックとは、それぞれの前と呼び出し後に単一で、実行する方法を属性と思われます無限ループに陥っているように見える:私は私の respond_to?をハックした場合

before (_run__374051839217347232__find__1707831318230746190__callbacks) 
before (_run__374051839217347232__find__1707831318230746190__callbacks) 
before (_run__374051839217347232__find__1707831318230746190__callbacks) 
before (_run__374051839217347232__find__1707831318230746190__callbacks) 
before (_run__374051839217347232__find__1707831318230746190__callbacks) 
before (_run__374051839217347232__find__1707831318230746190__callbacks) 
before (_run__374051839217347232__find__1707831318230746190__callbacks) 
before (_run__374051839217347232__find__1707831318230746190__callbacks) 
... 
SystemStackError: stack level too deep 

、その後、すべてがスムーズに実行するために表示されます。

def respond_to?(name, *args) 
    return true if super(name, *args) 
    return false if name =~ /^_run_.*_find_.*_callbacks$/ 
    parent_association.respond_to?(name) 
end 

このハッキングが必要なように私は間違っていますか?そして、どうすればそれを避けることができますか?

答えて

0

問題は、ことになりましたこの機能:

def parent_association 
    send(parent_association_name) # Essentially yields another ActiveRecord 
           # instance (e.g.: instance of User), but 
           # never returns itself. 
end 

変数parent_association_nameのようなものを介して定義されてemployeeのようなものです:

belongs_to :employee 

employeeはfindコールバックが実行されるまでモデルインスタンスに定義されていないため、findコールバックが呼び出される前に私が最初にrespond_to?を呼び出していたため(再帰的な呼び出しをrespond_to?に引き起こしていました。

+1

正確な言葉を使った方がより明確になります。 'parent_association_name'は変数ではなく、' employee'は常に定義されていると思いますが、ロードされていない可能性があります。あなたが実際に "コールバック"を意味するかどうかは不明です。私はあなたが描いていることが、私が言っていたことでもなく、あなたがそれをどのように固定したのかもまだ分かりません。 –

0

parent_associationが新しいARオブジェクトを返した場合、それはまたそれにrespond_to?を呼び出すことなど、AR新しいオブジェクトを作成しますあなたのrespond_to?を、呼び出します、あなたのハックを継承します...

+0

実際に作成する他のActiveRecordオブジェクトが実際にこのメソッドを実装している場合、それは当てはまりません。例えば、他のARオブジェクトは、元のARオブジェクトにはなかった(属性が実装されているため) '.name'のようなメソッドを持ちます。メソッドが実際にオブジェクト上に定義されているので、ある時点で、単に 'super'を呼び出して戻ります。 –

+0

フォローアップするだけで、 'respond_to? 'で再帰が終了しましたが、あなたが言及したように、別々のARオブジェクトで再帰的に未定義と呼ばれる関数の問題ではありませんでした。 –

関連する問題