Skip to content

Ruby Classes and Objects — class initialize attr_accessor and self Explained

DodaTech Updated 2026-06-28 8 min read

In this tutorial, you will learn about Ruby Classes and Objects. We cover key concepts, practical examples, and best practices to help you master this topic.

Ruby classes define objects with state and behavior using initialize constructors, attr_accessor for automatic getter/setter creation, and self for distinguishing instance-level from class-level context.

What You'll Learn

  • Defining classes and creating objects
  • Initialize method and constructor patterns
  • attr_reader, attr_writer, and attr_accessor
  • Instance methods vs class methods
  • Self keyword and its various meanings

Why It Matters

Object-oriented programming is central to Ruby. Rails models are classes, controllers are classes, and even configuration objects are classes. Durga Antivirus Pro models threats, scans, and reports as Ruby classes. Doda Browser uses classes for tabs, bookmarks, and history entries. Understanding classes means understanding Ruby itself.

Real-World Use

A Rails application might have User, Post, and Comment classes with validations, associations, and custom methods. A Sinatra API uses classes for route handlers. A data processing tool uses classes for transformers, filters, and exporters.

flowchart LR
    A["Classes & Objects"] --> B["Class Definition"]
    B --> C["Initialize"]
    C --> D["Attributes"]
    D --> E["Methods"]
    E --> F["Inheritance"]
    A:::current --> B
    style A fill:#2563eb,stroke:#2563eb,color:#fff
    style B fill:#dbeafe,stroke:#2563eb,color:#1e40af
    style C fill:#dbeafe,stroke:#2563eb,color:#1e40af
    style D fill:#dbeafe,stroke:#2563eb,color:#1e40af
    style E fill:#dbeafe,stroke:#2563eb,color:#1e40af
    style F fill:#f1f5f9,stroke:#94a3b8,color:#64748b

Defining a Class

class Person
end

alice = Person.new
puts alice.class  # Person

The Initialize Method

The initialize method runs when you call new:

class Person
  def initialize(name, age)
    @name = name
    @age = age
  end
end

alice = Person.new("Alice", 25)

@name and @age are instance variables — they belong to the specific object.

Attribute Accessors

Instance variables are private by default. Use accessors to expose them:

attr_reader (Read Only)

class Person
  attr_reader :name, :age

  def initialize(name, age)
    @name = name
    @age = age
  end
end

alice = Person.new("Alice", 25)
puts alice.name  # "Alice"
puts alice.age   # 25
# alice.name = "Bob"  # NoMethodError (no writer)

attr_writer (Write Only)

class Person
  attr_writer :name

  def initialize(name)
    @name = name
  end
end

alice = Person.new("Alice")
alice.name = "Bob"  # Works
# puts alice.name   # NoMethodError (no reader)

attr_accessor (Read and Write)

class Person
  attr_accessor :name, :age

  def initialize(name, age)
    @name = name
    @age = age
  end
end

alice = Person.new("Alice", 25)
puts alice.name   # "Alice"
alice.name = "Bob"
puts alice.name   # "Bob"

Instance Methods

class Calculator
  def add(a, b)
    a + b
  end

  def multiply(a, b)
    a * b
  end

  def power(base, exp)
    base ** exp
  end
end

calc = Calculator.new
puts calc.add(5, 3)       # 8
puts calc.multiply(4, 3)  # 12
puts calc.power(2, 10)    # 1024

Methods That Use Instance Variables

class BankAccount
  attr_reader :balance

  def initialize(initial_balance = 0)
    @balance = initial_balance
  end

  def deposit(amount)
    @balance += amount
    puts "Deposited #{amount}. New balance: #{@balance}"
  end

  def withdraw(amount)
    if amount > @balance
      puts "Insufficient funds"
    else
      @balance -= amount
      puts "Withdrew #{amount}. New balance: #{@balance}"
    end
  end
end

account = BankAccount.new(100)
account.deposit(50)    # Deposited 50. New balance: 150
account.withdraw(30)   # Withdrew 30. New balance: 120
account.withdraw(200)  # Insufficient funds

Class Methods

Methods called on the class itself, not on instances:

class MathHelper
  def self.pi
    3.14159
  end

  def self.square(n)
    n * n
  end
end

puts MathHelper.pi      # 3.14159
puts MathHelper.square(5)  # 25

Alternative Syntax for Class Methods

class MathHelper
  class << self
    def pi
      3.14159
    end

    def square(n)
      n * n
    end
  end
end

The Self Keyword

self refers to the current object context:

class Person
  attr_accessor :name

  def initialize(name)
    @name = name
  end

  def introduce
    "Hi, I'm #{self.name}"
  end

  def rename(new_name)
    self.name = new_name  # Required for writer methods
  end
end

alice = Person.new("Alice")
puts alice.introduce  # Hi, I'm Alice
alice.rename("Bob")
puts alice.introduce  # Hi, I'm Bob

In class methods, self is the class itself:

class Person
  def self.species
    "Homo sapiens"
  end

  def self.describe
    "I am a #{self.species}"
  end
end

puts Person.species   # Homo sapiens
puts Person.describe  # I am a Homo sapiens

Constructor Overloading

Ruby doesn't support multiple initialize methods. Use default values or class methods:

class User
  attr_reader :name, :email, :role

  def initialize(name, email, role: "user")
    @name = name
    @email = email
    @role = role
  end

  def self.create_admin(name, email)
    new(name, email, role: "admin")
  end

  def to_s
    "#{@name} (#{@role})"
  end
end

