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)

···

--
Posted via http://www.ruby-forum.com/.

(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 31.01.2013 19:56, schrieb Javier 12:

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)

--
<https://github.com/stomar/&gt;

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.

···

--
Posted via http://www.ruby-forum.com/.

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)...

···

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.

--
<https://github.com/stomar/&gt;

Quoting sto.mar@web.de (sto.mar@web.de):

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

···

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

--
  * Se la Strada e la sua Virtu' non fossero state messe da parte,
* K * Carlo E. Prelz - fluido@fluido.as che bisogno ci sarebbe
  * di parlare tanto di amore e di rettitudine? (Chuang-Tzu)

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

···

--
Posted via http://www.ruby-forum.com/\.

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.
[...]

···

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

--
<https://github.com/stomar/&gt;

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!'

···

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

--
<https://github.com/stomar/&gt;

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!!

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:

···

On 3 February 2013 01:00, <sto.mar@web.de> wrote:

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!'

--
  Matthew Kerwin, 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