Page Content

Tutorials

Method Visibility In Ruby : Public, Private, And Protected

Method Visibility in Ruby

The context in which methods can be called is defined by the three levels of method visibility in Ruby: public, private, and protected. These access levels control how encapsulation is preserved and are essential to Ruby object-oriented programming.

Public, private, and protected are not actual keywords in Ruby; rather, they are instance methods of the Module class. These methods, when used in a class or module definition, can change the visibility of previously specified methods by taking method names (as strings or symbols) as arguments, or they can dynamically change the visibility of methods written underneath them.

With code samples, the three access levels are explained as follows:

Public Methods

An object’s public methods specify its anticipated behavior and make up its public API.

Access: There are no limitations on the use of public methods, and they can be invoked from anywhere.

Default: In Ruby classes, methods are by default public.

Exceptions: Methods written at the top level outside of a class are defined as private instance methods of Object, and the initialize method is always implicitly private. These are the two main exceptions.

Code Example (Public Method):

The public method “speak” in the following example can be invoked directly on the Cat instance:

class Cat
  def initialize(name)
    @name = name
  end
  # This method is public by default
  def speak
    puts "I'm #{@name} and I'm 2 years old"
  end
end

new_cat = Cat.new("garfield") #=> <Cat:0x2321868 @name="garfield">
new_cat.speak                  #=> I'm garfield and I'm 2 years old 
# Public method called from outside the object scope.

Output

I'm garfield and I'm 2 years old

Private Methods

For a class’s internal implementation details, private methods are utilized.

Access Restriction: An explicit receiver is never allowed when calling a private method.

Invocation Rule: They must be called in a functional manner, which implies that they are implicitly called on self (for example, by just using method_name). A private method can therefore only be invoked by instance methods that belong to the same class (or its subclasses).

Inheritance: Subclasses inherit private methods and have the ability to call and override them.

Code Example (Private Method):

The public hint method can implicitly invoke the secret method below, but an explicit effort to call it on the object instance is unsuccessful:

class SecretNumber
  def initialize
    @secret = rand(20)
  end

  # Public method that calls the private method implicitly
  def hint
    puts "The number is #{"not " if secret <= 10}greater than 10." 
    # Calling `secret` without a receiver (implicit call on self)
  end

  private 
  def secret
    @secret
  end
end

s = SecretNumber.new
s.hint                                     # The number is greater than 10. (Works) 

# s.secret                                 
# NoMethodError: private method `secret' called for #<SecretNumber:...> (Fails) 

Output

The number is not greater than 10.

Even from within the defining class, a NoMethodError will occur if you try to call a private method using an explicit receiver, like self.method_private.

Protected Methods

Instances of the same class (or subclass) must be able to access each other’s internal methods, which is where protected methods come in handy.

Access: Within the implementation of the class that defines the protected method or any of its subclasses, the protected method can be called.

Invocation Rule: Protected methods can be directly called on another object, unlike private methods, as long as the object is a member of the calling object’s class or a subclass.

Purpose: They are frequently used to construct accessors, like comparison logic implementations, that let instances of a class share internal state.

Code Example (Protected Method):

In this example, the Account class uses a protected accessor (balance) so that an Account instance can directly compare its internal balance against another Account instance:

class Account
  def initialize(balance)
    @balance = balance
  end

  attr_reader :balance # Creates the getter method `balance`
  protected :balance  # Makes the getter method protected 

  def greater_balance_than(other)
    # Allows explicit call `other.balance` because `self` and `other` 
    # are both instances of (or subclasses of) Account.
    return @balance > other.balance 
  end
end

# Example usage demonstrating why protected is needed:
a = Account.new(100)
b = Account.new(50)

puts a.greater_balance_than(b) # => true

# If `balance` were private, attempting to call `other.balance` would raise a NoMethodError.
# If `balance` were public, anyone could access the internal balance, violating encapsulation.

Output

true

You can also read What Is Command Line Crash Course Navigation In Ruby

Agarapu Geetha
Agarapu Geetha
My name is Agarapu Geetha, a B.Com graduate with a strong passion for technology and innovation. I work as a content writer at Govindhtech, where I dedicate myself to exploring and publishing the latest updates in the world of tech.
Index