2

我正在尝试向fooRuby 的Array类添加一个实例方法,因此当它被调用时,数组的字符串元素将更改为字符串“foo”。

这可以通过猴子修补 RubyStringArray类来轻松完成。

class String
  def foo
    replace "foo"
  end
end

class Array
  def foo
    self.each {|x| x.foo if x.respond_to? :foo }
  end
end

a = ['a', 1, 'b']
a.foo
puts a.join(", ")   # you get 'foo, 1, foo' as expected

现在我正在尝试使用 Ruby 2 的改进功能重写上述内容。我正在使用 Ruby 版本 2.2.2。

以下工作(在文件中,例如 ruby​​ test.rb,但由于某种原因不在 irb 中)

module M
  refine String do
    def foo
      replace "foo"
    end
  end
end

using M
s = ''
s.foo
puts s      # you get 'foo'

foo但是,在添加到Array班级时我无法让它工作。

module M
  refine String do
    def foo
      replace "foo"
    end
  end
end

using M

module N
  refine Array do
    def foo
      self.each {|x| x.foo if x.respond_to? :foo }
    end
  end
end

using N

a = ['a', 1, 'b']
a.foo
puts a.join(", ")   # you get 'a, 1, b', not 'foo, 1, foo' as expected

有两个问题:

  1. 使用新方法优化类后,respond_to?即使您可以在对象上调用该方法,也无法正常工作。尝试puts 'yes' if s.respond_to? :foo 在第二个代码片段的最后一行添加,你会看到“是”没有打印出来。
  2. 在我的 Array 改进中, String#foo 超出了范围。如果你if x.respond_to? :foo从 Array#foo 中删除,你会得到错误undefined method 'foo' for "a":String (NoMethodError)。所以问题是:如何使 String#foo 细化在 Array#foo 细化中可见?

我如何克服这两个问题才能让它发挥作用?

(请不要提供不涉及细化的替代解决方案,因为这是一个理论练习,所以我可以学习如何使用细化)。

谢谢你。

4

2 回答 2

1
  1. respond_to?方法不起作用, 此处记录了这一点。

  2. 问题是您只能在顶级激活细化,并且它们在范围内是词法的。

一种解决方案是:

module N
  refine String do
    def foo
      replace 'foobar'
    end
  end

  refine Array do
    def foo
      self.each do |x|
        x.foo rescue x
      end
    end
  end
end

using N

a = ['a', 1, 'b']
p a.foo

puts a.join(", ") # foo, 1, foo
于 2015-08-12T17:56:51.480 回答
0

再次以您的示例为例,一个简单的解决方案可能是覆盖 respond_to? 细化块中的方法:

module M
  refine String do
    def foo
      replace "foo"
    end
    def respond_to?(name,all=false)
      list_methods = self.methods.concat [:foo]
      list_methods.include? name
    end
  end

  refine Array do
    def foo
      self.each {|x| x.foo if x.respond_to? :foo }
    end
  end

end

using M

a = ['a', 1, 'b']
a.foo
puts a.join(", ")    # you get 'foo, 1, foo'
于 2018-04-18T13:06:20.987 回答