Ruby Interpolation Results in Out-of-Order Output

84 views Asked by At

New to Ruby here and trying to output the contents of an instance for a class I've created. I don't think it should matter, but known_var in the following is being retrieved from an array. The line I am running is simply:

for known_var in @known_variables
  puts "known_var is #{known_var}"

The result I am getting is consistently in the format:

resistance = 2200 Ohmsknown_var is #<Variable:0x000001e21047f920>

The class does have a customized to_s definition, which why the first chunk is formatted as it is:

def to_s
    if @value == nil
    print "#@full_name = 0"
    else print "#@full_name = #@value #@unit"
    end
end

However, I'm not sure why this is showing up before the "known_var is" part of the string. It looks similar if I specify:

puts "known_var is #{known_var}"

minus the Variable part, which makes sense:

amperage = 12 Aknown_var is

Is there something that should be done differently if I want it to just output the text in the order it's provided in puts: "known_var is XXXXXX"?

I've run some searches to see if I can find an explanation (without luck so far). I can work around it by splitting the puts into two separate lines, but that's not what I'm looking to do, and more importantly, I want to understand why puts is ordering things in the way it is here.

2

There are 2 answers

2
Amadan On BEST ANSWER

puts is not ordering anything. Let us construct a toy example, and then trace through the execution.

class Foo
  def to_s
    print "[In to_s]"
    "42"
  end
end

def test
  foo = Foo.new
  puts "foo is #{foo}"
end

Just as you noted, running test prints [In to_s]foo is 42. How?

  • in test: a new Foo is constructed, and assigned to foo
  • The string "foo is #{foo}" needs to be evaluated in order to pass it as an argument to the puts function
  • To do that, foo.to_s needs to be evaluated
  • to_s prints [In to_s] without a newline (because that is what print does, unlike puts)
  • to_s returns "42" (Ruby functions return the last evaluated expression, unless an explicit return statement is executed)
  • Now that we know what foo.to_s evaluates to ("42"), we can evaluate "foo is #{foo}": it is "foo is 42"
  • puts gets executed, with "foo is 42" as its argument
  • "foo is 42" is output, right after the [In to_s] that was output before.

As Rajagopalan notes, to_s should create a string representation of your object; it is not supposed to output anything. Once I have the string representation, I can decide whether to print it or not. For example, it would be very weird if bar = foo.to_s outputs anything — I just want to store the text representation of foo into a variable.

0
Rajagopalan On

Avoid printing statements within the to_s method; instead, consider the updated to_s method below.

def to_s
  if @value == nil
    "#@full_name = 0"
  else
    "#@full_name = #@value #@unit"
  end
end