2009-06-18 51 views
3

我與紅寶石玩弄,我寫了下面的代碼:包括模塊定義動態類方法

module IdAndNameRedefine 
    def self.included(base) 
    base.extend(ClassMethods) 
    end 

    module ClassMethods 
    def use_id_and_name_from_module(use_attribute) 
     class_eval <<CODE 
     def id_and_name 
      "\#{id}-\#{#{use_attribute}.downcase}" 
     end 
CODE 
    end 
    end 
end 


class Model 
    include IdAndNameRedefine 

    attr_reader :id, :name1, :name2 

    def initialize(id, name1, name2) 
    @id = id 
    @name1 = name1 
    @name2 = name2 
    end 

    def id_and_name 
    "#{id}-#{name1}" 
    end 

    use_id_and_name_from_module :name2 
end 

model = Model.new(1, "TesT", "Test number TWO") 
puts model.id_and_name 

當我想在這裏做的是覆蓋類方法id_and_name類使用IdAndNameRedefine模塊動態插入模型。當包含該模塊時,它將創建一個「靜態」方法(實際上,我理解它是Model.class的一個類方法),並調用use_id_and_name_from_module時,它將在Model中創建一個類方法,該方法重新定義id_and_name以使用任何屬性模型的要求。

我的問題是...... 有沒有更好的方法來做到這一點,或者這是做到這一點的「正確」方式?我真的不確定是否喜歡class_eval,並且需要一個字符串,以便我需要轉義值等來完成這項工作。

回答

5

您不必將字符串傳遞給class_eval。它可以取而代之。事實上,我無法想象我必須將字符串傳遞給class_eval。所以我們可以重寫ClassMethods模塊,像這樣:

module ClassMethods 
    def use_id_and_name_from_module(use_attribute) 
    class_eval do 
     define_method(:id_and_name) {"#{id}-#{send(use_attribute).downcase}"} 
    end 
    end 
end 

但是,在這種特殊情況下,我們只是告訴selfclass_eval,這意味着我們已經在這個類的上下文。因此,它實際上可以簡化爲:

module ClassMethods 
    def use_id_and_name_from_module(use_attribute) 
    define_method(:id_and_name) {"#{id}-#{send(use_attribute).downcase}"} 
    end 
end 

(我只是想說明如何class_eval真正的作品,因爲你似乎最感興趣的部分。)

+0

謝謝!我認爲這看起來有點愚蠢,但我找不出一個更好的方式來做到這一點。我相信我也嘗試了define_method,但是那一點我在類定義的頂部有`use_id_and_name_from_module``調用,因此它不起作用(在我後面的類中被聲明覆蓋)。 只是出於好奇;有什麼方法可以將`use_id_and_name_from_module`放在類的頂部,並確保它在以後的聲明中不會被覆蓋。 – 2009-06-18 08:24:24