Hi --

On Tue, 14 Aug 2007, Nasir Khan wrote:

> Thanks to all the respondents.
> The confusion stemmed from some of the inconsistency that is around the
> to_str usage within Ruby.
>
> See this below.
>
> class A
>   def to_str
>     "hello"
>   end
>   def to_s
>     "bye"
>   end
> end
>
> # Note to_s and to_str return different strings.
>
> irb(main):018:0> "xyz"+A.new
> => "xyzhello"
>
> Good, as to_str is called when it is 'expected' that arg to + is going to be
> a string.
>
> irb(main):019:0> "hello" == A.new
> => false
>
> This is fine too as David pointed out, one could argue though because there
> already is a equal? but anyway....lets move on.

I'm digging around in the source to figure out the == behavior. Here's
the method:

static VALUE
rb_str_equal(str1, str2)
     VALUE str1, str2;
{
     if (str1 == str2) return Qtrue;
     if (TYPE(str2) != T_STRING) {
         if (!rb_respond_to(str2, rb_intern("to_str"))) {
             return Qfalse;
         }
         return rb_equal(str2, str1);
     }
     if (RSTRING(str1)->len == RSTRING(str2)->len &&
         rb_str_cmp(str1, str2) == 0) {
         return Qtrue;
     }
     return Qfalse;
}

I haven't quite figured out what the point is of testing to see
whether str2 responds to "to_str", since even if it does, to_str isn't
called. But I might just have to keep following the trail....

> Then see
>
> irb(main):020:0> A.new.upcase
> NoMethodError: undefined method `upcase' for hello:A
>        from (irb):20
>
> Again since A.new is not a String a method_missing is called etc.
>
> Bear with me a little more  :-)
>
> irb(main):021:0> "hellooo".include? A.new
> => true
>
> This is good because the argument is expected to be a string.

I believe the to_str thing is always about arguments. The object A.new
is not just going to act like a string; you can't call String instance
methods on it (like upcase). It's only going to provide its to_str
representation when it's an argument, as in include? .

> Now see this
>
> irb(main):022:0> "a" << A.new
> => "ahello"
> OK so now Ruby used the string representation from to_str *not* to_s
> Now is it true that *only* object of class String are expected to be
> appended to a string using << operator, hence the coercion?
>
> Not quite -
>
> irb(main):023:0> "a" << 1
> => "a\001"
>
> While of course -
> irb(main):025:0> 1.to_str
> NoMethodError: undefined method `to_str' for 1:Fixnum
>
> But of course -
> irb(main):038:0> 1.to_s
> => "1"
>
> So is it the case that when a string is "expected" then a to_str is tried if
> not found then to_s is tried, as can be deduced from the last two operations
> above? But then of course the following would fail -

It's not exactly that. Have a look at this and you'll see:

irb(main):004:0> "abc" << 1
=> "abc\001"
irb(main):005:0> "abc" << 100
=> "abcd"
irb(main):006:0> "abc" << 10
=> "abc\n"

This operation doesn't involve to_s or to_str, so it doesn't tell you
anything about how they work. Your next example answers the question,
though: to_s does not serve as a fall-back for to_str.


David

-- 
* Books:
   RAILS ROUTING (new! http://www.awprofessional.com/title/0321509242)
   RUBY FOR RAILS (http://www.manning.com/black)
* Ruby/Rails training
     & consulting:  Ruby Power and Light, LLC (http://www.rubypal.com)