2012-12-12 31 views
8

在Ruby 1.9.3我需要創建幾個類的實例,其中每個具有類似的實例和類的方法但只由幾個固定的參數而變化。它們的類類型的區別也很重要,所以我不能簡單地使用同一類的單獨實例。定義類方法在動態紅寶石

簡化示例看起來像這樣。

module Animal 
private 
    def self.make_animal(name, legs, noise) 
    klass = Class.new 
    klass.const_set(:NUM_LEGS, legs) 
    klass.class.send(:define_method, :scream) { noise.upcase + '!' } 
    Animal.const_set(name, klass) 
    end 
    make_animal :Tiger, 4, 'roar' 
    make_animal :Human, 2, 'derp' 
end 

這似乎不同之處在於,其中動態地定義了「尖叫」的方法塊中使用的變量在「尖叫」方法而不是「make_animal」方法的運行時間的運行時間綁定到正常工作。

Animal::Human::NUM_LEGS # => 2 -- ok 
Animal::Tiger::NUM_LEGS # => 4 -- ok 
Animal::Human.scream # => "DERP!" -- ok 
Animal::Tiger.scream # => "DERP!" -- fail! 

如何修改上面的代碼讓老虎慘叫"ROAR!"

[注意]我真的需要在示例中保持愚蠢的OO結構,這是因爲這裏涉及太多的原因。我只感興趣的是學習如何用參數化方法實現以編程方式定義動態定義的類上的類方法。

回答

10

klass.class是在兩種情況下(類)是相同的:所有類的Class實例。因此,你正在定義尖叫,然後重新定義它。

在ruby中常常被認爲是類方法,實際上是單例方法(如果你感興趣的話,有很多東西需要閱讀特徵類等)。

def some_object.foo 
end 

構建產生單態方法。很多時候,這將是一個類定義內,使用自但是如果你

x = 'dog' 
def x.bark 
    "Woof" 
end 

然後x.bark會返回緯你可以做任何事情,例如,但樹皮不會對任何其他被定義串。

這裏您的方法需要引用您的noise變量,因此您需要使用define_singleton_method來定義您的方法。

如果你仍然在紅寶石1.8不能使用define_singleton_method - 你需要使用的事實,單方法是在eigenclass方法。

klass = Class.new 
eigenclass = class << klass; self; end 
eigenclass.send(:define_method, :scream){noise} 

等同於使用define_singleton_method

+5

啊,是的,'define_singleton_method'。忘了它:) –

+0

是的,「define_singleton_method」似乎是我失蹤。 – maerics

4

與您的代碼的問題是沒有錯的綁定的時刻。這就是你正在定義方法Class#class。驚奇的是,它是唯一的,只有Class。所以你用「derp」版本覆蓋了「roar」版本。

相反,你應該直接定義這些動態類的方法。這裏是我拿(它使用了noise比如VAR,希望這不是一個問題)。

module Animal 
private 
    def self.make_animal(name, legs, noise) 
    klass = Class.new 
    klass.const_set(:NUM_LEGS, legs) 
    klass.instance_variable_set(:@noise, noise) 

    klass.instance_eval do |k| 
     def scream 
     @noise.upcase + '!' 
     end 
    end 

    Animal.const_set(name, klass) 
    end 

    make_animal :Tiger, 4, 'roar' 
    make_animal :Human, 2, 'derp' 
end 

Animal::Human::NUM_LEGS # => 2 
Animal::Tiger::NUM_LEGS # => 4 

Animal::Human.scream # => "DERP!" 
Animal::Tiger.scream # => "ROAR!" 
+0

是啊,好趕上在'klass.class ...'位,我傻=) – maerics