So, I am just beginning to learn Ruby and I included a to_s method in my Class so that I can simply pass the Object to a puts method and have it return more than just the Object ID. I made a mistake and defined it as such:
def to_s
puts "I'm #{#name} with a health of #{#health}."
end
instead of:
def to_s
"I'm #{#name} with a health of #{#health}."
end
So, when I do this while using the first code block:
player1 = Player.new("larry")
puts player1
I get an object ID and a string when I execute the above two lines of code and not just the string. Why is this? I get this output:
I'm Larry with a health of 90.
#<Player:0x007fca1c08b270>
I am trying to think about why the first version of the program doesn't just print out the string to console, but instead returns the object ID and the string. I thought that when I pass the object to puts, all that is happening is that puts turns around and calls the to_s method to get the player's string representation. Right?
When given arguments that are not strings or arrays puts calls rb_obj_as_string to turn its arguments into strings (see rb_io_puts)
If you search for rb_obj_as_string through the ruby codebase (I find http://rxr.whitequark.org useful for this) you can see it's defined as
VALUE rb_obj_as_string(VALUE obj)
{
VALUE str;
if (RB_TYPE_P(obj, T_STRING)) {
return obj;
}
str = rb_funcall(obj, id_to_s, 0);
if (!RB_TYPE_P(str, T_STRING))
return rb_any_to_s(obj);
if (OBJ_TAINTED(obj)) OBJ_TAINT(str);
return str;
}
In brief this:
returns straightaway if the argument is already a string
calls to_s
if the result is not a string, call rb_any_to_s and return that.
rb_any_to_s is what implements the default "class name and id" result that you're seeing: for any object it returns a string of the form #<ClassName: 0x1234567890abcdef>
Returning to your code, when you run puts player1 it calls rb_obj_as_string to convert your player to a string.
This first calls your to_s method, which uses puts to output your message. Your method then returns nil (because that's what puts always returns) so ruby calls rb_any_to_s, and that is what the outermost puts ends up using.
That's because the puts returns nil, so does that version of to_s:
def to_s
puts "I'm #{#name} with a health of #{#health}."
end
With puts player1, player1.to_s method is called, which prints the String "I'm ...", but the return value is that of the puts call inside to_s, which is nil.
So player1 is an object of which to_s returns nil, thus puts player1 in the end prints the result of the inherited to_s method.
Experiential Rule: If the result of to_s is not a String, then ruby returns the default.
Application of Rule: puts() returns nil, which means your to_s method returns nil, and nil is not a String, so ruby returns the default.
Another example:
class Object
def inspect
'obj-inspect'
end
def to_s
'obj-to_s'
end
end
class Dog
def inspect
'dog-inspect'
end
def to_s
nil
end
end
puts Dog.new
--output:--
#<Dog:0x1001b6218>
Once to_s fails to return a String, ruby does not continue along the method lookup path to call another to_s method. That makes some sense: the method was found, so there is no need to look up the method in a parent class. Nor does ruby alternatively call inspect() to get a result.
Where does the default come from? I think ruby must directly call the Object#to_s method which is a method written in C--thereby bypassing ruby's method overriding mechanism.
The first example using puts will write to stdout and return nil. It does not actually return a String.
The second example returns a String.
If you want to write to the console you can, but you will need to also return the value.
#or put it in a variable first and return that after you print it
def to_s
puts "I'm #{#name} with a health of #{#health}."
"I'm #{#name} with a health of #{#health}."
end
Related
In continuation of another question I also have the following question.
I have a class which has a very central instance variable called var. It is so central to the class that when I print an object of the class, it should just print the instance variable.
When I compare that same object with a string which matches the instance variable, I would like it to return true, but that doesn't always work in the implementation below:
class MyClass
attr_accessor :var
def initialize(v)
#var = v
end
def to_s
#var.to_s
end
def inspect
#var.inspect
end
def ==(other)
self.var == other
end
# ... methods to manipulate #var
end
str = "test"
obj = MyClass.new("test")
puts obj # prints the var string - great this is what i want
p obj # prints the var string - great this is what i want
puts obj==str # returns true - great this is what i want
puts str==obj # returns false - oops! Fails since i didn't overload the == operator in String class. What to do? I don't think i want to "monkey patch" the String class.
I understand that it's not my overwritten == operator which is used. It's the one in the String class. What do you suggest I do to get around this? Am I going down the wrong path?
I read through the question What's the difference between equal?, eql?, ===, and ==? without getting an answer to my question.
I understand that it's not my overwritten == operator which is used. It's the one in the String class. What do you suggest I do to get around this? Am I going down the wrong path?
Before trying anything else, have a look at the docs for String#==:
If object is not an instance of String but responds to to_str, then the two strings are compared using object.==.
In other words: you have to implement to_str and return the object's string value:
class MyClass
# ...
def to_str
#var.to_s
end
end
You can also return to_s or use alias to_str to_s.
With any of the above, you'd get:
"test" == MyClass.new("test")
#=> true
Note that to_str should only be implemented by objects that are string-like.
So, I am just beginning to learn Ruby and I included a to_s method in my Class so that I can simply pass the Object to a puts method and have it return more than just the Object ID. I made a mistake and defined it as such:
def to_s
puts "I'm #{#name} with a health of #{#health}."
end
instead of:
def to_s
"I'm #{#name} with a health of #{#health}."
end
So, when I do this while using the first code block:
player1 = Player.new("larry")
puts player1
I get an object ID and a string when I execute the above two lines of code and not just the string. Why is this? I get this output:
I'm Larry with a health of 90.
#<Player:0x007fca1c08b270>
I am trying to think about why the first version of the program doesn't just print out the string to console, but instead returns the object ID and the string. I thought that when I pass the object to puts, all that is happening is that puts turns around and calls the to_s method to get the player's string representation. Right?
When given arguments that are not strings or arrays puts calls rb_obj_as_string to turn its arguments into strings (see rb_io_puts)
If you search for rb_obj_as_string through the ruby codebase (I find http://rxr.whitequark.org useful for this) you can see it's defined as
VALUE rb_obj_as_string(VALUE obj)
{
VALUE str;
if (RB_TYPE_P(obj, T_STRING)) {
return obj;
}
str = rb_funcall(obj, id_to_s, 0);
if (!RB_TYPE_P(str, T_STRING))
return rb_any_to_s(obj);
if (OBJ_TAINTED(obj)) OBJ_TAINT(str);
return str;
}
In brief this:
returns straightaway if the argument is already a string
calls to_s
if the result is not a string, call rb_any_to_s and return that.
rb_any_to_s is what implements the default "class name and id" result that you're seeing: for any object it returns a string of the form #<ClassName: 0x1234567890abcdef>
Returning to your code, when you run puts player1 it calls rb_obj_as_string to convert your player to a string.
This first calls your to_s method, which uses puts to output your message. Your method then returns nil (because that's what puts always returns) so ruby calls rb_any_to_s, and that is what the outermost puts ends up using.
That's because the puts returns nil, so does that version of to_s:
def to_s
puts "I'm #{#name} with a health of #{#health}."
end
With puts player1, player1.to_s method is called, which prints the String "I'm ...", but the return value is that of the puts call inside to_s, which is nil.
So player1 is an object of which to_s returns nil, thus puts player1 in the end prints the result of the inherited to_s method.
Experiential Rule: If the result of to_s is not a String, then ruby returns the default.
Application of Rule: puts() returns nil, which means your to_s method returns nil, and nil is not a String, so ruby returns the default.
Another example:
class Object
def inspect
'obj-inspect'
end
def to_s
'obj-to_s'
end
end
class Dog
def inspect
'dog-inspect'
end
def to_s
nil
end
end
puts Dog.new
--output:--
#<Dog:0x1001b6218>
Once to_s fails to return a String, ruby does not continue along the method lookup path to call another to_s method. That makes some sense: the method was found, so there is no need to look up the method in a parent class. Nor does ruby alternatively call inspect() to get a result.
Where does the default come from? I think ruby must directly call the Object#to_s method which is a method written in C--thereby bypassing ruby's method overriding mechanism.
The first example using puts will write to stdout and return nil. It does not actually return a String.
The second example returns a String.
If you want to write to the console you can, but you will need to also return the value.
#or put it in a variable first and return that after you print it
def to_s
puts "I'm #{#name} with a health of #{#health}."
"I'm #{#name} with a health of #{#health}."
end
Went over this code on RubyMonk:
class Item
def initialize(item)
#item = item
end
def show
puts "The item name is: #{self}"
end
def to_s
"#{#item}"
end
end
Item.new("potion").show
The code passes but the use of the self variable is a bit ambiguous to me. You could've easily supplanted to_s with self in the show method and gotten the same results. Can somebody explain the difference between both interpolations and why/how self is used here?
Additionally, without the the method to_s, the code returns a proxy. What is the significance of defining the to_s here?
String interpolation implicitly calls the to_s method on an object. So, when you define the to_s method on Item, you are explicitly telling that object how to represent itself with respect to a string. self is used in this case because there is an implicit call to to_s within the interpolation of the Item object. Defining to_s explicitly tells Item how to render itself within a string.
For some additional details, check out this excellent post on explicit vs. implicit conversion methods.
While it's true that, in the example you provided, you could have just written "The item name is: #{#item}", that's not always the case.
As CDub points out, string interpolation implicitly calls to_s. If an object doesn't define a to_s method, Ruby returns an object reference in its place. In the example you gave us, writing "The item name is: #{#item}" only works because String implements to_s. If it didn't, or if you use Item to hold an object that doesn't implement to_s, you'll end up with the object's reference.
Now for the difference between using self and #item in your interpolation. self refers to the current object. When you interpolate self, you're calling the current object's to_s method. When you interpolate #item, you're calling #item's to_s method. That's not a problem in this simple case, but let's look at something a little bit more complex. Say we have two classes, Item and OtherItem (creative names, I know).
class Item
def initialize(item)
#item = item
end
def show
puts "The item name is: #{self}"
end
def to_s
"I'm a chunky monkey!"
end
end
class OtherItem
def initialize(item)
#otherItem = item
end
def to_s
"#{#otherItem}"
end
end
In this scenario, Item's show method uses self, so if we were to write:
Item.new(OtherItem.new("potion")).show
Ruby would call Item.show, which, in turn, would call self.to_s. Since self in that context is an Item, our output would be:
"The item name is: I'm a chunky monkey!"
If, however, we redefined Item.show like this:
def show
puts "The item name is: #{#item}"
end
And tried calling Item.new(OtherItem.new("potion")).show again, Item.show would call #item.to_s, and fill that in instead, so we'd get:
"The item name is: potion"
class A
def initialize(string, number)
#string = string
#number = number
end
def to_s
"In to_s:\n #{#string}, #{#number}\n"
end
def to_a
"In to_a:\n #{#string}, #{#number}\n"
end
end
puts a = A.new("hello world", 5)
output is
In to_s:
hello world, 5
How is the to_s method called automatically?
Why isn't another method called automatically such as to_a?
Since I did not write puts in the to_s method, why is output printed.
You're sending it to puts, which will try to render the object as a string using to_s.
If you changed your last line to: puts A.new("hello world", 5).to_a, it would instead call to_s on the returned Array and A's to_s would not be called.
puts generally prints the result of applying to_s on an object
read more
here
In addition to #numbers1311407 answer
whenever you try any code in irb
it calls to_s implicitly.
and as #numbers1311407 answer explains.
puts call to_s implicitly
I'm learning metaprogramming in Ruby and am just trying out defining missing methods via method_missing and define_method. I'm getting some unexpected behaviour and am wondering if anyone can explain this. Here is my class:
class X
def method_missing(m, *args, &block)
puts "method #{m} not found. Defining it."
self.class.send :define_method, m do
puts "hi from method #{m}"
end
puts "defined method #{m}"
end
end
Now, this code:
x = X.new
x.some_method
puts
x.some_method
puts
puts x
Produces the output:
method some_method not found. Defining it.
defined method some_method
hi from method some_method
method to_ary not found. Defining it.
defined method to_ary
#<X:0x007fcbc38e5030>
What I don't get is the last part: why is Ruby calling to_ary in a call to puts? Why would Ruby try to convert my object into an array just to print it?
I've Googled around and found these related links:
http://tenderlovemaking.com/2011/06/28/til-its-ok-to-return-nil-from-to_ary/
http://yehudakatz.com/2010/01/02/the-craziest-fing-bug-ive-ever-seen/
These also talk about method_missing and to_ary gotchas, but not specifically about why puts would call to_ary.
I should also mention that the behaviour does not change when I define a to_s, e.g.
def to_s
"I'm an instance of X"
end
The output of "puts x" is then:
method to_ary not found. Defining it.
defined method to_ary
I'm an instance of X
puts is a synonym for $stdout.puts. $stdout is an IO class, so look at the documentation for IO.puts:
Writes the given objects to ios as with IO#print. Writes a record
separator (typically a newline) after any that do not already end with
a newline sequence. If called with an array argument, writes each
element on a new line.
This mean that puts method is intended to write several lines of output. Thus it tries to call to_ary method on an object and if to_ary is defined, then prints each element of the returned Array on a new line, else puts calls to_s method.
to_ary internal usage is really not well documented in the Ruby documentation (Matz points this out in his The Ruby Programming Language book).
Methods print and p on the other hand don't call to_ary, only to_s.
Sidenote: Interesting, that to_ary must return real Array object, not an object defining each method or something else:
class Test
def to_ary
10.downto(1)
end
end
puts Test.new
#TypeError: can't convert Test to Array (Test#to_ary gives Enumerator)
# from (irb):28:in `puts'
# from (irb):28:in `puts'
# from (irb):28