Ruby What Class Gets a Method When There Is No Explicit Receiver

Ruby what class gets a method when there is no explicit receiver?

There are three implicit contexts in Ruby.

The most well-known is self, the current object and the default receiver.

The second well-known is the scope used for constant lookup. Broadly speaking, constant lookup is "lexically outward, then upward by inheritance", but there are many nuances. The Ruby maintainers call this context cref.

What you are asking about, is the third context, sometimes called the default definee. Usually, the default definee is the nearest lexically enclosing module. But, you already found one exception: at the top-level, the default definee is actually Object (plus, the default visibility is private). instance_eval changes both self (to the receiver of the instance_eval message) and the default definee (to the receiver's singleton class). class_eval changes both to the receiver.

Calling a method without an explicit receiver in the main environment

Method call without explicit receiver:

send( :hello, "gentle", "readers")

assumes an implicit self as the receiver, which is the main object in this case.

Method definition in the main environment:

def hello(*args)
"Hello " + args.join(' ')
end

assumes it is defined as an instance method on Object.

Since main is an instance of the Object class, the method definition and the call work together.

To which object are top-level methods assigned in Ruby?

There is top-level object in Ruby -- main

def method_name(args)
# body
end
self
# => main

self.methods.grep(/method_name/)
# => [:method_name]

main is an instance of Object. Any methods defined in main become instance methods of Object

Object.private_instance_methods.grep(/method_name/)
# => [:method_name]

This makes them available everywhere (because all classes are descendants of Object), meaning that we can call the method without a receiver inside classes

For example

def foo
puts "foo"
end

class X
def y
foo
end
end

# will print foo
X.new.y

# will raise private method `foo' called for #<X:0x000055e406a0f060> (NoMethodError)
# to reproduce don't use irb, just usual file
X.new.foo

Read more

Implicit receiver

In the text you quote the "not yourself" means not in the same context (object) that the call was made.

In this example of a private method...

class Foo
def bar
baz
end
private
def baz
'hello'
end
end

If you do

Foo.new.baz

You get an error, because baz was called with an explicit receiver (the part before the dot... Foo.new)

If you do

Foo.new.bar
=> "hello"

And that works because method bar called baz without a receiver. It was able to call it without a receiver because bar (like baz) are both instance methods of the Foo object, so they have the same context (the same self). the bar method was calling baz on the same object that contains the bar method (i.e. "itself" or "yourself" if you think of yourself as sitting in the object as you write the object's methods).

Now rewrite the class as...

class Foo
def bar
self.baz
end
private
def baz
'hello'
end
end

And now you see that bar no longer works, since you privded the receiver self (an explicit receiver) on the call to baz within the method bar. Technically the exact same functionality (no receiver is the same as self as receiver) , but Ruby implements private methods by disallowing explicit receivers, even self. So, as stated, private methods can't be called with an explicit receiver.

Every time you call a method on an object you are not calling that method on yourself (i.e. your context).

'george'.upcase
=> "GEORGE"

For upcase the explicit receiver is the part before the dot (the string object "george")

If you did

upcase

without specifying a receiver, it assumes you want to run upcase in your context (self) which in IRB is main:Object. That's why you get

NameError: undefined lcoal variable or method `upcase' for main:Object

Why does Ruby have both private and protected methods?

protected methods can be called by any instance of the defining class or its subclasses.

private methods can be called only from within the calling object. You cannot access another instance's private methods directly.

Here is a quick practical example:

def compare_to(x)
self.some_method <=> x.some_method
end

some_method cannot be private here. It must be protected because you need it to support explicit receivers. Your typical internal helper methods can usually be private since they never need to be called like this.

It is important to note that this is different from the way Java or C++ works. private in Ruby is similar to protected in Java/C++ in that subclasses have access to the method. In Ruby, there is no way to restrict access to a method from its subclasses like you can with private in Java.

Visibility in Ruby is largely a "recommendation" anyways since you can always gain access to a method using send:

irb(main):001:0> class A
irb(main):002:1> private
irb(main):003:1> def not_so_private_method
irb(main):004:2> puts "Hello World"
irb(main):005:2> end
irb(main):006:1> end
=> nil

irb(main):007:0> foo = A.new
=> #<A:0x31688f>

irb(main):009:0> foo.send :not_so_private_method
Hello World
=> nil

transaction do without explicit receiver

In Ruby the implicit recipient in an instance method is always the instance:

class Thing
def foo
inspect
end
end

irb(main):026:0> Thing.new.foo
=> "#<Thing:0x007fde95955828>"

Am I right to assume, that the self. is not really necessary since in
the scope of a model instance method, self is always the model
instance by default?

There is always an implicit recipient (self) in ruby. What it is depends on the context. In class methods its the class. Its "main" (the global object) if you are not in a class or module.

class Foo
inspect
end
# => "Foo"

def foo
inspect
end
# => "main"

You only have to explicitly use self where there is a need to disambiguate (like for example self.class). But there are many situations where it can help the readability of your code.

All of the options above are pretty much functionally equivalent:

  • ActiveRecord::Base.transaction connects to the DB for that environment from database.yml or ENV["DATABASE_URL"].
  • User.transaction or class.transaction can be used if you want to have different connections per model class or just don't like typing. All it really does is connection.transaction(options, &block).
  • The instance level self.transaction or @user.transaction just proxies to the class method. All it really does is self.class.transaction(options,&block);

Private methods in Ruby

Private methods cannot be called with an explicit receiver. But they can be called by any subclasses and instances of the class.

Here's a nice explanation of public, protected and private methods in Ruby.

Why am I able to call the class method as if it were an instance method here?

When inside an abstract class, the self refers to the proper class, not the object. That's why you can access the method without explicitly telling self

When do I need to use self.instance_method vs instance_method alone?

There are a few differences between self.foo(...) and foo(...), but they're mostly equivalent.

Privacy

private methods can only be called via foo directly, and never with an explicit receiver. So if foo is marked private, then you have to call it without self.

class Example

private def foo(x)
x + 1
end

def bar
foo # Works fine
self.foo # Error: foo is private
end

end

Shadowing

If you have a local variable called foo in the current function, then writing foo without arguments will reference the local variable instead

class Example

def foo(*args)
puts "Hello :)"
end

def bar
foo = 100 # Just an ordinary variable; no relation to the above method
foo # This refers to the local variable called "foo"
self.foo # This calls the method foo() with no arguments
foo(1) # This calls the method foo() with one argument
self.foo(1) # Equivalent to the above; calls with one argument
end

def baz
foo # There's no local variable called "foo", so this calls the method
end

end

Assignment

If the method you're talking about is an assignment method (i.e. ends in =), then it must always be called with an explicit receiver

class Example

def foo=(value)
puts "Assigning foo = #{value}"
end

def bar
foo = 0 # This creates a new local variable called foo and assigns to it
self.foo = 0 # Calls foo= on self
end

end

why self.method_name cannot access private method ? where as only private method_name can in any method can access private method in ruby

why self.method_name cannot access private method ?

Because that is how private is defined, at least in the older version of Ruby that you are using.

It can in current versions of Ruby. The behavior you are describing only exists in older versions of Ruby.

If I run your code using my Ruby installation (Ruby 3.0.0 as implemented by YARV 3.0.0), I don't get an error.

Here is how private has historically been defined:

A private method can only be called without a receiver.

See, for example section 13.3.5.3 Private methods of the ISO/IEC 30170:2012 Information technology — Programming languages — Ruby specification:

A private method cannot be invoked with an explicit receiver, i.e., a private method cannot be invoked if a primary-expression or a chained-method-invocation occurs at the position which corresponds to the method receiver in the method invocation

Note that this is not the whole story, however.

How do you call a private attribute writer under this rule? The answer is: you can't! You can't use self.foo = bar because you are not allowed to use an explicit receiver and you can't use foo = bar because that is an assignment to the local variable foo and not a call to the attribute writer foo=.

So, there is actually an exception to this rule:

A private method can only be called without a receiver, unless it is an attribute writer, then it can also be called with a receiver that is the literal pseudo-variable self.

Note that it has to be the literal pseudo-variable self. It cannot be any arbitrary expression that evaluates to the same object.

I.e. this is allowed:

self.foo = bar

but this is not:

this = self
this.foo = bar
# private method `foo=' called for #<…> (NoMethodError)

The exact text from the ISO Ruby Language Specification is:

A private method cannot be invoked with an explicit receiver, i.e., a private method cannot be invoked if a primary-expression or a chained-method-invocation occurs at the position which corresponds to the method receiver in the method invocation, except for a method invocation of any of the following forms where the primary-expression is a self-expression.

  • single-method-assignment
  • abbreviated-method-assignment
  • single-indexing-assignment
  • abbreviated-indexing-assignment

However, this still doesn't solve all problems: what about operators, for example self + bar or !self? What about methods whose names are reserved words like class?

The proposed rules became more and more complicated. For some insight, see the following discussion on the Ruby issue tracker:
Bug #9907 Abbreviated method assignment with private attr_writer/attr_reader does not work. [Disclaimer: I am the one who filed the bug.]

To make this work, you would have to change the rule to something like

A private method can only be called without a receiver, unless it is an attribute writer or an operator, then it can also be called with a receiver that is the literal pseudo-variable self, including in an abbreviated method assignment.

As you can see, the rule gets rather complex.

A much simpler rule was proposed, and is now implemented as of Ruby 2.7:

  • Feature #11297 Allow private method of self to be called
  • Feature #16123 Allow calling a private method with self.

The rule now is:

A private method can only be called without a receiver or with an explicit receiver that is the literal pseudo-variable self.

You might ask yourself, why not use this even simpler rule?

A private method can only be called with the receiver self.

The reason is that both the old rule, the in-between rules, and the new rule can be decided statically, in fact even syntactically at parse time, whereas this very simple rule cannot.



Related Topics



Leave a reply



Submit