轻松创建枚举器

Easily create an Enumerator

在创建 yield 的方法时,有时我们希望它 return 一个 Enumerator 如果没有给出块。 recommended way 基本上是 return to_enum(:name_of_method, [args]) unless block_given?。但是,必须为每个执行此操作的方法键入它是一件痛苦的事情。 Ruby 是 ruby,我决定创建一个 make_enum 方法,类似于 attr_accessor,这对我来说是这样的:

class Module # Put this in a mixin, but for the purposes of this experiment, it's in Module
  def make_enum *args
    args.each do |name|
      old_method = instance_method(name)
      define_method(name) do |*args, &block|
        next to_enum(name, *args) unless block
        old_method.bind(self).call(*args, &block)
      end
    end
  end
end

现在我可以像这样使用它了:

class Test
  def test
    yield 1
    yield 2
  end

  make_enum :test
end

t = Test.new
t.test { |n| puts n }
# 1
# 2
t.test.to_a #=> [1, 2]

而且有效!但如果 make_enum 在方法定义之前,它就不起作用。

如何在定义方法之前让这个方法起作用,以便下面的方法起作用?也许我需要利用 method_added?

class Test
  make_enum :test

  def test
    yield 1
    yield 2
  end
end

我不知道把它放在方法之前是否是个坏主意,但我认为这样做会很好的原因是它更符合我们使用的方式 attr_accessor等等。

虽然 attr_ 方法新创建实例方法,但您的 make_enum 修改现有方法,这与 protectedprivate 和 [=18= 非常相似] 方法。请注意,这些可见性方法以以下形式使用:

protected
def foo; ... end

protected def foo; ... end

def foo; ... end
protected :foo

您的 make_enum 已经可以使用后两种方式。特别是,第二种形式已经成为可能(Stefan 在评论中也提到了这一点)。你可以这样做:

make_enum def test; ... end

如果你想做第一种形式,你应该尝试在你的 make_enum 定义中实现它。