From: Robert Dober Date: 2007-08-13T20:26:39+09:00 Subject: Re: to_str behavior On 8/13/07, Nasir Khan 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 => # irb(main):002:0> puts x # => nil irb(main):003:0> puts x.to_s # => 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") > => # > > 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 > <#>. > > 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