Why does Ruby have both private and protected methods?

Viewed 55310

Before I read this article, I thought access control in Ruby worked like this:

  • public - can be accessed by any object (e.g. Obj.new.public_method)
  • protected - can only be accessed from within the object itself, as well as any subclasses
  • private - same as protected, but the method doesn't exist in subclasses

However, it appears that protected and private act the same, except for the fact that you can't call private methods with an explicit receiver (i.e. self.protected_method works, but self.private_method doesn't).

What's the point of this? When is there a scenario when you wouldn't want your method called with an explicit receiver?

8 Answers

What's the difference?

Private Methods Explained

@freddie = Person.new
@freddie.hows_it_going?
# => "oh dear, i'm in great pain!"

class Person   
    # public method
    def hows_it_going?
        how_are_your_underpants_feeling?
    end

    private

    def how_are_your_underpants_feeling? # private method
        puts "oh dear, i'm in great pain!"
    end
end

We can ask Freddie how things are going given it's a public method. That's perfectly valid. And it's normal and accepted.

But...the only person who can know how Freddie's underpants situation is, is Freddie himself. It would not do for random strangers to reach into Freddy's underpants and test out the situation -- no, no -- it's very, very private, and we don't want to expose what's private to the outside world.

@freddie.how_are_your_underpants_feeling?
# => # NoMethodError: private method `how_are_your_underpants_feeling?' called

Protected Methods Explained

Consider this:

class Person
    
    protected

    def gimme_your_credit_card! # protected method
        puts "Fine. Whatever. Here it is: 1234-4567-8910"
    end
end

class Rib < Person
end

class Wife < Rib # wife inherits from Rib
    def i_am_buying_another_handbag_with_your_card(husband)        
        husband.gimme_your_credit_card!
    end
end

@husband = Person.new
@mrs = Wife.new
@mrs.i_am_buying_another_handbag_with_your_card(@husband)
# => puts "Fine. Whatever. Here it is: 1234-4567-8910"

We're somewhat ok with mrs getting our credit card details, given mrs is flesh of our flesh, inherited from Person, but we don't want random individuals getting access to our credit card details.

If we tried to do that outside the subclass, it would fail:

@mrs = Wife.new
@mrs.gimme_your_credit_card!
# => protected method gimme_your_credit_card! called for #<Wife:0x00005567b5865818> (NoMethodError)

Summary

  • private methods can only be called from within, and without "an explicit receiver". (Strictly speaking, you can access a private method using a little bit of ruby magic, but I'm going to ignore that for the moment).
  • protected methods can be called within sub-classes.
  • I used the examples/analogies to help you vividly remember.

First Three types of access specifiers and those define thier scope.

  1. Public -> Access anywhere out side the class.
  2. Private -> Can not access outside the class.
  3. Protected -> This Method not access anywhere this method define scope.

But I have a solution for this problem for all method how to access explain in depth.

class Test
  attr_reader :name

  def initialize(name)
    @name = name
  end
  
  def add_two(number)
    @number = number 
  end
  
  def view_address
    address("Anyaddress")
  end
  
  private 

  def address(add)
    @add = add
  end
  
  protected 

  def user_name(name)
    # p 'call method'
    @name = name
  end
end

class Result < Test
  def new_user
    user_name("test355")
  end
end
  1. Object List
  2. p test = Test.new("test")
  3. p test.name
  4. p test.add_two(3)
  5. List item
  6. p test.view_address
  7. p r = Result.new("")
  8. p r.new_user
Related