Modules
Modules are collections of methods and constants. They can't be instantiated or inherit from other classes. Think of them as a way to group related functionality together. Modules solve two big problems: namespacing and multiple inheritance.
module Greeter
def greet(name)
"Hello, #{name}!"
end
def farewell(name)
"Goodbye, #{name}!"
end
end
class Host
include Greeter
end
host = Host.new
puts host.greet("Alice") # => "Hello, Alice!"
puts host.farewell("Bob") # => "Goodbye, Bob!"
Try it Yourself ->
Modules as Namespaces
Modules act as namespaces to prevent naming collisions. You wrap your code inside a module and reference it with the module name. This is how large Ruby projects stay organized.
module MyApp
module Auth
class User
def initialize(name)
@name = name
end
def greet
"I am #{@name} from MyApp::Auth"
end
end
end
module Payments
class User
def initialize(id)
@id = id
end
def details
"User ##{@id} in MyApp::Payments"
end
end
end
end
auth_user = MyApp::Auth::User.new("Alice")
pay_user = MyApp::Payments::User.new(42)
puts auth_user.greet # => "I am Alice from MyApp::Auth"
puts pay_user.details # => "User #42 in MyApp::Payments"
Try it Yourself ->
Module Methods
You can define methods directly on a module using self. These are called module methods and you call them on the module itself, not on instances.
module MathHelper
def self.circle_area(radius)
Math::PI * radius ** 2
end
def self.degrees_to_radians(degrees)
degrees * Math::PI / 180
end
end
puts MathHelper.circle_area(5) # => 78.53981633974483
puts MathHelper.degrees_to_radians(90) # => 1.5707963267948966
Try it Yourself ->
Built-in Modules
Ruby comes with powerful modules you'll use all the time. Enumerable gives you collection methods. Comparable adds comparison operators. Math gives you mathematical functions.
include Math
puts sqrt(144) # => 12.0
puts PI # => 3.141592653589793
# Comparable
class Temperature
include Comparable
attr_reader :degrees
def initialize(degrees)
@degrees = degrees
end
def <=>(other)
@degrees <=> other.degrees
end
end
t1 = Temperature.new(20)
t2 = Temperature.new(35)
puts t1 < t2 # => true
puts [t2, t1].sort.map(&:degrees) # => [20, 35]
Try it Yourself ->
Why Modules Over Multiple Inheritance
Ruby chose modules because they give you the benefits of multiple inheritance without the headaches. With modules you can mix in any number of behaviors. There's no diamond problem because modules don't have state conflicts the way classes do. Constants live in their own namespace. Module methods don't override instance methods. It's a cleaner, simpler design.
module Loggable
def log(message)
puts "[LOG] #{message}"
end
end
module Serializable
def to_hash
instance_variables.each_with_object({}) do |var, hash|
hash[var.to_s.delete("@").to_sym] = instance_variable_get(var)
end
end
end
class Product
include Loggable
include Serializable
def initialize(name, price)
@name = name
@price = price
end
end
product = Product.new("Widget", 29.99)
product.log("Product created") # => [LOG] Product created
puts product.to_hash # => {:name=>"Widget", :price=>29.99}
Try it Yourself ->