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, prepend
ed modules or include
d 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. class
es 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 interface
s 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
Why Are We Installing Ruby 1.9.2/1.9.3 Gems into a 1.9.1 Folder
What's the Best Way to Model Recurring Events in a Calendar Application
How to Get a Query String from a Url in Rails
Difference Between Map, Each, and Collect
Detect Mime Type of Uploaded File in Ruby
What Is the Easiest Way to Remove the First Character from a String
Rails Activerecord: Find All Users Except Current User
Get Names of All Files from a Folder With Ruby
Need a Simple Explanation of the Inject Method
How to Convert an Array to a Hash in Ruby
What Are the Restrictions For Method Names in Ruby
How to Configure Webrick to Use Ssl in Rails
How to Work With Two Different Databases in Rails With Active Records
Installing Rubygems in Windows
Code Block Passed to Each Works With Brackets But Not With 'Do'-'End' (Ruby)
How to Check If a Url Is Valid
How to Search File Text For a Pattern and Replace It With a Given Value