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

Inheritance

Ruby has single inheritance via class Child < Parent plus modules for shared behaviour (include, prepend, extend). The Method Resolution Order (MRO) walks ancestors top-down.

Inheritance, modules, super, prepend

EXAMPLE
# 1) Single inheritance
class Animal
    def initialize(name)
        @name = name
    end
    def sound = "?"
    def describe = "#{self.class.name.downcase} called #{@name} says #{sound}"
end

class Dog < Animal
    def sound = "woof"
end

puts Dog.new("Rex").describe   # "dog called Rex says woof"

# 2) super — call the parent method
class Cat < Animal
    def initialize(name, color)
        super(name)
        @color = color
    end
    def describe
        "#{super} (#{@color})"
    end
end

# 3) super vs super() — args
class Foo < Bar
    def call(a, b)
        super         # passes a, b to parent
        super()       # passes NOTHING to parent
        super(a)      # passes ONLY a
    end
end

# 4) Modules — share behaviour without inheritance
module Auditable
    def audit(action)
        puts "[#{Time.now}] #{self.class.name}#\#{action}"
    end
end

module Discountable
    def discount(percent)
        @price * (1 - percent / 100.0)
    end
end

class Order
    include Auditable           # adds instance methods
    include Discountable
    def initialize(price) = @price = price
end

o = Order.new(100)
o.audit("create")
puts o.discount(10)         # 90.0

# 5) extend — add module methods as CLASS methods
module SiteHelpers
    def site_name = "My Site"
end
class Page
    extend SiteHelpers
end
Page.site_name              # "My Site"

# 6) include vs prepend — controls ancestor order
module Logger
    def call(*args)
        puts "calling"
        super
    end
end

class Service
    prepend Logger              # Logger#call WRAPS Service#call (super calls down)
    def call(name) = "hello #{name}"
end

puts Service.new.call("Ada")
# calling
# hello Ada

Service.ancestors
# [Logger, Service, Object, Kernel, BasicObject]

# 7) Method Resolution Order
# Ruby walks ancestors top-to-bottom; the first defined method wins.
puts Service.ancestors    # check what's included where

# 8) Abstract-ish base class — raise NotImplementedError
class Reporter
    def render = raise NotImplementedError, "subclasses must override #render"
    def title  = "Untitled"
end

class PdfReporter < Reporter
    def render = "<pdf>"
end

# 9) Singleton class — define a method on ONE object
ada = User.new("Ada")
def ada.greeting = "Hello, Ada specifically!"
ada.greeting                # works
# User.new("Bo").greeting    # NoMethodError

# 10) Modules with shared state (rarely needed) — use class-level vars carefully
module Counter
    def self.included(base)
        base.class_variable_set(:@@count, 0)
        base.define_singleton_method(:count) { class_variable_get(:@@count) }
    end
    def bump = self.class.class_variable_set(:@@count, self.class.class_variable_get(:@@count) + 1)
end

# 11) Composition > inheritance
# Default to MODULES + COMPOSITION; reach for inheritance only when there's a true is-a relationship.
# Inheritance trees deeper than ~3 levels become hard to read.

Why it matters

prepend + super is the cleanest way to wrap behaviour (logging, caching, timing) without forking the original method. The decorator pattern, Ruby-style — one module per concern.

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

Example

Example
class Animal
    def speak; '…'; end
end
class Dog < Animal
    def speak; 'woof'; end
end
Try it Yourself »

Discussion

Loading…