Ruby
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Methods and Blocks
-
Section 4: Object-Oriented Ruby
-
Section 5: Metaprogramming
-
Section 6: Working with Data and Files
-
Section 7: Ruby Frameworks Overview
-
Section 8: Ecosystem and Testing
-
Section 9: Practical Projects
-
Section 10: Interview Practice
-
Section 11: Data Structures and Algorithms in Ruby
-
Section 12: More Practice Exercises
-
Section 13: Enumerable and Functional Style
-
Section 14: More OOP Practice
-
Section 15: More Testing
-
Section 16: Enumerable and Comparable Modules In Depth
-
Section 17: Ruby Standard Library: Core Utilities
-
Section 18: Ruby Standard Library: Data and Security
-
Section 19: Ruby Standard Library: CLI and Text
-
Section 20: Ruby Networking
-
Section 21: Ruby on Rails Deep Dive
-
Section 22: Ruby Metaprogramming Deep Dive
-
Section 23: Ruby Design Patterns
-
Section 24: Ruby Concurrency
-
Section 25: Ruby Testing Deep Dive
-
Section 26: Ruby Gems and Packaging
-
Section 27: Ruby Performance
-
Section 28: More Practice Exercises
-
Section 29: More Interview Practice
-
Section 30: Rails API Development
-
Section 31: Rails Authentication and Authorization
-
Section 32: Rails Testing Deep Dive
-
Section 33: Rails Performance
-
Section 34: Rails Deployment
-
Section 35: Sinatra and Lightweight Ruby Web Apps
-
Section 36: More Ruby Language Deep Dive
-
Section 37: Ruby 3.x Modern Features
-
Section 38: More Data Structures in Ruby
-
Section 39: More Practical Projects
-
Section 40: Ruby Ecosystem Tools
-
Section 41: More Practice and Review
-
Section 42: Final Practice and Mastery
-
Section 43: Ruby Interview Deep Dive
-
Section 44: Ruby Background Processing Deep Dive
-
Section 45: Ruby GraphQL
-
Section 46: Ruby Object Model Deep Dive
-
Section 47: Ruby Hanami Framework Overview
-
Section 48: Ruby gRPC and Protocol Buffers
-
Section 49: Ruby Data Processing
-
Section 50: Ruby Search Integration
-
Section 51: Ruby File Upload and Media
-
Section 52: Ruby Email and Notifications
-
Section 53: Ruby Admin Panels
-
Section 54: Ruby Feature Flags and Experimentation
-
Section 55: Ruby Monitoring and Observability
-
Section 56: Ruby Docker and Deployment Deep Dive
-
Section 57: Ruby Security Deep Dive
-
Section 58: More Advanced Metaprogramming
-
Section 59: More Final Projects
14: Modules and Mixins
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
definside that module. It's the actual action the person can perform. - Getting Certified is the
includekeyword. 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:
- Create a module named
TimerControlledwith a methodset_timer(duration)that prints: "Timer set for [duration] minutes." - Create a module named
VoiceControllablewith a methodvoice_command(cmd)that prints: "Executing voice command: [cmd]." - Create three classes:
LightBulb,Thermostat, andSmartLock. - Mix in the capabilities:
- The
LightBulbshould be bothTimerControlledandVoiceControllable. - The
Thermostatshould only beTimerControlled. - The
SmartLockshould only beVoiceControllable.
- The
- Instantiate one of each device and call their respective module methods to verify they work (and that a
Thermostatcannot perform avoice_command).
There are no comments for now.