Needing help with which to_s gets called

I'm a beginner learning Ruby and I've hit something which I can't find
an explanation for in my text books.

If I run the following code:

class Able < Array
# def initialize(contents)
# @contents = Array.new(contents)
# end

  def to_s
    "[Able: #{self.join(", ")}]"
  end
end

class Baker
  def initialize(contents)
    @contents = Array.new(contents)
  end

  def <<(item)
    @contents << item
  end

  def to_s
    "[Baker: #{@contents.join(", ")}]"
  end
end

item = Baker.new(["a", "b"])
item << Able.new(["c", "d"])
item << "e"
puts item

I get the following output:

[Baker: a, b, c, d, e]

Clearly the to_s method in my Able class is not being called. If
however I change the definition of Able to:

class Able
  def initialize(contents)
    @contents = Array.new(contents)
  end

  def to_s
    "[Able: #{@contents.join(", ")}]"
  end
end

(Note that it no longer inherits from Array and instead contains an
array as an instance variable.)

I now get this output (which is what I wanted in the first place).

[Baker: a, b, [Able: c, d], e]

Why when Able is a sub-class of Array does my to_s method get ignored?

TIA,
Joh

P.S. I tried to read the FAQ as instructed first but the link given for
it gives a "Not found" error.

···

--
Posted via http://www.ruby-forum.com/.

John Winters wrote:

Why when Able is a sub-class of Array does my to_s method get ignored?

