iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

Classes & Objects

Ruby classes are open and dynamic. Define one with class Foo; add methods anytime; mix in modules. initialize is the constructor; attr_accessor / attr_reader generate accessors.

attr, modules, inheritance, comparable

EXAMPLE
# 1) Basic class
class User
    attr_accessor :name, :email          # generates getters AND setters
    attr_reader   :id                    # getter only

    def initialize(id, name, email)
        @id    = id
        @name  = name
        @email = email
    end

    def greet = "Hi, I'm \#{@name}"      # one-line method (3.0+)

    def to_s
        "\#{@name} <\#{@email}>"
    end
end

u = User.new(1, 'Ada', 'ada@example.com')
puts u.greet
puts u                                    # uses to_s

# 2) Inheritance
class Admin < User
    def initialize(id, name, email, perms:)
        super(id, name, email)
        @perms = perms
    end

    def can?(action) = @perms.include?(action)
end

a = Admin.new(2, 'Cy', 'cy@example.com', perms: [:delete])
puts a.can?(:delete)

# 3) Module mixin — add behaviour without inheritance
module Auditable
    def audit_log
        "AUDIT: \#{self.class.name}#\#{__method__}"
    end
end

class Order
    include Auditable
end

puts Order.new.audit_log

# 4) class methods
class User
    @@count = 0                          # class variable (avoid; use class instance var)

    def initialize(...) = @@count += 1

    def self.count
        @@count
    end
end

# 5) Private + protected
class Account
    def initialize(balance) = @balance = balance

    def transfer(other, amount)
        debit(amount)
        other.send(:credit, amount)        # protected — same class
    end

    protected
    def credit(n) = @balance += n
    def debit(n)  = @balance -= n
end

# 6) Comparable mixin — define <=>, get >, <, ==, between?
class Version
    include Comparable
    attr_reader :parts

    def initialize(s)
        @parts = s.split('.').map(&:to_i)
    end

    def <=>(other) = parts <=> other.parts
end

versions = ['1.2.3', '1.10.0', '0.9'].map { Version.new(_1) }.sort
puts versions.map { _1.parts.join('.') }       # 0.9, 1.2.3, 1.10.0

# 7) Struct — quick class for a value object
Point = Struct.new(:x, :y) do
    def distance_to(other) = Math.hypot(x - other.x, y - other.y)
end

p = Point.new(0, 0)
q = Point.new(3, 4)
puts p.distance_to(q)                # 5.0

# 8) Data — immutable value object (Ruby 3.2+)
Money = Data.define(:amount, :currency)
m = Money.new(amount: 9.99, currency: 'AUD')
m.amount       # 9.99
# m.amount = 1 — NoMethodError; immutable

# 9) ObjectSpace + reflection — rarely needed, but handy in tests
User.instance_methods(false)         # methods defined on User itself

Why it matters

Reach for Data.define (3.2+) when you want a value object — immutable, value-equality semantics, and one line of code. Struct is the mutable older sibling.

Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.

Example

Example
class User
    attr_accessor :name, :age
    def initialize(name, age)
        @name = name
        @age  = age
    end
    def greet = "hi, #{@name}"
end
puts User.new('Ada', 36).greet
Try it Yourself »

Exercise

Constructor method name.

def (name) @name = name end

Test yourself

Q1. A constructor in Ruby is named…
Q2. attr_accessor generates…
Q3. Open a class again later with…

Discussion

Loading…