From: Robert Klemme Date: 2004-07-29T21:06:46+09:00 Subject: Re: to_s isn't automatically called "Austin Ziegler" schrieb im Newsbeitrag news:9e7db91104072904276f6d064e@mail.gmail.com... > On Thu, 29 Jul 2004 19:31:48 +0900, Daniel V�lkerts > wrote: > > 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 > > ' '+yield' ' > > end > > end > > Briefly, this would better be: > > class HtmlTag < Tag > def to_s > "#{yield}" > 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 = "" str << yield str << "" end But, as said, the functionality is already there plus CGI takes care of different HTML versions. Kind regards robert