3

考虑这个类

class Duck
  attr_accessor :name

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

  # Some quacking methods..

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

end

我希望我的鸭子能够响应 , , 等方法upcasesub所以gsub我可以这样做

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

除了手动实现这些方法之外,是否有一种巧妙的方法可以挑选出不会自变异的 String 方法并自动让我的类响应这些方法,然后调用to_s结果字符串上的方法?

4

3 回答 3

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)

于 2013-05-17T12:36:01.120 回答
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)
于 2013-05-17T12:49:37.657 回答
1

据我了解您的问题,您希望有一些作为字符串模块的东西,您可以将其混入您的 Animal 类中。但是,String 是一个类,从 String 访问方法的唯一方法是继承 String 和类之间紧密耦合的负担。

如果您需要重用大量字符串操作,我会定义一个模块:

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

  # ...
end

include StringMixins进入您的目标课程。那看起来像:

class Duck
  include StringMixins

  # ...
end

Rubinius 大写实现:https ://github.com/rubinius/rubinius/blob/master/kernel/common/string.rb#L735-L738

于 2013-05-17T12:35:48.983 回答