Ruby 细化细节
Ruby refinements subtleties
这里 ruby 中有关于当前改进实施的非常好的文档:
http://ruby-doc.org//core-2.2.0/doc/syntax/refinements_rdoc.html,
但是有一些奇怪的极端情况。
首先,include module
与using module
正交(一个包含模块的实例方法,一个激活细化)。但是有一个技巧可以包含一个细化模块本身,请参阅
Better way to turn a ruby class into a module than using refinements?.
def to_module(klass)
Module.new do
#note that we return the refinement module itself here
return refine(klass) {
yield if block_given?
}
end
end
class Base
def foo
"foo"
end
end
class Receiver
include to_module(Base) {
def foo
"refined " + super
end
}
end
Receiver.new.foo #=> "refined foo"
奇怪的是这个优化模块不能与using
一起使用!
m=to_module(Base) {}
m.class #=> Module
using m
#=>TypeError: wrong argument type Class (expected Module)
所以只使用在优化模块的封闭模块上工作。
其次,我想使用上面的 yield 技巧来传递一个 Proc 来优化(即使它只接受一个块),而不像在中那样求助于将 Proc 转换回源
https://www.new-bamboo.co.uk/blog/2014/02/05/refinements-under-the-knife/。
但是在包含示例中使用 yield
不起作用:
def ref_module1(klass)
Module.new do
refine(klass) {
yield
}
end
end
class Receiver1
using ref_module1(Base) {
def foo
"refined " + super
end
}
def bar
Base.new.foo
end
end
Receiver1.new.bar #=> NoMethodError: super: no superclass method `foo'
我们看到 Receiver1 仍然使用 Bar#foo 而不是改进的方法。
但是我们可以使用 module_eval
代替:
def ref_module2(klass,&b)
Module.new do
refine(klass) {
module_eval(&b)
}
end
end
class Receiver2
using ref_module2(Base) {
def foo
"refined " + super
end
}
def bar
Base.new.foo
end
end
Receiver2.new.bar #=> "refined foo"
我不太明白为什么 module_eval
在这里工作而不是 yield
方法。在细化块内部,'default_definee' 是细化模块,所以 module_eval
将 'default_definee' 放到 self
='the refinement module' 应该不会影响它。事实上,在开头的 'include' 示例中,当我使用 module_eval
或直接 yield
.
时,我得到了相同的结果
谁能解释这种行为?
上下文(或绑定)是 module_eval 起作用而 yield 在您最后一组示例中不起作用的原因。它实际上与改进无关,如下所示。
从 module_eval
开始:
class Foo
def run(&block)
self.class.module_eval(&block)
end
end
foo = Foo.new
foo.run {
def hello
"hello"
end
}
puts foo.hello # => "hello"
puts hello => # '<main>': undefined method 'hello' for main:Object (NameError)
在 Foo#run
中,我们在 Foo
上调用 module_eval
。这会将上下文 (self
) 切换为 Foo
。结果很像我们最初在 class Foo
中简单定义了 hello
。
现在让我们来看看yield
:
class Foo
def run
yield
end
end
foo = Foo.new
foo.run {
def hello
"hello"
end
}
puts hello # => "hello"
puts foo.hello # => '<main>': private method 'hello' called for ...
yield
只是在其原始上下文中调用该块,在本例中为 <main>
。调用块时,最终结果与正常在顶层定义方法完全相同:
class Foo
def run
yield
end
end
foo = Foo.new
def hello
"hello"
end
puts hello # => "hello"
puts foo.hello # => '<main>': private method 'hello' called for ...
您可能会注意到 foo
在 yield
示例中似乎有 hello
方法。这是将 hello
定义为顶层方法的副作用。事实证明,<main>
只是 Object
的一个实例,定义顶级方法实际上只是在 Object
上定义私有方法,几乎所有其他方法最终都会继承。您可以通过打开 irb 并 运行ning 以下内容来查看:
self # => main
self.class # => Object
def some_method
end
"string".method(:some_method) # => #<Method: String(Object)#some_method>
现在回到你的例子。
这是 yield
示例中发生的情况:
def ref_module1(klass)
Module.new do
refine(klass) {
yield
}
end
end
class Receiver1
# like my yield example, this block is going to
# end up being invoked in its original context
using ref_module1(Base) {
def foo
"I'm defined on Receiver1"
end
}
def bar
# calling foo here will simply call the original
# Base#foo method
Base.new.foo
end
end
# as expected, if we call Receiver1#bar
# we get the original Base#foo method
Receiver1.new.bar # => "foo"
# since the block is executed in its original context
# the method gets defined in Receiver1 -- its original context
Receiver1.new.foo # => "I'm defined on Receiver1"
至于 module_eval
,它在您的示例中有效,因为它导致块在新模块的上下文中为 运行,而不是在 Receiver1
class.
这里 ruby 中有关于当前改进实施的非常好的文档: http://ruby-doc.org//core-2.2.0/doc/syntax/refinements_rdoc.html, 但是有一些奇怪的极端情况。
首先,include module
与using module
正交(一个包含模块的实例方法,一个激活细化)。但是有一个技巧可以包含一个细化模块本身,请参阅
Better way to turn a ruby class into a module than using refinements?.
def to_module(klass)
Module.new do
#note that we return the refinement module itself here
return refine(klass) {
yield if block_given?
}
end
end
class Base
def foo
"foo"
end
end
class Receiver
include to_module(Base) {
def foo
"refined " + super
end
}
end
Receiver.new.foo #=> "refined foo"
奇怪的是这个优化模块不能与using
一起使用!
m=to_module(Base) {}
m.class #=> Module
using m
#=>TypeError: wrong argument type Class (expected Module)
所以只使用在优化模块的封闭模块上工作。
其次,我想使用上面的 yield 技巧来传递一个 Proc 来优化(即使它只接受一个块),而不像在中那样求助于将 Proc 转换回源
https://www.new-bamboo.co.uk/blog/2014/02/05/refinements-under-the-knife/。
但是在包含示例中使用 yield
不起作用:
def ref_module1(klass)
Module.new do
refine(klass) {
yield
}
end
end
class Receiver1
using ref_module1(Base) {
def foo
"refined " + super
end
}
def bar
Base.new.foo
end
end
Receiver1.new.bar #=> NoMethodError: super: no superclass method `foo'
我们看到 Receiver1 仍然使用 Bar#foo 而不是改进的方法。
但是我们可以使用 module_eval
代替:
def ref_module2(klass,&b)
Module.new do
refine(klass) {
module_eval(&b)
}
end
end
class Receiver2
using ref_module2(Base) {
def foo
"refined " + super
end
}
def bar
Base.new.foo
end
end
Receiver2.new.bar #=> "refined foo"
我不太明白为什么 module_eval
在这里工作而不是 yield
方法。在细化块内部,'default_definee' 是细化模块,所以 module_eval
将 'default_definee' 放到 self
='the refinement module' 应该不会影响它。事实上,在开头的 'include' 示例中,当我使用 module_eval
或直接 yield
.
谁能解释这种行为?
上下文(或绑定)是 module_eval 起作用而 yield 在您最后一组示例中不起作用的原因。它实际上与改进无关,如下所示。
从 module_eval
开始:
class Foo
def run(&block)
self.class.module_eval(&block)
end
end
foo = Foo.new
foo.run {
def hello
"hello"
end
}
puts foo.hello # => "hello"
puts hello => # '<main>': undefined method 'hello' for main:Object (NameError)
在 Foo#run
中,我们在 Foo
上调用 module_eval
。这会将上下文 (self
) 切换为 Foo
。结果很像我们最初在 class Foo
中简单定义了 hello
。
现在让我们来看看yield
:
class Foo
def run
yield
end
end
foo = Foo.new
foo.run {
def hello
"hello"
end
}
puts hello # => "hello"
puts foo.hello # => '<main>': private method 'hello' called for ...
yield
只是在其原始上下文中调用该块,在本例中为 <main>
。调用块时,最终结果与正常在顶层定义方法完全相同:
class Foo
def run
yield
end
end
foo = Foo.new
def hello
"hello"
end
puts hello # => "hello"
puts foo.hello # => '<main>': private method 'hello' called for ...
您可能会注意到 foo
在 yield
示例中似乎有 hello
方法。这是将 hello
定义为顶层方法的副作用。事实证明,<main>
只是 Object
的一个实例,定义顶级方法实际上只是在 Object
上定义私有方法,几乎所有其他方法最终都会继承。您可以通过打开 irb 并 运行ning 以下内容来查看:
self # => main
self.class # => Object
def some_method
end
"string".method(:some_method) # => #<Method: String(Object)#some_method>
现在回到你的例子。
这是 yield
示例中发生的情况:
def ref_module1(klass)
Module.new do
refine(klass) {
yield
}
end
end
class Receiver1
# like my yield example, this block is going to
# end up being invoked in its original context
using ref_module1(Base) {
def foo
"I'm defined on Receiver1"
end
}
def bar
# calling foo here will simply call the original
# Base#foo method
Base.new.foo
end
end
# as expected, if we call Receiver1#bar
# we get the original Base#foo method
Receiver1.new.bar # => "foo"
# since the block is executed in its original context
# the method gets defined in Receiver1 -- its original context
Receiver1.new.foo # => "I'm defined on Receiver1"
至于 module_eval
,它在您的示例中有效,因为它导致块在新模块的上下文中为 运行,而不是在 Receiver1
class.