Why Won't Ruby Allow Me to Specify Self as a Receiver Inside a Private Method

Why won't Ruby allow me to specify self as a receiver inside a private method?

The Problem

In Ruby, private methods can't be called directly with an explicit receiver; self doesn't get any special treatment here. By definition, when you call self.some_method you are specifying self as the explicit receiver, so Ruby says "No!"

The Solution

Ruby has rules for its method lookups. There may be a more canonical source for the rules (other than going to the Ruby source), but this blog post lays out the rules right at the top:

1) Methods defined in the object’s singleton class (i.e. the object itself)
2) Modules mixed into the singleton class in reverse order of inclusion
3) Methods defined by the object’s class
4) Modules included into the object’s class in reverse order of inclusion
5) Methods defined by the object’s superclass, i.e. inherited methods

In other words, private methods are first looked up in self without requiring (or allowing) an explicit receiver.

When using a private attr_accessor, why does self.attribute= work, but self.attribute doesn't?

According to a comment by @Jörg W Mittag to Alan Skorkin's blog entry Ruby Access Control – Are Private And Protected Methods Only A Guideline?:

Quick note: there is a special case where calling private methods with an explicit receiver is allowed:

If the method name ends with '=' (i.e. it is an attribute writer) and the explicit receiver is 'self', then this works. This is necessary, because setters can only be called with an explicit receiver because of the method/variable ambiguity. Otherwise it wouldn't be possible to call private setters.

Jörg cites no sources, but he's pretty reliable.

Understanding private methods in Ruby

Here's the short and the long of it. What private means in Ruby is a method cannot be called with an explicit receivers, e.g. some_instance.private_method(value). So even though the implicit receiver is self, in your example you explicitly use self so the private methods are not accessible.

Think of it this way, would you expect to be able to call a private method using a variable that you have assigned to an instance of a class? No. Self is a variable so it has to follow the same rules. However when you just call the method inside the instance then it works as expected because you aren't explicitly declaring the receiver.

Ruby being what it is you actually can call private methods using instance_eval:

class Foo
private
def bar(value)
puts "value = #{value}"
end
end

f = Foo.new
begin
f.bar("This won't work")
rescue Exception=>e
puts "That didn't work: #{e}"
end
f.instance_eval{ bar("But this does") }

Hope that's a little more clear.

-- edit --

I'm assuming you knew this will work:

class Foo
def public_m
private_m # Removed self.
end
private
def private_m
puts 'Hello'
end
end

Foo.new.public_m

How to create a private class method?

private doesn't seem to work if you are defining a method on an explicit object (in your case self). You can use private_class_method to define class methods as private (or like you described).

class Person
def self.get_name
persons_name
end

def self.persons_name
"Sam"
end

private_class_method :persons_name
end

puts "Hey, " + Person.get_name
puts "Hey, " + Person.persons_name

Alternatively (in ruby 2.1+), since a method definition returns a symbol of the method name, you can also use this as follows:

class Person
def self.get_name
persons_name
end

private_class_method def self.persons_name
"Sam"
end
end

puts "Hey, " + Person.get_name
puts "Hey, " + Person.persons_name

Is Ruby private method accessible in sub class?

Here's a brief explanation from this source:

  1. Public methods can be called by anyone---there is no access control. Methods are public by default (except for initialize, which is always private).
  2. Protected methods can be invoked only by objects of the defining class and its subclasses. Access is kept within the family.
  3. Private methods cannot be called with an explicit receiver. Because you cannot specify an object when using them, private methods can be called only in the defining class and by direct descendants within that same object.

This answer from a similar question expands on the topic in more detail: https://stackoverflow.com/a/1565640/814591

How and why should I avoid using self in a Ruby method declaration

There's nothing wrong with using self, but it bypasses the requirement to create a variable instance of your object, so some die-hard OO programmers would suggest avoiding self for that reason. If you avoid "self" then you are forced to initialize your class and assign it to a variable name which forces you think of it as a true object, and not just a collection of functions.

Here's an example class to demonstrate how you would call methods with and without "self"

class StaticVersusObjectMethod

def self.class_method
puts 'Hello, static class method world!'
end

def object_method
puts 'Hello, object-oriented world!'
end

end

# No need to create an object instance variable if the method was defined with 'self'
StaticVersusObjectMethod.class_method

# You must create an object instance variable to call methods without 'self'
object = StaticVersusObjectMethod.new
object.object_method

output:

Hello, static class method world!
Hello, object-oriented world!

Whether you use self in the declaration should depend on the data you want your method to use. If the methods will only operate on the variables you pass in as parameters, then use 'self'. On the other hand, don't use 'self' if you want them to act as true object methods. "True" object methods can operate on the state of the class variables (fields) in the objects which you create and assign to a one or more variable names.



Related Topics



Leave a reply



Submit