user = User.new("Alice", "alice@example.com")
admin = User.create_admin("Bob", "bob@example.com")

puts user   # Alice (user)
puts admin  # Bob (admin)

Object Equality

Ruby has several equality methods:

a = "hello"
b = "hello"

puts a == b        # true — value equality
puts a.equal?(b)   # false — same object? (identity)
puts a.eql?(b)     # true — value and type equality
puts a.object_id   # Some number
puts b.object_id   # Different number

Common Mistakes

1. Forgetting attr_reader / attr_accessor

class Person
  def initialize(name)
    @name = name
  end
end

p = Person.new("Alice")
puts p.name  # NoMethodError (no reader defined)

2. Using @name When You Have attr_accessor

class Person
  attr_accessor :name

  def uppercase_name
    @name.upcase  # Works but bypasses the accessor
    # Better: self.name.upcase
  end
end

3. Forgetting self in Setter Methods

class Person
  attr_accessor :name

  def change_name(name)
    name = name  # This creates a local variable, doesn't call the setter!
    # Correct: self.name = name
  end
end

4. Confusing Class Methods and Instance Methods

class Calculator
  def self.add(a, b)
    a + b
  end

  def subtract(a, b)
    a - b
  end
end

Calculator.add(1, 2)        # 3 (class method)
Calculator.subtract(1, 2)   # NoMethodError (instance method)

calc = Calculator.new
calc.subtract(1, 2)  # -1 (instance method)
calc.add(1, 2)       # NoMethodError (class method on instance)

5. Not Using attr_reader for Read-Only Attributes

class Person
  attr_accessor :ssn  # Allows writing too

  # Should be:
  attr_reader :ssn
  def initialize(ssn)
    @ssn = ssn
  end
end

Practice Questions

1. What does attr_accessor do?

It creates both getter and setter methods for the specified instance variables. attr_accessor :name creates name and name= methods.

2. What's the difference between @name and self.name?

@name directly accesses the instance variable. self.name calls the getter method. Use @name for direct access and self.name= (required) for setters.

3. How do you define a class method?

Use def self.method_name inside the class, or define inside class << self. Class methods are called on the class itself, not on instances.

4. What does the initialize method do?

It's the constructor — called automatically when Class.new is invoked. It sets up initial state for new objects.

Challenge: Create a Library class that maintains a collection of books with methods to add, remove, search by title, and display all books.

Solution
class Library
  attr_reader :name

  def initialize(name)
    @name = name
    @books = []
  end

  def add_book(title, author)
    @books << { title: title, author: author }
  end

  def remove_book(title)
    @books.reject! { |book| book[:title] == title }
  end

  def search(title)
    @books.select { |book| book[:title].downcase.include?(title.downcase) }
  end

  def display
    puts "#{@name}: #{@books.size} books"
    @books.each { |b| puts "  - #{b[:title]} by #{b[:author]}" }
  end
end

lib = Library.new("City Library")
lib.add_book("1984", "George Orwell")
lib.add_book("Brave New World", "Aldous Huxley")
lib.add_book("Fahrenheit 451", "Ray Bradbury")
lib.display
puts "Search: #{lib.search("world").inspect}"
lib.remove_book("1984")
lib.display

Expected output:

City Library: 3 books
  - 1984 by George Orwell
  - Brave New World by Aldous Huxley
  - Fahrenheit 451 by Ray Bradbury
Search: [{:title=>"Brave New World", :author=>"Aldous Huxley"}]
City Library: 2 books
  - Brave New World by Aldous Huxley
  - Fahrenheit 451 by Ray Bradbury

FAQ

{{< faq question="Can a class have multiple initialize methods?" >}} No. Ruby classes can have only one initialize method. Use default parameter values or class Factory methods (like create_admin) to handle different construction scenarios. {{< /faq >}}

{{< faq question="What's the difference between @@var and @var?" >}} @@var is a class variable shared across the class hierarchy (including instances). @var in a class body is a class-level instance variable, while @var in an instance method is an instance variable. {{< /faq >}}

{{< faq question="How do I make a method private?" >}} Use the private keyword before the method definition. Private methods can only be called without an explicit receiver. Use protected for methods callable by instances of the same class. {{< /faq >}}

{{< faq question="Can I reopen a class to add methods?" >}} Yes. Ruby supports open classes. You can add methods to existing classes, including built-in ones. This is called monkey patching. Use it carefully — it can cause conflicts. {{< /faq >}}

{{< faq question="What is the difference between new and initialize?" >}} new is a class method that creates the object and then calls initialize on it. You can override new (as a class method) but rarely do. initialize is the instance method where you set up initial state. {{< /faq >}}

Try It Yourself

# classes_demo.rb

class Counter
  attr_reader :count

  def initialize(start = 0)
    @count = start
  end

  def increment
    @count += 1
  end

  def decrement
    @count -= 1
  end

  def reset
    @count = 0
  end
end

c = Counter.new(10)
puts "Start: #{c.count}"
puts "Increment: #{c.increment}"
puts "Increment: #{c.increment}"
puts "Decrement: #{c.decrement}"
c.reset
puts "Reset: #{c.count}"

Expected output:

Start: 10
Increment: 11
Increment: 12
Decrement: 11
Reset: 0

What's Next

Now that you understand classes, explore modules for organizing reusable code across classes.

Topic Description Link
Ruby Modules module, include, extend, prepend {{< ref "10-modules" >}}
Ruby Inheritance Superclass, < operator, ancestors {{< ref "11-inheritance" >}}
Python Classes Compare Python class patterns Python

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro