Overview
Ruby implements class definition through the class
keyword followed by a constant name. Classes serve as blueprints for creating objects, defining instance variables, methods, and behavior inheritance. Ruby creates classes as instances of the Class
class, making them first-class objects that support dynamic modification and introspection.
Class instantiation occurs through the new
method, which allocates memory for the object and calls the initialize
method if defined. Ruby supports multiple instantiation patterns including factory methods, singleton objects, and alternative constructors.
class Vehicle
def initialize(make, model)
@make = make
@model = model
end
end
car = Vehicle.new("Toyota", "Camry")
# => #<Vehicle:0x00007f8b1c0a2b40 @make="Toyota", @model="Camry">
The class definition establishes the object's structure, while instantiation creates specific instances with unique state. Ruby maintains references to class objects, allowing runtime modifications and metaprogramming capabilities.
class Account
@@next_id = 1
def initialize(balance = 0)
@id = @@next_id
@@next_id += 1
@balance = balance
end
attr_reader :id, :balance
end
account1 = Account.new(100)
account2 = Account.new
# => Each instance gets unique @id values
Ruby classes support inheritance through the <
operator, creating parent-child relationships where child classes inherit methods and can override behavior. The object instantiation process respects the inheritance chain, calling parent initialize
methods when super
is invoked.
Basic Usage
Class definition begins with the class
keyword followed by a CamelCase constant name. The class body contains method definitions, instance variable declarations, and class-level configuration. Ruby evaluates class bodies during definition, executing any code present.
class BankAccount
def initialize(initial_balance)
@balance = initial_balance
@transactions = []
end
def deposit(amount)
@balance += amount
@transactions << { type: :deposit, amount: amount }
end
def withdraw(amount)
return false if @balance < amount
@balance -= amount
@transactions << { type: :withdrawal, amount: amount }
true
end
def balance
@balance
end
end
Object instantiation uses the new
method, which Ruby defines on all classes. The new
method allocates memory, creates the object, then calls initialize
with the provided arguments. The initialize
method sets up instance variables and performs object setup.
account = BankAccount.new(1000)
account.deposit(500)
account.withdraw(200)
puts account.balance # => 1300
Instance variables use the @
prefix and belong to specific object instances. Each object maintains its own copy of instance variables, providing encapsulation and state isolation. Instance variables remain private unless explicitly exposed through accessor methods.
Class variables use the @@
prefix and are shared among all instances of a class and its subclasses. Class variables maintain state across all objects, making them suitable for tracking class-wide information like counters or configuration.
class Employee
@@total_employees = 0
def initialize(name, department)
@name = name
@department = department
@@total_employees += 1
end
def self.total_employees
@@total_employees
end
end
emp1 = Employee.new("Alice", "Engineering")
emp2 = Employee.new("Bob", "Sales")
Employee.total_employees # => 2
Ruby provides attr_reader
, attr_writer
, and attr_accessor
methods for generating getter and setter methods automatically. These class methods create instance methods during class definition, reducing boilerplate code for simple attribute access patterns.
class Product
attr_reader :name, :price
attr_accessor :stock_quantity
def initialize(name, price, stock)
@name = name
@price = price
@stock_quantity = stock
end
end
product = Product.new("Laptop", 999.99, 50)
product.stock_quantity = 45 # Uses generated setter
puts product.name # Uses generated getter
Advanced Usage
Ruby supports multiple inheritance patterns through modules and mixins. Classes can include modules to share behavior without creating inheritance hierarchies. Module inclusion adds methods to the class's ancestors chain, affecting method lookup and super
behavior.
module Auditable
def self.included(base)
base.extend(ClassMethods)
base.class_eval do
attr_accessor :created_at, :updated_at
end
end
module ClassMethods
def create_with_timestamp(attrs)
instance = new(attrs)
instance.created_at = Time.now
instance.updated_at = Time.now
instance
end
end
def touch
self.updated_at = Time.now
end
end
class Document
include Auditable
def initialize(title, content)
@title = title
@content = content
end
end
doc = Document.create_with_timestamp(title: "Report", content: "Content")
doc.touch
Class methods defined with self.method_name
or within class << self
blocks operate on the class object itself rather than instances. These methods often serve as factory methods, configuration points, or class-level utilities.
class Database
@@connections = {}
class << self
def connection(name = :default)
@@connections[name] ||= establish_connection(name)
end
def establish_connection(name)
# Connection establishment logic
OpenStruct.new(name: name, connected: true)
end
def disconnect_all
@@connections.each_value(&:close)
@@connections.clear
end
end
end
db = Database.connection(:primary)
Database.disconnect_all
Ruby supports alternative constructor patterns through class methods that create and return instances. These factory methods can perform validation, apply defaults, or create objects from different data formats.
class User
attr_reader :name, :email, :role
def initialize(name, email, role = :user)
@name = name
@email = email
@role = role
end
def self.from_hash(data)
new(data[:name], data[:email], data[:role])
end
def self.admin(name, email)
new(name, email, :admin)
end
def self.guest
new("Guest", "guest@example.com", :guest)
end
end
user1 = User.from_hash(name: "Alice", email: "alice@example.com")
admin = User.admin("Bob", "bob@admin.com")
guest = User.guest
Metaprogramming capabilities allow dynamic class modification and method generation. Classes can define methods programmatically, modify their behavior at runtime, and respond to method calls dynamically.
class ConfigurableModel
def self.define_attribute(name, type = :string)
define_method(name) do
instance_variable_get("@#{name}")
end
define_method("#{name}=") do |value|
converted_value = convert_type(value, type)
instance_variable_set("@#{name}", converted_value)
end
(@attributes ||= []) << { name: name, type: type }
end
def self.attributes
@attributes || []
end
private
def convert_type(value, type)
case type
when :integer then value.to_i
when :float then value.to_f
when :boolean then !!value
else value.to_s
end
end
end
class Settings < ConfigurableModel
define_attribute :debug_mode, :boolean
define_attribute :max_connections, :integer
define_attribute :timeout, :float
end
settings = Settings.new
settings.debug_mode = "true"
settings.max_connections = "50"
puts settings.debug_mode # => true
puts settings.max_connections # => 50
Common Pitfalls
Class variable inheritance creates unexpected behavior when subclasses modify shared state. Class variables are shared between parent and child classes, leading to unintended side effects when multiple classes access the same variable.
class Parent
@@count = 0
def self.increment
@@count += 1
end
def self.count
@@count
end
end
class Child < Parent
end
Parent.increment # => @@count = 1
Child.increment # => @@count = 2 (same variable!)
Parent.count # => 2 (affected by Child)
The solution involves using class instance variables instead of class variables for class-specific state:
class Parent
@count = 0
class << self
attr_accessor :count
def increment
@count += 1
end
end
end
class Child < Parent
@count = 0 # Separate variable for Child
end
Method visibility rules can cause confusion when defining private methods within class definitions. Methods defined after private
remain private until visibility changes or the class definition ends. This affects method accessibility in unexpected ways.
class Example
def public_method
"public"
end
private
def private_method
"private"
end
# This method is also private!
def another_method
"accidentally private"
end
end
obj = Example.new
obj.another_method # => NoMethodError
Initialize method return values get ignored because new
always returns the newly created object. Returning different values from initialize
does not affect the instantiation process, leading to confusion about object creation.
class Problem
def initialize(value)
@value = value
return "not the object" # This return value is ignored
end
end
obj = Problem.new(42) # => Returns the Problem instance, not the string
Constant redefinition warnings occur when classes are redefined or reopened. Ruby allows class modification at runtime but issues warnings when constants are reassigned, indicating potential code organization issues.
class MyClass
VERSION = "1.0"
end
# Later in the code...
class MyClass
VERSION = "2.0" # => warning: already initialized constant
end
Instance variable access from class methods fails because class methods execute in the class context, not the instance context. Instance variables belong to specific objects and remain inaccessible from class method scope.
class Broken
def initialize
@instance_var = "value"
end
def self.try_access
@instance_var # => nil (this is a class instance variable)
end
end
obj = Broken.new
Broken.try_access # => nil, not "value"
Reference
Class Definition Methods
Method | Parameters | Returns | Description |
---|---|---|---|
class ClassName |
Block | Class |
Defines new class or reopens existing class |
attr_reader |
*names |
nil |
Creates getter methods for named attributes |
attr_writer |
*names |
nil |
Creates setter methods for named attributes |
attr_accessor |
*names |
nil |
Creates getter and setter methods for named attributes |
include |
Module |
self |
Includes module methods as instance methods |
extend |
Module |
self |
Includes module methods as class methods |
prepend |
Module |
self |
Prepends module to ancestor chain |
Instance Creation Methods
Method | Parameters | Returns | Description |
---|---|---|---|
new |
*args, **kwargs, &block |
Object |
Creates new instance and calls initialize |
allocate |
None | Object |
Allocates memory without calling initialize |
initialize |
*args, **kwargs, &block |
Ignored | Sets up new instance (called by new) |
Visibility Control
Method | Parameters | Returns | Description |
---|---|---|---|
private |
*method_names |
self |
Makes methods private (default if no names given) |
protected |
*method_names |
self |
Makes methods protected |
public |
*method_names |
self |
Makes methods public |
Variable Types
Type | Syntax | Scope | Description |
---|---|---|---|
Instance Variable | @variable |
Object instance | Unique to each object instance |
Class Variable | @@variable |
Class hierarchy | Shared among class and subclasses |
Class Instance Variable | @variable (in class context) |
Single class | Belongs to specific class object |
Constant | CONSTANT |
Global/Class | Unchanging values with lexical scope |
Inheritance Hierarchy Methods
Method | Parameters | Returns | Description |
---|---|---|---|
superclass |
None | Class or nil |
Returns parent class |
ancestors |
None | Array |
Returns ancestor chain including modules |
< |
Class |
Class |
Defines inheritance relationship |
super |
Optional args | Object |
Calls parent method implementation |
Introspection Methods
Method | Parameters | Returns | Description |
---|---|---|---|
class |
None | Class |
Returns object's class |
instance_of? |
Class |
Boolean |
Tests exact class membership |
is_a? |
Class |
Boolean |
Tests class or ancestor membership |
respond_to? |
Symbol/String |
Boolean |
Tests method availability |
methods |
include_super = true |
Array |
Lists available methods |
instance_variables |
None | Array |
Lists instance variable names |
Class Configuration Constants
Constant | Value | Description |
---|---|---|
Class::VISIBILITY_LEVELS |
[:private, :protected, :public] |
Method visibility options |
Object::RESERVED_METHODS |
Method names | Methods that cannot be undefined |
Common Class Patterns
Pattern | Implementation | Use Case |
---|---|---|
Factory Method | def self.create(params) |
Alternative constructors |
Singleton | include Singleton |
Single instance classes |
Builder | Chain setter methods | Complex object construction |
Template Method | Abstract methods with raise NotImplementedError |
Subclass customization |