Adding *each*

I'm trying to wrap my head around how to do an "each" for objects of a class that I have created.

Consider this contrived example
  class X
    def initialize
      @x = [10, 'a', 'c', 9, 8]
    end
  end

In Ruby 1.8, what's the magic incantation so that I can do

  x = X.new
  x.each {|y| puts y}

I have read and reread Dave Thomas'*Programming Ruby 1.9* and ... well ... I think I have to define *each* in class X and add some enumerable stuff ... but ... I just don't get it.

class X
  include Enumerable

  def initialize
    @x = [10, 'a', 'c', 9, 8]
  end

  def each(&block)
    @x.each(&block)
  end
end

···

On Sun, Aug 22, 2010 at 2:45 AM, Ralph Shnelvar <ralphs@dos32.com> wrote:

I'm trying to wrap my head around how to do an "each" for objects of a class that I have created.

Consider this contrived example
class X
def initialize
@x = [10, 'a', 'c', 9, 8]
end
end

In Ruby 1.8, what's the magic incantation so that I can do

x = X.new
x.each {|y| puts y}

I have read and reread Dave Thomas'*Programming Ruby 1.9* and ... well ... I think I have to define *each* in class X and add some enumerable stuff ... but ... I just don't get it.

--
Michael Fellinger
CTO, The Rubyists, LLC
I check email a couple times daily; to reach me sooner, use:
http://awayfind.com/manveru

In Ruby 1.8, what's the magic incantation so that I can do

  x = X.new
  x.each {|y| puts y}

You can do that anyway, it just doesn't do anything. All methods can accept a
block, whether you want them to or not. All you have to do is yield to it.

I have read and reread Dave Thomas'*Programming Ruby 1.9* and ... well ...
I think I have to define *each* in class X and add some enumerable stuff

First, why are you reading a 1.9 book and applying it to 1.8?
Not that it should matter...

You have to define 'each', yes, and you probably should include enumerable.
I'll give a contrived solution to your contrived example:

class X
  def each
    yield @x[0]
    yield @x[1]
    ...
  end
end

That 'yield' is a call to whatever block you passed. You could, of course,
wrap it in a loop, or even an enumerator of its own:

class X
  def each
    @x.each {|x| yield x}
  end
end

In this specific case, your contrived example is just contrived enough that we
could take it a step further. You can grab that block as an actual argument by
prefixing it with &, and apply it the same way. So, you can just pass it
straight through, as Michael Fellinger suggested:

class X
  def each(&block)
    @x.each(&block)
  end
end

It's contrived, but we may as well finish it up:

class X
  # adds map, select, and so on
  include Enumerable

  def each(&block)
    # enum_for makes a new enum which uses 'each' by default
    return enum_for if block_given?
    @x.each(&block)
  end
end

I don't always include Enumerable, but I do always do enum_for, because not
everything I want to enumerate is already in a form I can call 'each' on. For
example:

class Foo
  def all_bars
    return enum_for :all_bars unless block_given?
    yield :bar
    yield :foobar
    yield :baz
  end
end

Now I can do crazy stuff like:

Foo.new.all_bars.map(&:to_s)
Foo.new.all_bars{|b| puts b}

That way, I can have multiple methods like that, each of which either behaves
as the appropriate iterator, or gives me an appropriate enumerable (an
enumerator, specifically) to play with.

I just don't get it.

I hope I've helped, but if not, I'm going to need more than that. If you still
don't get it, let me know what you don't get, and we can go from there.

···

On Saturday, August 21, 2010 12:45:49 pm Ralph Shnelvar wrote: