In Ruby, which is the object where the method puts is applied?

Viewed 588

In ruby you use the dot to call a method, or in other words, to send a method to the object treated.

"100".to_i

We are sending to the object "100" the message to_i

When we do:

puts "hello"

The method puts, to which object is applied?

I thought this:

self.puts "hello"

Where self would be main, like the environment where you are now, the irb console for example.

But it throws an error.

Traceback (most recent call last):
        2: from /Users/albert/.rbenv/versions/2.5.0/bin/irb:11:in `<main>'
        1: from (irb):17
NoMethodError (private method `puts' called for main:Object)

Why is this wrong? Where is the method puts applied?

UPDATE: Ruby 2.7 allows calling private methods with self. This feature was requested and discussed at 1 and 2.

3 Answers

puts is a module method of Kernel. Kernel is included by Object so it's available from nearly every class. puts "foo" and Kernel.puts "foo" are equivalent. The difference is Kernel.puts is explicit while puts could call a locally defined puts method.

def puts(str)
  p "my puts: #{str}"
end

puts "foo";        # "my puts: foo"
Kernel.puts "foo"  # foo

Under the hood, Kernel.puts calls $stdout.puts. $stdout is a predefined global instance of IO.

The OP is correct in thinking that, when the explicit receiver is omitted, the receiver becomes self. And since puts works without an explicit receiver, it is not that bad of an idea to try self.puts "hello" in the same environment. Indeed puts is defined on main, or whatever object self points to in the given environment.

The issue here is that the methods that can be called with an explicit receiver are public methods, whereas the method puts is a private method, which rejects explicit receivers.

A standard way to detour this restriction is to use the method send as follows:

self.send(:puts, "hello")

In Ruby, when you leave out the receiver of a message send, then the implicit receiver is always self. (No exceptions.)

So, saying

foo

where foo is not a local variable in the current lexical scope, is always equivalent to

self.foo

However, there is a small wrinkle: in Ruby, the definition of a private method is "a method than can only be invoked as the result of a message send without a receiver". So, while foo and self.foo are equivalent in terms of the receiver of the message send, there may be differences with access control. In particular, if foo is private, then foo will work but self.foo will raise a NoMethodError exception with the message (for example) NoMethodError (private method `foo' called for main:Object).

So, you were right all along:

puts

is equivalent to

self.puts

but you misread the error message: it's no that the method doesn't exist, it's that the method is private.

In particular, all of those methods that are intended to be more like "procedures" in that they don't actually do anything interesting with their receiver, are defined in Kernel and are defined as private methods. This also includes methods like Kernel#print, Kernel#require, etc.

Related