2013-05-17 66 views
3

考慮這個類添加字符串功能

class Duck 
    attr_accessor :name 

    def initialize(name) 
    @name = name || 'Donald' 
    end 

    # Some quacking methods.. 

    def to_s 
    "#{@name} (Duck)" 
    end 

end 

我想我的鴨子給方法,如upcase迴應,subgsub等..,所以我可以做

my_duck = Duck.new("Scrooge") 
my_duck.upcase 
    --> "SCROOGE (DUCK)" 

除了手動實現這些方法,是否有一種漂亮的方式可以挑出不是自變異的String方法,並自動讓我的類對這些方法做出響應,然後調用to_s,然後調用結果字符串上的方法?

+3

'鴨#chop','鴨#slice','鴨#squeeze' :-) – Stefan

回答

7

您可以使用Forwardable模塊:

require 'forwardable' 

class Duck  
    extend Forwardable 

    # This defines Duck#upcase and Duck#sub, you can 
    # add as many methods as you like. 
    def_delegators(:to_s, :upcase, :sub) 

    # All the other code here... 
end 

Duck.new('duffy').upcase 
# => DUFFY (DUCK) 

Duck.new('rubber').respond_to?(:upcase) 
# => true 

一般調用def_delegators(:foo, :bar)相當於手工定義bar方法是這樣:

def bar(*args, &block) 
    foo.bar(*args, &block) 
end 

def_delegators的第一個參數可以是實例變量的名稱,即def_delegators(:@foo, :bar, :baz)

+0

有趣,但你怎麼知道它是委託給String類? – poseid

+0

@poseid因爲'to_s'返回一個字符串。 – toro2k

+0

@ toro2k:非常酷。 –

1

正如我理解你的問題,你想有一些東西作爲一個字符串模塊,你可以混入你的動物類。但是,String是一個類,從String訪問方法的唯一方法是繼承String和類之間緊密耦合的負擔。

如果您需要重新使用了大量的字符串操作,我將定義一個模塊:

module StringMixins 
    def upcase 
    # see e.g. Rubinius link below 
    end 

    # ... 
end 

include StringMixins到你的目標類。這將是這樣的:

class Duck 
    include StringMixins 

    # ... 
end 

Rubinius的upcase實現:https://github.com/rubinius/rubinius/blob/master/kernel/common/string.rb#L735-L738

2

您可以使用method_missing方法來檢查您的to_s實現的返回值是否響應此方法並調用它(如果是這種情況)。

class Duck 
    attr_accessor :name 

    def initialize(name) 
     @name = name || 'Donald' 
    end 

    # Some quacking methods.. 

    def to_s 
     "#{@name} (Duck)" 
    end 

    def method_missing(m, *args, &block) 
     raise NoMethodError unless self.to_s.respond_to? m 
     self.to_s.send(m, *args, &block) 
    end 

    end 

    d = Duck.new "Donald" 
    puts d.upcase # DONALD (DUCK) 
    puts d.swapcase # dONALD (dUCK) 
    puts d.downcase # donald (duck) 
    puts d.sub('D') { |m| m.downcase } # donald (Duck) 
+0

這是一個有趣的解決方案,它使得它非常容易添加到所有的String方法中。但是,我認爲它在可讀性方面有成本。例如,如果您執行'duck.methods',您將看不到委託,這意味着任何'respond_to?'調用都將失敗。此外,如果您想將代理添加到除String和to_s之外的其他源,我可以想象'method_missing'處理程序變得不整潔。 –

+0

這些確實有效! – maerch