Ruby respond_to 的优化问题?和范围界定
Ruby refinement issues with respond_to? and scoping
我正在尝试将实例方法 foo
添加到 Ruby 的 Array
class
所以当它被调用时,数组的字符串元素被更改为字符串 "foo".
这可以通过猴子修补 Ruby 的 String
和 Array
classes.
轻松完成
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 的 refinements feature 重写上面的代码。
我正在使用 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
class 时,我无法让它工作。
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
有两个问题:
- 使用新方法优化 class 后,即使您可以调用
respond_to?
也不起作用
对象上的方法。尝试添加 puts 'yes' if s.respond_to? :foo
作为第二个代码片段的最后一行,您会看到 'yes' 是 not printed.
- 在我的数组优化中,String#foo 超出了范围。如果您从中删除
if x.respond_to? :foo
Array#foo,你会得到错误 undefined method 'foo' for "a":String (NoMethodError)
。所以问题是:如何使 String#foo 细化在 Array#foo 细化中可见?
我该如何克服这两个问题才能让它发挥作用?
(请不要提供不涉及细化的替代解决方案,因为这是一个理论练习,所以我可以学习如何使用细化)。
谢谢。
respond_to?
方法不起作用,这已记录在案
here.
问题是您只能在顶级激活细化
它们在范围内是词法的。
一个解决方案是:
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
再次以您的示例为例,一个简单的解决方案可能是覆盖 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'
我正在尝试将实例方法 foo
添加到 Ruby 的 Array
class
所以当它被调用时,数组的字符串元素被更改为字符串 "foo".
这可以通过猴子修补 Ruby 的 String
和 Array
classes.
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 的 refinements feature 重写上面的代码。 我正在使用 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
class 时,我无法让它工作。
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
有两个问题:
- 使用新方法优化 class 后,即使您可以调用
respond_to?
也不起作用 对象上的方法。尝试添加puts 'yes' if s.respond_to? :foo
作为第二个代码片段的最后一行,您会看到 'yes' 是 not printed. - 在我的数组优化中,String#foo 超出了范围。如果您从中删除
if x.respond_to? :foo
Array#foo,你会得到错误undefined method 'foo' for "a":String (NoMethodError)
。所以问题是:如何使 String#foo 细化在 Array#foo 细化中可见?
我该如何克服这两个问题才能让它发挥作用?
(请不要提供不涉及细化的替代解决方案,因为这是一个理论练习,所以我可以学习如何使用细化)。
谢谢。
respond_to?
方法不起作用,这已记录在案 here.问题是您只能在顶级激活细化 它们在范围内是词法的。
一个解决方案是:
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
再次以您的示例为例,一个简单的解决方案可能是覆盖 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'