Skip to Content
Course content
Click on the "Edit" button in the top corner of the screen to edit your slide content.

Think about professional certifications in the real world. You might have a person who is a "Project Manager" (their primary job/class). Now, that person might also be "CPR Certified" and "Scrum Certified." They aren't a "CPR" by nature—that's not their identity—but they have the capability to perform CPR. If you have a "Nurse" and a "Lifeguard," they are completely different roles, but both of them might be "CPR Certified."

In Ruby, modules are exactly like those certifications. They allow us to define a set of behaviors (methods) that can be plugged into any class, regardless of where that class sits in the inheritance tree. Here is how that maps to the code:

  • The Certification is the module. It's a collection of methods, but it can't be instantiated on its own (you can't just "create" a CPR certification; you create a person who has it).
  • The Skill is the def inside that module. It's the actual action the person can perform.
  • Getting Certified is the include keyword. This is where you tell Ruby, "Give this class all the capabilities defined in this module."

When the Family Tree Isn't Enough

Up until now, you've used class inheritance. But Ruby only allows single inheritance—a class can have only one parent. This is a common wall I see developers hit. You might have a User class and a Product class. Suddenly, you realize both need a way to be converted into a JSON string for an API. You can't make them both inherit from a JsonConvertible class because they already have their own parents.

This is where "Mixins" come in. A Mixin is simply a module that you include in a class to add functionality. Check this out:

module Exportable
  def to_csv
    # I'm pretending we're doing real CSV logic here
    puts "Converting #{self.class} attributes to CSV format..."
    "id,name,created_at"
  end
end

class User
  include Exportable
  attr_accessor :name
end

class Product
  include Exportable
  attr_accessor :price
end

# Now both can export, even though they are totally different things
u = User.new
u.to_csv # Works!

p = Product.new
p.to_csv # Works too!

I love this pattern because it keeps your classes lean. Instead of bloating a base class with every possible utility method, you create small, focused modules that do one thing well and plug them in only where they're needed.

Keeping Your Code from Cluttering the Room

Modules aren't just for mixins; they are also used as Namespaces. As your project grows, you'll start running into "name collisions." You might have a Client class that handles a database connection, but then you add a Client class that handles a customer's account. Ruby will get confused (or rather, it'll just overwrite the first one).

By wrapping your classes in a module, you're essentially putting them in a labeled folder.

module Billing
  class Client
    def process_invoice
      puts "Processing money..."
    end
  end
end

module Network
  class Client
    def connect
      puts "Connecting to socket..."
    end
  end
end

# To use them, you use the scope resolution operator (::)
billing_user = Billing::Client.new
network_conn = Network::Client.new

I usually recommend this for any project that's larger than a few scripts. It makes it immediately obvious to anyone reading your code exactly what the "domain" of a class is just by looking at its namespace.




📋 Practical Task

Building a Smart Home Capability System

You are designing a system for a smart home. You have different devices: LightBulb, Thermostat, and SmartLock. Some of these devices can be controlled via a timer, and some can be controlled via voice commands, but not all of them support both.

Your task:

  1. Create a module named TimerControlled with a method set_timer(duration) that prints: "Timer set for [duration] minutes."
  2. Create a module named VoiceControllable with a method voice_command(cmd) that prints: "Executing voice command: [cmd]."
  3. Create three classes: LightBulb, Thermostat, and SmartLock.
  4. Mix in the capabilities:
    • The LightBulb should be both TimerControlled and VoiceControllable.
    • The Thermostat should only be TimerControlled.
    • The SmartLock should only be VoiceControllable.
  5. Instantiate one of each device and call their respective module methods to verify they work (and that a Thermostat cannot perform a voice_command).
Rating
0 0

There are no comments for now.

to be the first to leave a comment.