To_s isn't automatically called

Hello there,

perhaps it is a newbies fault, but I can't figure out where to find my logical failure.

I'd like to build up a Tree for a Document. The result should contain a string represantion of this document (e.g. HTML file).

I started writing ...

class Tag
   def to_s
     yield
   end
end

Thats easy so I went on ...

class HtmlTag < Tag
   def to_s
     '<html>'+yield'</html>'
   end
end

That looks the way I want it to look like, but if I call the following

Html = HtmlTag.new

# Okay, not valid HTML. Just for testing.
puts Html {'Hello'}

The interpreter returned

tags.rb:27: undefined method `Html' for main:Object (NoMethodError)

I understand this messages as the Mainobject doesn't have such method, but why doesn't he send a message to Html called 'to_s'?

If I write

Test = String.new('Test')

and execute a

puts Test

He works fine an returned 'Test'.

Please point me to a solution.

TIA.

···

--
Daniel Völkerts ::
"Ich habe einen Drachen, und ich WERDE ihn benutzen!" - Esel in Shrek

perhaps it is a newbies fault, but I can't figure out where to
find my logical failure.

I'd like to build up a Tree for a Document. The result should
contain a string represantion of this document (e.g. HTML file).

I started writing ...

class Tag
  def to_s
    yield
  end
end

Thats easy so I went on ...

class HtmlTag < Tag
  def to_s
    '<html> '+yield'</html> '
  end
end

Briefly, this would better be:

  class HtmlTag < Tag
    def to_s
      "<html>#{yield}</html>"
    end
  end

However, most #to_s implementations aren't going to yield anything,
and I don't think that blocks bind to implicit method calls, if the
implicit method call is used in any case. That's actually where the
problem is:

That looks the way I want it to look like, but if I call the following
  Html = HtmlTag.new
    # Okay, not valid HTML. Just for testing.
  puts Html {'Hello'}
The interpreter returned
tags.rb:27: undefined method `Html' for main:Object (NoMethodError)

I understand this messages as the Mainobject doesn't have such method,
but why doesn't he send a message to Html called 'to_s'?

Because Ruby is seeing your statement as:

  puts Html() { 'Hello' }

You've forced a method context.

#to_s is called when a string form of the object is required -- and
in many cases, a block will *not* be provided. (When I did something
similar in irb, I got:

  irb(main):009:0> b = A.new
  LocalJumpError: no block given
          from (irb):3:in `to_s'
          ...

You would need to manually call #to_s. Better would be to redesign
your objects so that you're really calling a method (like CGI#html).

  class Tag
    def fill
      @string = yield
    end

    def to_s
      "<tag>#{@string}</tag>
    end
  end

  aa = Tag.new
  aa.fill { "Hello" }
  puts aa

-austin

···

On Thu, 29 Jul 2004 19:31:48 +0900, Daniel Völkerts <dvoelkerts@web.de> wrote:
--
Austin Ziegler * halostatue@gmail.com
               * Alternate: austin@halostatue.ca

"Austin Ziegler" <halostatue@gmail.com> schrieb im Newsbeitrag
news:9e7db91104072904276f6d064e@mail.gmail.com...

> perhaps it is a newbies fault, but I can't figure out where to
> find my logical failure.
>
> I'd like to build up a Tree for a Document. The result should
> contain a string represantion of this document (e.g. HTML file).
>
> I started writing ...
>
> class Tag
> def to_s
> yield
> end
> end
>
> Thats easy so I went on ...
>
> class HtmlTag < Tag
> def to_s
> '<html> '+yield'</html> '
> end
> end

Briefly, this would better be:

  class HtmlTag < Tag
    def to_s
      "<html>#{yield}</html>"
    end
  end

However, most #to_s implementations aren't going to yield anything,

And that's why one shouldn't override to_s with a version expecting a
block.

and I don't think that blocks bind to implicit method calls, if the
implicit method call is used in any case.

It can't since the invocation of #to_s is *in* Kernel#puts, i.e. that
method looks something like

def puts(*args)
  args.each do |o|
    # here's the #to_s invocation - no chance
    # to hand over the block:
    str = o.to_s
    send_to_output str
  end
end

That's actually where the
problem is:

> That looks the way I want it to look like, but if I call the following
> Html = HtmlTag.new
> # Okay, not valid HTML. Just for testing.
> puts Html {'Hello'}
> The interpreter returned
> tags.rb:27: undefined method `Html' for main:Object (NoMethodError)
>
> I understand this messages as the Mainobject doesn't have such method,
> but why doesn't he send a message to Html called 'to_s'?

Because Ruby is seeing your statement as:

  puts Html() { 'Hello' }

You've forced a method context.

#to_s is called when a string form of the object is required -- and
in many cases, a block will *not* be provided. (When I did something
similar in irb, I got:

  irb(main):009:0> b = A.new
  LocalJumpError: no block given
          from (irb):3:in `to_s'
          ...

You would need to manually call #to_s. Better would be to redesign
your objects so that you're really calling a method (like CGI#html).

CGI#html is a good example - and btw. maybe that's all you really need.
No need to invent the wheel twice.

If you want to do it manually, best is probably:

def html(attrs = {})
  str = "<html"
  attrs.each {|at, val| str << " " << at << "=\"" << val << "\""}
  str << ">"

  str << yield

  str << "</html>"
end

But, as said, the functionality is already there plus CGI takes care of
different HTML versions.

Kind regards

    robert

···

On Thu, 29 Jul 2004 19:31:48 +0900, Daniel Völkerts > <dvoelkerts@web.de> wrote:

Robert Klemme wrote:
  > But, as said, the functionality is already there plus CGI takes care of

different HTML versions.

Kind regards

Oh that sounds great. Thanks a lot. I'll have a look on it tomorrow.

Bye,

···

--
Daniel Völkerts ::
"Ich habe einen Drachen, und ich WERDE ihn benutzen!" - Esel in Shrek