1

我正在構建一個Rails 3 gem,它基本上修改了從ActiveRecord查詢返回的記錄。我正在做的一件事是重寫method_missingrespond_to?方法,但似乎我的respond_to?定義導致了一個無限循環,導致「SystemStackError:堆棧層太深」錯誤。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?輸出「後」,看看它卡住。

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

運行時,似乎各種回調和屬性運行方法如預期,有單前和呼叫後每個:

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 
... 

但是,任何時候我看到查找回調,那似乎正在陷入一個死循環:

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 

如果我砍我的respond_to?,那麼一切似乎順利運行:

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沒有在模型實例上定義,直到執行查找回調之後,並且因爲在查找回調被調用(在我的原始問題中包含代碼否)時我第一次調用respond_to?,所以調用send(parent_assocation_name)正在對respond_to?進行遞歸調用。

+1

如果你使用精確的語言,它會更清晰。 'parent_association_name'不是一個變量,我認爲'employee'總是被定義的,儘管它可能不會被加載。不確定你實際上是否指「回調」。我仍然不確定你所描述的不是我所說的,也不是你如何修正它。 – 2012-04-09 20:05:52

0

如果parent_association返回一個新的AR對象,它也將繼承你的黑客,所以要求它respond_to?會打電話給你respond_to?,這將創建一個AR新的對象,等...

+0

如果它創建的另一個ActiveRecord對象實際上實現了方法,這應該是這裏的情況。例如,另一個AR對象將有一個類似於'.name'的方法(由於實現了屬性),原始AR對象可能沒有這個方法。在某些時候,它只是調用'super'並返回,因爲該方法實際上是在對象上定義的。 – 2012-03-13 14:46:58

+0

只是爲了跟進,它確實最終通過'respond_to?'進行遞歸,但它不是一個被稱爲在單獨AR對象上遞歸定義的函數的問題,正如您所提到的。 – 2012-04-09 19:22:09