Classes & Objects
🏛️ Classes & Objects
🏗️ Defining a Class and initialize
initialize is Ruby's constructor — it runs automatically whenever .new is called, the same role as PHP's __construct or Python's __init__. Class names are capitalized by convention (CamelCase, not snake_case) — this isn't just style, Ruby actually treats capitalized identifiers as constants, and class names are constants.
📌 Instance Variables — the @ Sigil
This is the first genuinely new syntax in this chapter. Ruby instance variables are written with a leading @, directly on the variable name — no $this-> and no self. required to declare or read one from inside an instance method:
Instance Variables: PHP vs Python vs Ruby
| PHP | Python | Ruby | |
|---|---|---|---|
| Declaring/setting | $this->name = $name; | self.name = name | @name = name |
| Reading inside a method | $this->name | self.name | @name |
| Requires an explicit receiver? | Yes — $this-> | Yes — self. | No — @ is the whole syntax |
Referencing @nickname before it's ever been assigned doesn't raise an error the way accessing an undeclared property might elsewhere — it silently evaluates to nil. This is convenient for quick scripts, but it means a typo in an instance variable name (@nmae instead of @name) fails silently instead of raising a clear error, which can be a genuinely confusing bug to track down.
🚪 attr_accessor, attr_reader, attr_writer
By default, instance variables are private to the object — there's no automatic public getter or setter the way some languages provide. Writing them by hand gets repetitive fast:
Ruby collapses that entire pattern into one line:
attr_reader — getter only
attr_reader :name generates only the getter, leaving the instance variable read-only from outside the class. Use this for values that shouldn't be changed after construction — an ID, a creation timestamp, anything meant to stay fixed.
attr_writer — setter only
attr_writer :password generates only the setter, with no way to read the value back out through a public method. Rare in practice, but useful for write-only style properties.
Getters/Setters: PHP vs Python vs Ruby
| PHP | Python | Ruby | |
|---|---|---|---|
| Approach | Write getName()/setName() by hand, or PHP 8.1+ readonly properties | @property / @name.setter decorators | attr_accessor / attr_reader / attr_writer |
| Lines needed for a full getter+setter | ~6-10 | ~6-8 | 1 (attr_accessor :name) |
📝 Customizing to_s
Every Ruby object has a default to_s that produces an unhelpful string like #<Person:0x00007f8b1a0b8c a8>. Override it to control how an object prints — the equivalent of PHP's __toString or Python's __str__:
⚡ Class Methods — def self.method_name
A method defined with self. belongs to the class itself, not to individual instances — the equivalent of PHP's static methods or Python's @staticmethod/@classmethod:
💻 Coding Challenges
Challenge 1: A Book Class
Write a Book class with attr_accessor for :title, :author, and :pages, plus an initialize that sets all three. Create two instances and print each one's title using the generated getter.
Goal: Get comfortable with the initialize + attr_accessor pattern that appears in nearly every Ruby class.
Challenge 2: Custom to_s
Add a to_s method to your Book class from Challenge 1 that returns a formatted string like "'Title' by Author (pages pp.)". Use puts on an instance directly to confirm it's picked up automatically.
Goal: Practice overriding a default Ruby method to control an object's string representation.
Challenge 3: A Class Method Counter
Modify the Book class to track how many books have been created: add a class variable (research @@count briefly, or use a class-level instance variable with self.count) that increments inside initialize, and a class method self.total_books that returns the current count.
Goal: See class-level state and a class method working together, distinct from any single instance.
It's tempting to always reach for attr_accessor since it's the shortest to type, but the moment a value shouldn't be freely rewritten from outside the class — an ID, a total that should only change through a specific method — switch to attr_reader and add an explicit method for any controlled mutation. Default to the more restrictive option and only open things up when you actually have a reason to.
🎯 What's Next
Next chapter: Modules & Mixins — include vs extend, and how Ruby gets the benefits of multiple inheritance without actually allowing a class to have more than one superclass.