Understanding Private Methods in Ruby

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

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.

How to access private class methods in Ruby?

You should do:

class MyClass
def self.my_class_method
puts "class method"
end

private

def my_method
puts "regular method"
end

private_class_method :my_class_method
end

# to call class method
MyClass.send :my_class_method # => class method
# to call instance method
MyClass.new.send :my_method # => regular method

In Ruby, class(s) are also objects, so you can call the #send method on the class also.

In Ruby, you can define private class methods as

class MyClass
class << self
private

def my_class_method
puts "class method"
end
end
end

Or using thhis macro like method: private_class_method

How to access private methods

Yes, this is possible with Kernel#send:

receiver.send :method_name, parameters

Though there are workarounds like BasicObject#instance_eval, or Kernel#binding manipulations, the common way to call private method is to call send on the receiver.

Working with private methods in ruby on rails

You can make them class methods to call them in your controllers.

 private 

def self.create_notification_on_accept(event)
# I don't know where are you getting participant id, so pass it if you need
# additionally, if they are available on event object then you could do
# event.participant.id, event.user_id since the background of the code is not given
# hence I'm making some assumptions
event.notifications.create(action_type: "Accept", actor_id: participant.id,
user_id: user_id)
end

def self.create_notification_on_reject(event)
event.notifications.create(action_type: "Reject", actor_id: participant.id,
user_id: user_id)
end

And in your controller you can do this

def accept_invitation
#find event so you can pass it as a param
Event.create_notification_on_accept(event)
@participants.where(user_id: current_user.id).update_all(is_attending: true)
render "update"
end

However, a better thing to do this would be to have callbacks instead of class methods. That way you can reduce the overhead of calling the method everywhere, instead you can leave it onto the model to know when to call.

Use or misuse of private class methods in Ruby

There is no such thing as a "class method" in Ruby. There are only instance methods. "Class methods" are actually singleton methods (on an object which just so happens to be an instance of Class), and "singleton methods" are just instance methods of the singleton class.

private methods can only be called with an implicit receiver (which is self), i.e. they can only be called by other methods on the same object (which in turn means they must be methods of the same class or one of its ancestors, i.e. superclasses, prepended modules or included modules).

This means that private "class methods" can only be called by other "class methods", i.e. methods defined in Example's singleton class, Class, Module, Object, Kernel, or BasicObject. You cannot call them from methods defined in Example.

Think about it: what's the purpose of private? Its purpose is encapsulation, we want to encapsulate the internal implementation and representation details from the external protocol. There are two kinds of encapsulation in wide use currently: Abstract Data Type-Oriented Encapsulation (where instances of different types are encapsulated from each other, but instances of the same type can access each other's internals, e.g. classes in Java) and Object-Oriented Encapsulation (where different objects are encapsulated from each other, even if they are instances of the same type, e.g. Ruby and interfaces in Java). Ruby is an object-oriented language, therefore it uses object-oriented encapsulation. (Java OTOH uses ADT-oriented encapsulation for classes, which is counter-intuitive, since it is usually claimed to be an object-oriented language).

In your case, we have two objects, Example and an instance of Example. They are two different objects, so object-oriented encapsulation simply forbids you from accessing one object's private parts from the other object. Even if Ruby did use ADT-oriented encapsulation, it still wouldn't work: ADT-oriented encapsulation allows two instances of the same type to access each other's privates, but the two objects aren't of the same type, one is an instance of Class and the other is an instance of Example.

Basically, you want to manipulate two object's internals at the same time and that is just not possible in OOP. It's a fundamental design principle of OOP that each object (and each object alone) is responsible for its private parts, and you can only interact with that object by sending it messages through its public protocol.

tl;dr: what you want goes directly against the basic encapsulation principles in OO. Either Example::g must be public or you need to change your design. (Resorting to reflective hacks to circumvent access protection in code you have no control over is a code smell at best. Resorting to reflective hacks to circumvent access protection in code you own is just plain wrong, because you control the access protection, you could just change it.)


One possible solution is to leave OOP behind altogether, and look to functional programming for help. We could try and use closures for encapsulation:

We start out with your original example:

class Example
private_class_method def self.g
puts 4711
end

def f
self.class.send(:g)
end
end

Example.new.f
# 4711

Now, we turn g into a local variable and assign a lambda to it, and in turn use that lambda to define f:

class Example
g = -> {
puts 4711
}

define_method(:f, &g)
end

Example.new.f
# 4711

Now, g is (in some sense) even more "private" than before, because it only exists within the lexical scope of the class body, not even class methods defined in a different class body can access it. However, the lambda being referenced by g is a proper object and can be passed around even into different scopes.

But, presumably you don't want f and g to be just identical (otherwise you could just have used module_function, after all), instead you want f to do something other than just delegate to g. That is also possible:

class Example
g = -> {
puts 4711
}

define_method(:f) do
puts 42
g.()
end
end

Example.new.f
# 42
# 4711

This works, because in some other sense g is less "private" than before: lexical scopes can nest, in particular the lexical scopes of blocks (and only blocks) nest, so that the nested block (the block passed to define_method in this case) can access the lexical environment of the outer lexical scope (the class body in this case) even after that lexical scope no longer exists (the class body has finished evaluating).

Protected and private methods in Rails

For models, the idea is that the public methods are the public interface of the class. Public methods are intended to be used by other objects, while protected/private methods are to be hidden from the outside.

This is the same practice as in other object-oriented languages.

For controllers and tests, just do as you please. Both controller and test classes are only instantiated and called by the framework (yes, I know you can theoretically get the controller from the view, but if you do that, something is strange anyway). Since no one will ever create those things directly, there's nothing to "protect" against.

Addendum/Correction: For controllers, you should mark the "helper" methods as protected private, and only the actions themselves should be public. The framework will never route any incoming HTTP calls to actions/methods that are not public, so your helper methods should be protected in that way.

For helpers it will make no difference if a method is protected or private, since they are always called "directly".

You can mark stuff protected in all those cases if it makes things easier for you to understand, of course.

How to define a private method in Ruby?

It's fairly standard to put private/protected methods at the bottom of the file. Everything after private will become a private method.

class MyClass

def a_public_method

end

private

def a_private_method
end

def another_private_method
end

protected
def a_protected_method
end

public
def another_public_method
end
end

As you can see in this example, if you really need to you can go back to declaring public methods by using the public keyword.

It can also be easier to see where the scope changes by indenting your private/public methods another level, to see visually that they are grouped under the private section etc.

You also have the option to only declare one-off private methods like this:

class MyClass

def a_public_method

end

def a_private_method
end

def another_private_method
end
private :a_private_method, :another_private_method
end

Using the private module method to declare only single methods as private, but frankly unless you're always doing it right after each method declaration it can be a bit confusing that way to find the private methods. I just prefer to stick them at the bottom :)

Where to place private methods in Ruby?

The best practice in my point of view is to go sequentially and declare your methods without keeping private in point of view.

At the end, you can make make any method private by just adding: private :xmethod

Example:

class Example
def xmethod
end

def ymethod
end

def zmethod
end

private :xmethod, :zmethod

end

Does this justify your question?

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


Related Topics



Leave a reply



Submit