Ruby Access Control

Managing method visibility and encapsulation

🔒 What is Access Control?

Access control determines which methods can be called from outside a class. Ruby provides three levels: public, private, and protected, helping you hide implementation details and protect data.


class BankAccount
  def initialize(balance)
    @balance = balance
  end
  
  def deposit(amount)
    @balance += amount
  end
  
  private
  
  def balance
    @balance
  end
end

account = BankAccount.new(100)
account.deposit(50)
                                    

Output:

Deposit successful

Key Access Control Concepts

🌐

Public

Accessible from anywhere

def public_method
  # code
end
🔐

Private

Only within the class

private
def private_method
end
🛡️

Protected

Within class and subclasses

protected
def protected_method
end
🎯

Encapsulation

Hide internal details

@internal_data

🔹 Public Methods

Public methods are the default in Ruby and can be called by anyone. They form the public interface of your class that other code interacts with:

class Calculator
  # Public by default
  def add(a, b)
    a + b
  end
  
  def subtract(a, b)
    a - b
  end
  
  # Explicitly marking as public
  public
  
  def multiply(a, b)
    a * b
  end
end

calc = Calculator.new
puts calc.add(5, 3)
puts calc.subtract(10, 4)
puts calc.multiply(6, 2)
                            

Output:

8

6

12

🔹 Private Methods

Private methods can only be called within the class, without an explicit receiver. They hide implementation details and protect sensitive operations from external access:

class User
  def initialize(name, password)
    @name = name
    @password = encrypt(password)
  end
  
  def login(password)
    if encrypt(password) == @password
      puts "Login successful for #{@name}"
    else
      puts "Invalid password"
    end
  end
  
  private
  
  def encrypt(text)
    "encrypted_#{text}"
  end
end

user = User.new("Alice", "secret123")
user.login("secret123")
user.login("wrong")

# This will cause an error:
# user.encrypt("test")
                            

Output:

Login successful for Alice

Invalid password

🔹 Protected Methods

Protected methods can be called by instances of the same class or subclasses. They're useful when objects need to interact with each other's internal state:

class Person
  def initialize(name, age)
    @name = name
    @age = age
  end
  
  def older_than?(other_person)
    age > other_person.age
  end
  
  protected
  
  def age
    @age
  end
end

person1 = Person.new("Alice", 30)
person2 = Person.new("Bob", 25)

puts person1.older_than?(person2)

# This will cause an error:
# puts person1.age
                            

Output:

true

🔹 Access Control Syntax

You can specify access control in two ways: using keywords that affect all following methods, or passing method names as symbols to the access control keywords:

class Example
  # Method 1: Keyword affects following methods
  def public_method1
    puts "Public 1"
  end
  
  private
  
  def private_method1
    puts "Private 1"
  end
  
  def private_method2
    puts "Private 2"
  end
  
  public
  
  def public_method2
    puts "Public 2"
    private_method1  # Can call private methods internally
  end
  
  # Method 2: Pass method names as symbols
  def helper
    puts "Helper"
  end
  
  private :helper
end

ex = Example.new
ex.public_method1
ex.public_method2
                            

Output:

Public 1

Public 2

Private 1

🔹 Practical Example

Here's a real-world example showing how access control protects sensitive data while providing a clean public interface for users:

class CreditCard
  def initialize(number, cvv)
    @number = number
    @cvv = cvv
  end
  
  def display_info
    "Card ending in #{last_four_digits}"
  end
  
  def process_payment(amount)
    if valid_card?
      puts "Processing $#{amount} payment..."
      puts "Payment successful!"
    else
      puts "Invalid card"
    end
  end
  
  private
  
  def last_four_digits
    @number[-4..]
  end
  
  def valid_card?
    @number.length == 16 && @cvv.length == 3
  end
  
  protected
  
  def full_number
    @number
  end
end

card = CreditCard.new("1234567890123456", "123")
puts card.display_info
card.process_payment(99.99)

# These will cause errors:
# puts card.full_number
# puts card.valid_card?
                            

Output:

Card ending in 3456

Processing $99.99 payment...

Payment successful!

🔹 Best Practices

Follow these guidelines when using access control to create well-designed, maintainable classes:

  • Default to private: Make methods private unless they need to be public
  • Minimal public interface: Expose only what users need
  • Protect data: Keep instance variables private, use attr_accessor carefully
  • Use protected sparingly: Only when objects need to compare internal state
  • Document public methods: Make it clear what's meant to be used
class GoodDesign
  attr_reader :name  # Public read access
  
  def initialize(name, secret)
    @name = name
    @secret = secret
  end
  
  def public_action
    puts "Performing action for #{@name}"
    internal_process
  end
  
  private
  
  def internal_process
    # Hidden implementation
    validate_secret
  end
  
  def validate_secret
    # More hidden logic
  end
end
                            

🧠 Test Your Knowledge

Which access level is the default for methods in Ruby?