Forwarding an iterator

Viewed 52

I have class (highly simplified for the sake of this discussion)

class MyClass
  def initialize
    @myrecs = %w(a b c d)
  end
  def each_rec
    @myrecs.each {|r| yield(r)}
  end
end

and use it as

x = MyClass.new
x.each_rec { |r| .... }

Since my method each_rec basically does just a myrecs.each, I wanted to define somehow that each_rec just forwards to each. I can achieve the desired effect by explicitly passing the block, i.e.

def each_rec(&block)
  @myrecs.each(&block)
end

but I wonder wether it is also possible to achieve my goal without having the block as explicit parameter. I tried the following approaches without success:

(1) Exploit the fact that each returns an Enumerator when called without a block:

  def each_rec
    @myrecs.each
  end

(2) Create an Enumerator:

   def each_rec
     @myrecs.enum_for(:each)
   end

In both cases, I did not get an error, but the block passed to each_rec was simply not entered.

1 Answers

In Ruby, if you call Proc.new without a block inside a method that received a block, it'll convert that method's passed block to a proc. So, you can do:

class MyClass
  def initialize
    @myrecs = %w(a b c d)
  end
  def each_rec
    @myrecs.each(&Proc.new)
  end
end

x = MyClass.new
x.each_rec { |r| puts r }

and now, you no longer need an explicit parameter to your each_rec method. Now, however, you must supply a block to the method, or get an ArgumentError ("tried to create Proc object without a block"). We can fix that with a guard clause:

def each_rec
  return @myrecs.each unless block_given?

  @myrecs.each(&Proc.new)
end

and can now use it just like each:

x = MyClass.new
x.each_rec { |r| p r }
x.each_rec.with_index { |r, i| p [r, i] }
Related