Ruby Mixins

Sharing functionality across multiple classes

🔀 What are Ruby Mixins?

Mixins let you share methods across multiple classes without inheritance. They solve Ruby's single inheritance limitation by allowing classes to include multiple modules for added functionality.


# Define a mixin module
module Printable
  def print_info
    puts "Printing information..."
  end
end

class Document
  include Printable
end

doc = Document.new
doc.print_info
                                    

Output:

Printing information...

Key Mixin Concepts

🧩

Composition

Combine multiple behaviors

include Module1
include Module2
♻️

Reusability

Share code across classes

module Shared
  # methods
end
🎭

Multiple Mixins

Include many modules

class A
  include B, C
end
🔍

Method Lookup

Ruby searches included modules

ancestors

🔹 Creating Mixins

Mixins are modules that add functionality to classes. Create a module with methods, then include it in any class that needs those features:

module Walkable
  def walk
    puts "#{self.class} is walking"
  end
end

module Talkable
  def talk
    puts "#{self.class} is talking"
  end
end

class Human
  include Walkable
  include Talkable
end

class Robot
  include Walkable
  include Talkable
end

human = Human.new
human.walk
human.talk

robot = Robot.new
robot.walk
robot.talk
                            

Output:

Human is walking

Human is talking

Robot is walking

Robot is talking

🔹 Mixins vs Inheritance

Inheritance creates "is-a" relationships, while mixins add "can-do" abilities. Use mixins when classes need shared behavior but aren't related by type:

module Chargeable
  def charge
    puts "Charging battery..."
  end
end

class Phone
  include Chargeable
  
  def call
    puts "Making a call"
  end
end

class Laptop
  include Chargeable
  
  def compute
    puts "Computing..."
  end
end

phone = Phone.new
phone.charge
phone.call

laptop = Laptop.new
laptop.charge
laptop.compute
                            

Output:

Charging battery...

Making a call

Charging battery...

Computing...

🔹 Mixin with Instance Variables

Mixins can access and modify instance variables of the class they're included in. This allows mixins to work with the object's state:

module Nameable
  def full_name
    "#{@first_name} #{@last_name}"
  end
  
  def initials
    "#{@first_name[0]}.#{@last_name[0]}."
  end
end

class Person
  include Nameable
  
  def initialize(first_name, last_name)
    @first_name = first_name
    @last_name = last_name
  end
end

class Author
  include Nameable
  
  def initialize(first_name, last_name, books)
    @first_name = first_name
    @last_name = last_name
    @books = books
  end
end

person = Person.new("John", "Doe")
puts person.full_name
puts person.initials

author = Author.new("Jane", "Smith", 5)
puts author.full_name
puts author.initials
                            

Output:

John Doe

J.D.

Jane Smith

J.S.

🔹 Method Lookup Chain

When you call a method, Ruby searches in a specific order: the class, then included modules (last included first), then parent classes. Use ancestors to see the lookup chain:

module A
  def greet
    puts "Hello from A"
  end
end

module B
  def greet
    puts "Hello from B"
  end
end

class MyClass
  include A
  include B
end

obj = MyClass.new
obj.greet  # Which greet will be called?

puts "\nMethod lookup order:"
puts MyClass.ancestors
                            

Output:

Hello from B

Method lookup order:

MyClass

B

A

Object

Kernel

BasicObject

🔹 Practical Mixin Example

Here's a real-world example showing how mixins add specific capabilities to different classes without forcing them into an inheritance hierarchy:

module Timestampable
  def created_at
    @created_at ||= Time.now
  end
  
  def age
    Time.now - created_at
  end
end

module Taggable
  def add_tag(tag)
    @tags ||= []
    @tags << tag
  end
  
  def tags
    @tags || []
  end
end

class BlogPost
  include Timestampable
  include Taggable
  
  attr_accessor :title
  
  def initialize(title)
    @title = title
  end
end

post = BlogPost.new("Ruby Mixins")
post.add_tag("ruby")
post.add_tag("programming")

puts "Post: #{post.title}"
puts "Tags: #{post.tags.join(', ')}"
puts "Created: #{post.created_at}"
                            

Output:

Post: Ruby Mixins

Tags: ruby, programming

Created: 2025-01-07 10:30:45 +0000

🧠 Test Your Knowledge

What is the main advantage of mixins over inheritance?