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