Problem redefining String::to_s

I’ve been checking String::to_s source code and found an issue.

A program like this will not work:

class String
def to_s
“example”
end
end

puts “Hi”

The output will be “Hi” and not “example”.

This is due to C code, it checks if object is a String, and then print
it directly without calling “to_s” (it calls to_s on all other classes)

I wanted to override String::to_s to apply some color to my program
output (just to check some things) and I can’t.

Is this a missing feature?

(i’m using ruby 1.8.7)

Am 31.01.2013 19:56, schrieb Javier 12:

puts "Hi"

(i’m using ruby 1.8.7)
(why???)

The built-in classes behave sometimes differently than the classes
you define. That’s neither a bug nor a feature.
Also, why should puts send to_s to a string? It is already a string.

There seems to be gems to colorize output, or maybe this helps:
colors - How can I use Ruby to colorize the text output to a terminal? - Stack Overflow.

class String
def red
“\e[31m#{self}\e[0m”
end
end

puts ‘Red text’.red

Regards,
Marcus

Am 02.02.2013 01:07, schrieb Javier 12:

I’m using CentOS, it ships with that version but it’s fine.

I wanted to “intercept” to_s so I can test some variations on my output
but I found it’s impossible since “puts” won’t call String.to_s

That’s a bit annoying because you expect all objects to behave the same
way.

Well, you should not expect that.
Why should to_s be sent to an object that already is a string???

Maybe you need to change the behaviour of puts
(sounds a little scary to me)…

I’m using CentOS, it ships with that version but it’s fine.

I wanted to “intercept” to_s so I can test some variations on my output
but I found it’s impossible since “puts” won’t call String.to_s

That’s a bit annoying because you expect all objects to behave the same
way.

Subject: Re: Problem redefining String::to_s
Date: sab 02 feb 13 06:21:06 +0900

Quoting [email protected] ([email protected]):

Why should to_s be sent to an object that already is a string???

The method exists, and it is useful when you code method that expect
various parameters, and convert them all to strings. It is not invoked
in string expansion (for reasons of optimization, I believe).

You can redefine to_s, and then explicitly invoke it, like

class String
def to_s
‘another string’
end
end

‘test’.to_s

Carlo

Am 02.02.2013 13:06, schrieb Javier 12:

unknown wrote in post #1094871:

Well, you should not expect that.
Why should to_s be sent to an object that already is a string???

because you’re told that puts will call to_s in the object you try to
print, and it’s expected that all objects work the same way, that’s all

The docs tell differently:

ri IO.puts

Writes the given objects to ios as with IO#print.
[…]

ri IO.print

Writes the given object(s) to ios. The stream must be opened for
writing. If the output field separator ($,) is not nil, it will
be inserted between each object. If the output record separator
($) is not nil, it will be appended to the output. If no arguments
are given, prints $_. Objects that aren’t strings will be converted
by calling their to_s method.
[…]

unknown wrote in post #1094871:

Well, you should not expect that.
Why should to_s be sent to an object that already is a string???

Maybe you need to change the behaviour of puts
(sounds a little scary to me)…

because you’re told that puts will call to_s in the object you try to
print, and it’s expected that all objects work the same way, that’s all

it was an interesting find, and I have to rely on a custom String class
because of that

If you’re seriously going to monkey-patch String, then try this instead:

class String
def colourise(colour=nil)
me = self.dup
me.colourise!(colour)
end
def colourise!(colour=nil)
return self unless colour
# do some magic to make things look the way you want
self
end
end

then call:

puts “Hello, world!”.colourise :green

(for example)

But really, instead of that, take a look at the formatador gem. Great
for colourising things already!!

Am 02.02.2013 13:06, schrieb Javier 12:

it was an interesting find, and I have to rely on a custom String class
because of that

Maybe you should describe your actual use case in more detail.

Couldn’t you simply use a wrapper method that does the
coloring and puts to stdout?

Like:

def colored_puts(string)
puts “\e[31m#{string}\e[0m”
end

colored_puts ‘Hi!’

On 3 February 2013 01:00, [email protected] wrote:

coloring and puts to stdout?

Like:

def colored_puts(string)
puts “\e[31m#{string}\e[0m”
end

colored_puts ‘Hi!’

Philosophically I’d argue that this is more correct anyway. If your
intention is to change all Strings everywhere (stored in memory, written
to
files, sent over the network, stored in databases, etc.) to include
extra
bytes that correspond with ANSI/VT escape sequences then by all means
monkeypatch String and co.; but if your intention is just to inject
those
bytes into console output, you should do the patching at the console
output phase
, either by messing with the IO#print family of methods, or
adding the new #colored_puts method Marcus presented above.

Note, too, that it’s not just puts/print that call #to_s; if String#to_s
added ANSI escapes, you’d have to have special logic to ensure they were
only added once, or things could get a bit … out of hand.

When in doubt, look for a gem that already does what you want. Many
exist,
and some are even pretty good. :wink:


Matthew K., B.Sc (CompSci) (Hons)
http://matthew.kerwin.net.au/
ABN: 59-013-727-651

“You’ll never find a programming language that frees
you from the burden of clarifying your ideas.” - xkcd