Because puts special cases arrays, not calling to_s on them at all.
If you compare puts [1,2,3] to puts [1,2,3].to_s you will see
that this is the case (puts [1,2,3] is just like puts 1,2,3 printing
each number on its own line instead of having it all in one line like
you get with to_s.

HTH,
Sebastian

···

--
NP: Dimmu Borgir - Sorgens Kammer - Del II (Bonus Track)
Jabber: sepp2k@jabber.org
ICQ: 205544826

Sebastian Hungerecker wrote:

John Winters wrote:

Why when Able is a sub-class of Array does my to_s method get ignored?

Because puts special cases arrays, not calling to_s on them at all.
If you compare puts [1,2,3] to puts [1,2,3].to_s you will see
that this is the case (puts [1,2,3] is just like puts 1,2,3 printing
each number on its own line instead of having it all in one line like
you get with to_s.

Thanks for that. There certainly seems to be some sort of
special-casing going on, but it's more widespread than just puts.

I tried the test case which you suggested and see exactly what you said.
However I also went back to my original code and changed the "puts" line
from:

puts item

to:

output = item.to_s
puts output

and I still get the same oddity so it's clearly not just puts doing it.

Do you have a reference where I can read up more on this?

TIA,
John

···

--
Posted via http://www.ruby-forum.com/\.

output is an instance of Baker so Ruby will invoke Baker's to_s method.

···

On Thu, Jul 31, 2008 at 2:31 PM, John Winters <john@sinodun.org.uk> wrote:

Sebastian Hungerecker wrote:
> John Winters wrote:
>> Why when Able is a sub-class of Array does my to_s method get ignored?
>
> Because puts special cases arrays, not calling to_s on them at all.
> If you compare puts [1,2,3] to puts [1,2,3].to_s you will see
> that this is the case (puts [1,2,3] is just like puts 1,2,3 printing
> each number on its own line instead of having it all in one line like
> you get with to_s.

Thanks for that. There certainly seems to be some sort of
special-casing going on, but it's more widespread than just puts.

I tried the test case which you suggested and see exactly what you said.
However I also went back to my original code and changed the "puts" line
from:

puts item

to:

output = item.to_s
puts output

and I still get the same oddity so it's clearly not just puts doing it.

Do you have a reference where I can read up more on this?

TIA,
John
--
Posted via http://www.ruby-forum.com/\.

--
"Hey brother Christian with your high and mighty errand, Your actions speak
so loud, I can't hear a word you're saying."

-Greg Graffin (Bad Religion)

John Winters wrote:

Thanks for that. There certainly seems to be some sort of
special-casing going on, but it's more widespread than just puts.

You're right. join special cases arrays as well, joining nested arrays as well
instead of to_sing them (that or it just calls flatten on the array before
starting it's work).

HTH,
Sebastian

···

--
NP: Tonic - You wanted more
Jabber: sepp2k@jabber.org
ICQ: 205544826

Actually now that I think about it a little more it appears that in the
first case something a little odd is happening in that Ruby is just treating
the Able data as a normal array but as soon as you assign the data to an
instance variable is adds an instance of Able to the array rather than just
passing the array values through.

···

On Thu, Jul 31, 2008 at 2:35 PM, Glen Holcomb <damnbigman@gmail.com> wrote:

On Thu, Jul 31, 2008 at 2:31 PM, John Winters <john@sinodun.org.uk> wrote:

> Sebastian Hungerecker wrote:
> > John Winters wrote:
> >> Why when Able is a sub-class of Array does my to_s method get ignored?
> >
> > Because puts special cases arrays, not calling to_s on them at all.
> > If you compare puts [1,2,3] to puts [1,2,3].to_s you will see
> > that this is the case (puts [1,2,3] is just like puts 1,2,3 printing
> > each number on its own line instead of having it all in one line like
> > you get with to_s.
>
> Thanks for that. There certainly seems to be some sort of
> special-casing going on, but it's more widespread than just puts.
>
> I tried the test case which you suggested and see exactly what you said.
> However I also went back to my original code and changed the "puts" line
> from:
>
> puts item
>
> to:
>
> output = item.to_s
> puts output
>
> and I still get the same oddity so it's clearly not just puts doing it.
>
> Do you have a reference where I can read up more on this?
>
> TIA,
> John
> --
> Posted via http://www.ruby-forum.com/\.
>
>
output is an instance of Baker so Ruby will invoke Baker's to_s method.

--
"Hey brother Christian with your high and mighty errand, Your actions speak
so loud, I can't hear a word you're saying."

-Greg Graffin (Bad Religion)

--
"Hey brother Christian with your high and mighty errand, Your actions speak
so loud, I can't hear a word you're saying."

-Greg Graffin (Bad Religion)

I guess this is an example of core class special cases that I was
worried about in an earlier thread today.

I really don't think it's a good practice to subclass core classes
unless you are only adding new behaviour.
Wrapping and delegating is much more predictable.

That having been said, the problem goes away if you first explicitly
map using to_s before the join(), but this is an indication of the
kind of hacks subclassing core objects like Array will force upon you
:-/

def to_s
   "[Baker: #{@contents.map { |e| e.to_s }.join(", ")}]"
end

···

On Thu, Jul 31, 2008 at 4:41 PM, Sebastian Hungerecker <sepp2k@googlemail.com> wrote:

John Winters wrote:

Thanks for that. There certainly seems to be some sort of
special-casing going on, but it's more widespread than just puts.

You're right. join special cases arrays as well, joining nested arrays as well
instead of to_sing them (that or it just calls flatten on the array before
starting it's work).

Thanks to all for some very useful pointers.

[snip]

You're right. join special cases arrays as well, joining nested arrays as well
instead of to_sing them (that or it just calls flatten on the array before
starting it's work).

That certainly seems to be the case, as witnessed by the following
snippet.

a = ["a", "b", "c"]
b = ["d", "e", "f"]
c = Array.new
c << a
c << b
puts "Size of c is #{c.size}"
puts c.join(", ")

which produces:

Size of c is 2
a, b, c, d, e, f

It looks very much like join will flatten anything which it thinks it
can.

Gregory Brown wrote:

I guess this is an example of core class special cases that I was
worried about in an earlier thread today.

I really don't think it's a good practice to subclass core classes
unless you are only adding new behaviour.
Wrapping and delegating is much more predictable.

Thank you - I'll take note of that. My original problem was that I have
a structure involving nested arrays and I wanted them to print out with
the nesting apparent. Sub-classing Array seemed the obvious way to do
it, but perhaps not.

That having been said, the problem goes away if you first explicitly
map using to_s before the join(), but this is an indication of the
kind of hacks subclassing core objects like Array will force upon you
:-/

def to_s
   "[Baker: #{@contents.map { |e| e.to_s }.join(", ")}]"
end

Adding to the snippet above:

puts c.map { |e| e.to_s }.join(", ")

Produces:

abc, def

So I now have a choice of two ways to do it.

Cheers,
John

···

On Thu, Jul 31, 2008 at 4:41 PM, Sebastian Hungerecker <sepp2k@googlemail.com> wrote:
--
Posted via http://www.ruby-forum.com/\.