On 8/13/07, Nasir Khan <rubylearner / gmail.com> wrote:
> Hi,
> I have two related questions on to_str behavior. AFAIK to_str does automatic
> coercion to string wherever one is required,
not quite always, Nasir.
Sometime it is rather done by to_s, look at this example:
519/20 > irb
irb(main):001:0> x=Object.new
=> #<Object:0xb7d71fec>
irb(main):002:0> puts x
#<Object:0xb7d71fec>
=> nil
irb(main):003:0> puts x.to_s
#<Object:0xb7d71fec>
=> nil
irb(main):004:0> x.extend Module::new{ def to_s; "hi there" end}
=> hi there
irb(main):005:0> puts x
hi there
=> nil

 it is to be provided for
> objects that exhibit string like behavior. But this mental model did not
> match up with my test -
>
> Consider a simple class
>
>  class B
>     def initialize(str)
>       @value = str
>     end
>     def to_s
>       @value
>     end
>     def to_str
>       to_s
>     end
>   end
>
> irb(main):038:0> x = B.new("hello")
> => #<B:0x27f1550 @value="hello">
>
> irb(main):042:0> a = "hello"+x
> => "hellohello"
yup in this use case you are correct
>
> So far so good. Concatenation automatically happend. Then I went on to write
> a test -
>
>  class MyTest < Test::Unit::TestCase
>    def test_equality
>      x = B.new("hello")
>      assert_equal("hello", x)
>    end
>  end
>
> Which failed to my surprise.
I feel that Howard has explained that nicely ;)
>
>   1) Failure:
> test_equality(MyTest) [(irb):49]:
> <"hello"> expected but was
> <#<B:0x27549bc @value="hello">>.
>
> 1 tests, 1 assertions, 1 failures, 0 errors
>
>
> Trying to experiment further I made my class B a subclass of String just to
> check the test -
>
>
> class B < String
>    def initialize(str)
>      @value = str
>    end
>    def to_s
>      @value
>    end
>    def to_str
>      to_s
>    end
>  end
>
> irb(main):023:0* a = B.new("hello")
> => ""
> irb(main):024:0>
> irb(main):025:0*
> irb(main):026:0* puts a
>
> => nil
>
> Which surprised me even more.
I am not sure if it is Howard who has adressed your surprise here
as a matter of fact puts calls to_s on objects that are not strings,
but when they are already they do nothing, they just print the String,
however, as Howard has said correctly you just added @value to a
String that does not make it have the 'value' hello.
Try this variation
irb(main):019:0> class B < String
irb(main):020:1> def initialize s
irb(main):021:2>   super s
irb(main):022:2>  @value = "value = #{s}"
irb(main):023:2> end
irb(main):024:1> end
=> nil
irb(main):025:0> puts B.new("hi")
hi
=> nil

you see you get hi and even if you define to_s  that does not change
the behavior of puts:
irb(main):026:0> class B
irb(main):027:1> def to_s
irb(main):028:2> @value
irb(main):029:2> end
irb(main):030:1> end
=> nil
irb(main):031:0> puts B.new("hi")
hi
=> nil
irb(main):032:0> puts B.new("hi").to_s
value = hi
=> nil

>
> Any explanation of these two counter-intuitive behaviors will be greatly
> appeciated.
Hopefully it is less counter-intuitive now, I agree that there might
be some slight inconsistencies between to_str and to_s, but for the
second part, be aware that you extended/subclassed a core class, that
is a tricky thing to do sometimes ;)
HTH
Robert


>
> Thanks
> Nasir
>


-- 
[...] as simple as possible, but no simpler.
-- Attributed to Albert Einstein