How to dry up an Enumerator in Ruby

Viewed 62

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.

1 Answers

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
Related