2

My class has a File in it and I want to implement each_line with the same feature as File#each_line so it can be called with a block or without a block. When called without a block, an Enumerator is returned.

Here is my code currently:

  def each_line(&block)
    if block_given?
      read_loop(&block)
    else
      Enumerator.new do |y|
        read_loop(&y)
      end
    end
  end

  private

  def read_loop(&block)
    while line = readline
      block.yield line
    end
  end

I'm wondering if there is a more concise idiomatic way to implement each_line rather than asking if a block was passed and doing very much the same thing only slightly different in each case.

readline is a public method in the class (which does more what File#readline does.

pedz
  • 2,271
  • 1
  • 17
  • 20

1 Answers1

5

That is exactly what the Object#enum_for method is for. It creates an Enumerator from an iteration method that takes a block.

Just put something like this as the first line (after argument validation) of any iteration method you write:

def each_line
  return enum_for(__callee__) unless block_given?

  while line = readline
    yield line
  end
end
Jörg W Mittag
  • 363,080
  • 75
  • 446
  • 653
  • 1
    StackOverflow Article about [`__callee__`](https://stackoverflow.com/questions/35391160/difference-between-callee-and-method) – pedz May 17 '20 at 20:44