Ruby精炼细微之处

前端之家收集整理的这篇文章主要介绍了Ruby精炼细微之处前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
这里有关于 ruby中当前优化实现的很好的文档:
http://ruby-doc.org//core-2.2.0/doc/syntax/refinements_rdoc.html,
但是有一些奇怪的角落案例.

首先,include模块与使用模块正交(一个包括模块的实例方法,另一个激活细化).但是有一个技巧包括一个改进模块本身,请参阅
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"

奇怪的是,这个改进模块不能与使用一起使用!

m=to_module(Base) {}
m.class #=> Module
using m    
#=>TypeError: wrong argument type Class (expected Module)

因此,仅使用细化模块的封闭模块.
其次我想使用上面的yi​​eld技巧来传递一个Proc来进行改进(即使它只接受一个块),而不需要将Proc转换回源代码,如同
https://www.new-bamboo.co.uk/blog/2014/02/05/refinements-under-the-knife/.
但是在include示例中使用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’是细化模块,因此将’default_definee’置于self =’细化模块’的module_eval不应该影响它.事实上,在开头的’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.结果很像我们原来在类Foo中有简单定义的hello.

现在让我们来看看收益率:

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并运行以下命令来查看:

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类上运行.

原文链接:https://www.f2er.com/ruby/268091.html

猜你在找的Ruby相关文章