将String功能添加到类

时间:2013-05-17 12:23:20

标签: ruby string delegates

考虑这个课程

class Duck
  attr_accessor :name

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

  # Some quacking methods..

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

end

我希望我的鸭子回复upcasesubgsub等方法,以便我能做到

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

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

3 个答案:

答案 0 :(得分: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)

答案 1 :(得分: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)

答案 2 :(得分:1)

正如我理解你的问题,你希望有一些字符串模块可以混合到你的Animal类中。但是,String是一个类,从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