Skip to content

Ruby Classes

Ruby Classes and Objects: The Blueprint of OOP

Section titled “Ruby Classes and Objects: The Blueprint of OOP”

Ruby is a pure Object-Oriented Programming (OOP) language. Key OOP principles include:

  • Data Encapsulation: Bundling data (attributes) and methods that operate on the data within a single unit (an object). Access to the internal state is controlled.
  • Data Abstraction: Hiding complex implementation details and exposing only essential features of an object.
  • Polymorphism: The ability of objects of different classes to respond to the same message (method call) in different, class-specific ways.
  • Inheritance: Allowing a new class (subclass) to inherit properties and methods from an existing class (superclass), promoting code reuse and establishing relationships.

In Ruby, everything is an object. A class is a blueprint for creating objects. An object is an instance of a class. For example, if String is a class, then "hello" is an object (an instance of String).

A class definition starts with the class keyword, followed by the class name (which must begin with a capital letter by convention, e.g., CamelCase), and ends with the end keyword.

class Vehicle
# Class content (attributes and methods) goes here
end

Ruby supports several types of variables within the context of classes:

  • Local Variables: Defined within a method. Their scope is limited to that method. They begin with a lowercase letter or _ (e.g., my_var).
  • Instance Variables: Belong to a specific instance (object) of a class. They hold the state of an object. Their names start with an @ symbol (e.g., @color). Each object can have different values for its instance variables.
  • Class Variables: Shared among all instances of a class, and also accessible by the class itself. Their names start with @@ (e.g., @@count). Changes to a class variable are visible to all instances. Use with caution, especially with inheritance, as their behavior can sometimes be surprising.
  • Global Variables: Accessible from anywhere in the program. Their names start with a $ symbol (e.g., $debug_mode). Global variables should be used sparingly as they can make code harder to maintain and debug.
#!/usr/bin/ruby
class Car
# Class variable to keep track of the number of cars created
@@number_of_cars = 0
# Shortcut for creating getter and setter methods for @color and @make
# attr_reader :color # Only getter
# attr_writer :color # Only setter
attr_accessor :color, :make # Both getter and setter
# The initialize method is a special constructor called when Car.new is invoked
def initialize(make, color)
@make = make # Instance variable
@color = color # Instance variable
@@number_of_cars += 1
puts "Initialized a #{@color} #{@make}."
end
# Instance method
def start_engine
puts "The #{@make}'s engine roars to life!"
end
# Class method (note the 'self.')
def self.total_cars_created
"Total cars created: #{@@number_of_cars}"
end
end
# Creating Objects (Instances of the Car class)
my_car = Car.new("Toyota", "Red")
her_car = Car.new("Honda", "Blue")
# Accessing instance variables (via attr_accessor)
puts "My car is a #{my_car.color} #{my_car.make}."
my_car.color = "Silver" # Using the setter
puts "My car is now #{my_car.color}."
# Calling instance methods
my_car.start_engine
her_car.start_engine
# Calling a class method
puts Car.total_cars_created

This will produce output similar to:

Initialized a Red Toyota.
Initialized a Blue Honda.
My car is a Red Toyota.
My car is now Silver.
The Toyota's engine roars to life!
The Honda's engine roars to life!
Total cars created: 2

The initialize method is a special private method in Ruby classes. It’s automatically called when you create a new object using ClassName.new. It’s primarily used to set the initial state of an object by assigning values to its instance variables.

Instance methods operate on an instance of the class (an object). They have access to the object’s instance variables (e.g., start_engine in the Car example).

Class methods are called on the class itself, not on an instance. They are defined by prefixing the method name with self. (e.g., self.total_cars_created). They do not have access to an instance’s variables but can access class variables.

Attribute Accessors (attr_reader, attr_writer, attr_accessor):

Section titled “Attribute Accessors (attr_reader, attr_writer, attr_accessor):”

Ruby provides convenient shortcuts for creating common getter and setter methods for instance variables:

  • attr_reader :variable_name creates a getter method (e.g., def variable_name; @variable_name; end).
  • attr_writer :variable_name creates a setter method (e.g., def variable_name=(value); @variable_name = value; end).
  • attr_accessor :variable_name creates both a getter and a setter method.

Using these accessors makes code more concise and idiomatic.

Method Visibility: public, private, protected

Section titled “Method Visibility: public, private, protected”

Ruby controls access to methods using visibility keywords:

  • public: Methods are accessible from anywhere. This is the default visibility.
  • private: Methods can only be called by other methods within the same class instance (implicitly, without self. as a receiver, except for setter methods self.foo =). They are not accessible by subclasses or directly on an object.
  • protected: Methods can be called by instances of the defining class and its subclasses. They can be called with an explicit receiver (e.g., other_object.protected_method) as long as other_object is an instance of the same class or a subclass of the caller’s class.
class BankAccount
attr_reader :balance
def initialize(initial_deposit)
@balance = initial_deposit
end
def deposit(amount)
@balance += amount
log_transaction("Deposit: #{amount}")
end
def withdraw(amount)
if can_withdraw?(amount)
@balance -= amount
log_transaction("Withdrawal: #{amount}")
else
puts "Insufficient funds!"
end
end
# Protected: Accessible by BankAccount and its subclasses
protected
def can_withdraw?(amount)
@balance >= amount
end
# Private: Only accessible within this BankAccount instance
private
def log_transaction(message)
puts "[LOG] #{Time.now}: #{message}, New Balance: #{@balance}"
end
end
account = BankAccount.new(100)
account.deposit(50)
account.withdraw(30)
# account.log_transaction("test") # This would raise NoMethodError (private)
# account.can_withdraw?(10) # This would raise NoMethodError (protected, if called from outside directly)

Practical Applications:

Classes and objects are the cornerstone of most Ruby applications. They are used to model real-world entities (like users, products, orders), abstract concepts (like a database connection or a file parser), and to organize code into reusable and maintainable components. Frameworks like Ruby on Rails heavily rely on class-based structures (Models, Views, Controllers).