Reputation: 381
In short, I expect all three #{}s in the final line of this test program to result in the same string and they don't. It works if my class inherits from Object.
> ./test.rb
A string foobar, and MyString: , MyString.to_s: foobar
> cat test.rb
#!/usr/bin/env ruby
class MyString < String
def initialize s
@s= s.to_s
@simple = s.index(?+)
end
def to_s() @s end
end
x='foobar'
puts "A string #{x}, and MyString: #{
MyString.new(x)}, MyString.to_s: #{MyString.new(x).to_s}"
Upvotes: 1
Views: 2490
Reputation: 79783
As Niklas B. demonstrates, string interpolation doesn't call to_s
on String
objects. The default value for the argument to String#initialize
is ""
, so instances of your subclass are all in effect empty strings.
One way to get this to work is to call super
in the initialize
method if your subclass, and pass the string value:
class MyString < String
def initialize s
super s.to_s # this line added
@s= s.to_s
@simple = s.index(?+)
end
def to_s() @s end
end
puts "#{MyString.new("foo")}" # => produces "foo"
Upvotes: 2
Reputation: 95318
It seems like string interpolation handles objects of type String
differently:
irb(main):011:0> class MyString < String
irb(main):012:1> def initialize(str)
irb(main):013:2> @str = str
irb(main):014:2> end
irb(main):015:1> def to_s
irb(main):016:2> puts "to_s called!"
irb(main):017:2> @str
irb(main):018:2> end
irb(main):019:1> end
=> nil
irb(main):020:0> "#{MyString.new('foo')}"
=> ""
As you can see, to_s
is not even called. The reason for that is the invariant str.to_s == str
where str.is_a? String
. You broke that invariant, thus confusing the library code.
Conclusion: Don't override to_s
if you're subclassing String
(it doesn't make a lot of sense anyway).
Upvotes: 4