2012-03-25 95 views
2

我們有兩個Rails模型:PersonAdministrator。我們在模型級別不允許拆除Administrator S:重新定義調用超類方法的實例上的單個方法

class Person < ActiveRecord::Base 
end 

class Administrator < Person 
    def destroy 
    raise "Can't remove administrators." 
    end 
end 

me = Administrator.new 
me.destroy    # raises an exception 

我希望能夠在測試過程中解決這個問題,但僅限於建立和拆除過程中產生的具體實例。我不想改變班級的行爲,所以class_evalremove_method不可行。

我試圖重新定義實際實例的方法#destroy

def me.destroy 
    super 
end 

或重新定義它的單例類:

class << me 
    def destroy 
    super 
    end 
end 

但那些仍然拋出的異常。我無法弄清楚如何讓它隱式調用超類方法。我結束了創建我自己的destroy!方法(因爲這實際上是不是ActiveRecord的方法),這有點違揹我的願望不會改變類的行爲:

def destroy! 
    ActiveRecord::Persistence.instance_method(:destroy).bind(self).call 
end 

有沒有什麼簡單的方法來告訴單實例方法來調用它的超類方法?


最終的答案:基於文章霍爾格就掛,我可以簡單地顯式調用父類方法:

def me.destroy 
    self.class.superclass.instance_method(:destroy).bind(self).call 
end 

回答

3

我試圖重構行爲,以便更好地測試。例如。你可以允許一個可選參數來銷燬i_know_what_im_doing必須設置爲true才能執行銷燬。另外,您可以用before_destroycanceldestroy

class Administrator < Person 
    def before_destroy(record) 
    # You can't destroy me 
    false 
    end 
end 

在您的測試,你可以調用Administrator.skip_callback :before_destroy忽略它,並有適當的破壞。

最後,您可以在測試中覆蓋/存根方法。雖然你說你不想修改班級的行爲,但你仍然必須這樣做(並且今天用你的destroy!方法隱式做到這一點)。

+0

謝謝,Holger。你的建議很好,但我主要好奇爲什麼我的'super'調用不能調用超類方法。你有什麼想法爲什麼會發生? – Brandan 2012-03-25 15:37:41

+0

使用你的構造,你在'me'實例的單例類(也稱爲特徵類)上創建了重寫的destroy方法。如果你在該方法的範圍內,超級鏈如下:eigenclass - > class - >包含在類中的模塊 - >父類...這意味着你的'destroy'方法在你的'destroy'方法上'me'在你的'Aministrator'類上調用'destroy'方法。有關本徵類概念的更多信息,請參見[本文](http://blog.madebydna.com/all/code/2011/06/24/eigenclasses-demystified.html)。 – 2012-03-25 16:03:29

+0

非常好!那篇文章爲我清除了它。 – Brandan 2012-03-26 13:39:11

1

我不熟悉的紅寶石metaprograming,所以我不會回答你是否可以在不修改它的情況下調用實例上超類的方法。但是你可以創建一個鉤到超法alias

class Administrator < Person 

    alias :force_destroy :destroy 

    def destroy 
    raise "Can't remove administrators." 
    end 
end 

有了這個,admin.destroy將引發異常,但admin.force_destroy實際上將調用ActiveRecord的破壞。