Classes & Objects
๐๏ธ Classes & Objects
๐๏ธ Defining a Class and initialize
class Person def initialize(name, age) @name = name @age = age end end philip = Person.new("Philip", 33)
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:
class Person def initialize(name) @name = name end def greet "Hi, I'm #{@name}" # @name is read directly -- no self./this-> prefix needed end end
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:
# The manual way class Person def initialize(name) @name = name end def name # manual getter @name end def name=(value) # manual setter -- note the = is part of the method name @name = value end end
Ruby collapses that entire pattern into one line:
class Person attr_accessor :name, :age # generates BOTH a getter and setter for each def initialize(name, age) @name = name @age = age end end philip = Person.new("Philip", 33) philip.name # => "Philip" -- the generated getter philip.name = "P." # the generated setter
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 Person attr_accessor :name, :age def initialize(name, age) @name = name @age = age end def to_s "#{@name} (#{@age})" end end puts Person.new("Philip", 33) # => Philip (33) -- puts calls to_s automatically
โก 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:
class Person def self.species "Homo sapiens" end end Person.species # => "Homo sapiens" -- called on the class, no instance needed
๐ป 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.