Labs ICT
โญ Pro Login

Modules

Namespaces and mixins.

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 ->

๐Ÿงช Quick Quiz

What keyword defines a module?