to_s isn't automatically called

D

Daniel Völkerts

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.
 
A

Austin Ziegler

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
 
R

Robert Klemme

Austin Ziegler said:
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:


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
 
D

Daniel Völkerts

Robert said:
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,
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,578
Members
45,052
Latest member
LucyCarper

Latest Threads

Top