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 fromdatabase.yml
orENV["DATABASE_URL"]
.User.transaction
orclass.transaction
can be used if you want to have different connections per model class or just don't like typing. All it really does isconnection.transaction(options, &block)
.- The instance level
self.transaction
or@user.transaction
just proxies to the class method. All it really does isself.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-variableself
.
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-variableself
, 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-variableself
.
You might ask yourself, why not use this even simpler rule?
A
private
method can only be called with the receiverself
.
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
Optional Parens in Ruby for Method with Uppercase Start Letter
How to Use Same Browser Window for Automated Test Using Selenium-Webdriver (Ruby)
Install Cocoapods Failed on MAC
Can't Convert String into Integer in Ruby/Ruby-On-Rails
How to Format a Date to Mm/Dd/Yyyy in Ruby
What Order Do Before Filters Occur In
What Does the Term "Vendoring" or "To Vendor" Mean for Ruby on Rails
How to Preload Concerns in a Rails Initializer Using Rails 6/Zeitwerk
Paperclip Error: Model Missing Required Attr_Accessor for 'Avatar_File_Name'
How to Get a Linux Command Output to Chef Attribute
Regular Expression "Empty Range in Char Class Error"
Time Gt Query Not Working with Mongoid and Ruby on Rails
Rspec: Should Be (This or That)