2013-05-17 48 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