LearnAI ToolsCareerPractice BuildsPlayContact
Lesson 922 min read

Classes & Modules

Learn Ruby class syntax, initialize, attr_accessor, inheritance, and how modules provide mixins for sharing behavior across classes.

Introduction

Ruby's classes work much like classes in other object-oriented languages, but modules add something distinctive: a clean way to share behavior across unrelated classes without forcing them into a single-parent inheritance chain.

What You Will Learn
  • How to define a class with initialize and instance methods.
  • How attr_accessor, attr_reader, and attr_writer generate getters and setters for you.
  • How single inheritance works with <, and how to override a parent method.
  • What a module is, and how include gives a class its behavior as a "mixin."

Defining a Class

initialize is Ruby's constructor — automatically called when you write ClassName.new(...). Inside any instance method, self refers to the current object.

class Dog
def initialize(name)
@name = name
end
def bark
"#{@name} says Woof!"
end
end
rex = Dog.new("Rex")
puts rex.bark
Output

Click Run to see what this code prints.

attr_accessor and Friends

Writing a getter and setter method by hand for every instance variable is repetitive, so Ruby provides three shortcuts: attr_reader (getter only), attr_writer (setter only), and attr_accessor (both).

class Dog
attr_accessor :name, :age
def initialize(name, age)
@name = name
@age = age
end
end
rex = Dog.new("Rex", 3)
puts rex.name # calls the generated getter
rex.age = 4 # calls the generated setter
puts rex.age
Output

Click Run to see what this code prints.

Inheritance

The < operator declares a class's parent. A subclass inherits all of its parent's methods, and can override any of them by redefining a method with the same name — calling super invokes the parent's version.

class Animal
def initialize(name)
@name = name
end
def speak
"#{@name} makes a sound."
end
end
class Cat < Animal
def speak
super + " Specifically, a meow."
end
end
whiskers = Cat.new("Whiskers")
puts whiskers.speak
Output

Click Run to see what this code prints.

Class Variables and Class Methods

A @@ class variable is shared across every instance of a class. A method defined with self. instead of a plain name becomes a class method, callable directly on the class itself without creating an instance.

class Dog
@@total_dogs = 0
def initialize(name)
@name = name
@@total_dogs += 1
end
def self.total_dogs
@@total_dogs
end
end
Dog.new("Rex")
Dog.new("Buddy")
puts Dog.total_dogs
Output

Click Run to see what this code prints.

Modules: Mixins

Use case: a module groups related methods together, similar to a class, but is never instantiated directly. include-ing a module inside a class adds all of its methods to that class — a "mixin" — letting unrelated classes share behavior without forcing a common parent class.

module Greetable
def greet
"Hello, I am #{name}."
end
end
class Person
include Greetable
attr_accessor :name
def initialize(name)
@name = name
end
end
class Robot
include Greetable
attr_accessor :name
def initialize(name)
@name = name
end
end
puts Person.new("Anaya").greet
puts Robot.new("Unit-7").greet
Output

Click Run to see what this code prints.

Why Modules Instead of More Inheritance

Person and Robot have nothing meaningfully in common as a shared parent class — forcing both to inherit from some artificial "Greetable" base class would be awkward. A module lets both share the exact same greet behavior without pretending they're the same kind of thing.

Common Mistakes

Avoid These Mistakes
  • Writing manual getter/setter methods by hand when attr_accessor (or attr_reader/attr_writer) would do it in one line.
  • Overusing class variables (@@) for state that really belongs on individual instances (@).
  • Reaching for deep inheritance chains when a module mixin would express shared behavior more cleanly.

Best Practices

  • Use attr_accessor/attr_reader/attr_writer instead of hand-written getters and setters unless you need custom logic in one.
  • Reach for a module mixin when unrelated classes need to share behavior, rather than forcing an artificial inheritance relationship.
  • Keep inheritance hierarchies shallow — Ruby, like most single-inheritance languages, gets harder to reason about with deep chains.

Frequently Asked Questions

Yes — a class can include as many modules as it needs, which is exactly how Ruby achieves many of the practical benefits of multiple inheritance without its complexity.

A module can't be instantiated with .new and has no inheritance chain of its own — it exists purely to group related methods (or constants) for use via include, or as a namespace.

Yes, conceptually — self refers to "the current object" inside an instance method, or "the class itself" inside a method defined with self., very similar to this in Java/JavaScript or self in Python.

Key Takeaways

  • initialize is Ruby's constructor, automatically called by ClassName.new(...).
  • attr_accessor, attr_reader, and attr_writer generate common getter/setter methods for you.
  • The < operator declares inheritance; super calls the parent class's version of an overridden method.
  • A module groups reusable methods; include mixes those methods into a class without full inheritance.

Summary

Classes and modules together give Ruby a flexible, expressive object-oriented toolkit — inheritance for "is-a" relationships, and mixins for shared behavior across otherwise-unrelated classes. Next, you'll cover Ruby's two core collection types: Arrays and Hashes.

Next Lesson →

Collections: Arrays & Hashes