3

When creating methods that yield, sometimes we want it to return an Enumerator if no block is given. The recommended way is basically return to_enum(:name_of_method, [args]) unless block_given?. However, it's a pain to have to type that for every method that does this. Ruby being ruby, I decided to create a make_enum method, similar to attr_accessor, which does this for me:

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

Now I can use it like so:

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]

And it works! But it doesn't work if make_enum is before the method definition.

How can I get this method to work before defining a method, so that the following works? Perhaps I need to make use of method_added?

class Test
  make_enum :test

  def test
    yield 1
    yield 2
  end
end

I don't know if it's a bad idea for it to be before the method, but my reason for thinking that it would be nice to do that is that it better matches the way we use attr_accessor and the like.

Community
  • 1
  • 1
Justin
  • 24,288
  • 12
  • 92
  • 142
  • I'm not sure what you want is possible, `test` will not be defined when it is passed to `make_enum` if that method is first. – jkeuhlen Aug 06 '15 at 20:49
  • If you are using rails, you could probably do this using filters actually. – jkeuhlen Aug 06 '15 at 20:50
  • @jkeuhlen I'm using pure Ruby. I know that `test` won't be defined, but perhaps there is a way to defer until `test` *is* defined. – Justin Aug 06 '15 at 20:52
  • Ah dang, yeah with pure Ruby I don't know of a good way to solve that problem since it is interpreted and not compiled. `test` HAS to be defined when it is passed to `make_enum`. is there any real reason you can't do it and the end of the class? – jkeuhlen Aug 06 '15 at 20:53
  • 2
    Not what you want, but since `def` returns the method's name as a symbol (starting with Ruby 2.1), you can write `make_enum def test ...` – Stefan Aug 06 '15 at 20:59
  • BTW, I think it should be `to_enum(name, *args)` (note the splat) – Stefan Aug 06 '15 at 21:00
  • Don't forget to select an answer if you find any of them helpful (though I see there is only one so far). – Cary Swoveland Aug 13 '15 at 23:02

1 Answers1

3

Whereas attr_ methods create instance methods newly, your make_enum modifies an existing method, which is rather similar to protected, private, and public methods. Note that these visibility methods are used either in the form:

protected
def foo; ... end

or

protected def foo; ... end

or

def foo; ... end
protected :foo

The latter two ways are already available with your make_enum. Especially, the second form is already possible (which Stefan also notes in the comment). You can do:

make_enum def test; ... end

If you want to do the first form, you should try to implement that in your make_enum definition.

sawa
  • 165,429
  • 45
  • 277
  • 381