Why Does Ruby Have Both Private and Protected Methods

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

Why can I access private/protected methods using Object#send in Ruby?

Technically: Because send doesn't do anything to check method visibility. (It would be more work to do so.)

Philosophically: Ruby is a very permissive language. You can already just open up a class and make any method you want public. The language designers implemented send in a way that allows it to override the restrictions normally imposed by private. Ruby 1.9 was originally going to have two variants, a private-respecting send and an unsafe variant called send!, but this was apparently dropped for backwards compatibility.

As for what private, protected and public mean:

  • public methods can be called by any sender
  • protected methods cannot be called outside of an instance of the method's class or an instance of a subclass
  • private methods cannot be called with an explicit receiver (with a couple of exceptions, such as setter methods, which always have to have an explicit receiver, and so can be called within the class that way)

Why does ruby allow child classes access parent's private methods?

The difference is that in ruby you can call private methods in subclasses implicitly but not explicitly. Protected can be called both ways. As for why? I guess you would have to ask Matz.

Example:

class TestMain

protected
def say_hola
puts "hola"
end

def say_ni_hao
puts "ni hao"
end

private
def say_hi
puts "hi"
end

def say_bonjour
puts "bonjour"
end
end

class SubMain < TestMain
def say_hellos
# works - protected/implicit
say_hola
# works - protected/explicit
self.say_ni_hao

# works - private/implicit
say_hi
# fails - private/explicit
self.say_bonjour
end
end

test = SubMain.new
test.say_hellos()

Rails using private and protected methods in a Service object

You have an extra end after def execute..end. That end closes the CreateService class. This means your protected methods are defined on the Store module.

Hence the missing method.

Protected and Private methods

protected methods (or atributes) can only be used by the classes that inherit the class with protected methods (or atributes).

a      d
\ \
b e
\
c

If the class a have a protected method, this can be used by b and c, but can't be used by d or e.
Note: Ascii art diagram for inherit in ruby classes.

class A   
public
def f obj
obj.c
end
def g obj
obj.b
end
def call_b
b
end

private

def b
puts "Hi!_b"
end


protected
def c
puts "Hi!_c"
end
end

a = A.new
b = A.new

a.f(b) # Hi!_c
a.g(b) # inj.rb:7:in `g': private method `b' called for #<A:0xb76df4cc> (NoMethodError)
a.call_b # Hi!_b

In this case, the method f can 'see' the protected method because its of the same class (or an inherited one), but the private method encapsulate(hides) the 'b' method of all the cases, except if this is called inside of his class (by another accesible method (in this case, the method call_b)).

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.



Related Topics



Leave a reply



Submit