Tutorial: Modules in Ruby

Modules in Ruby are a way to group related methods, constants, and classes together.

They are similar to classes but cannot be instantiated. Modules are primarily used for namespacing and mixins.

What You’ll Learn

1. What Are Modules?

A module in Ruby is a collection of methods and constants. Modules serve two main purposes:

  1. Namespacing: Avoiding name collisions by grouping related code.
  2. Mixins: Adding shared functionality to classes using include or extend.

2. Defining and Using Modules

Syntax

module ModuleName
  # Methods and constants
end

Example: Basic Module

module Greetings
  def self.say_hello
    puts "Hello!"
  end
end

# Accessing module method
Greetings.say_hello  # Output: Hello!

3. Including Modules (Mixins)

Including a module allows its methods to be added as instance methods to a class.

Syntax

class ClassName
  include ModuleName
end

Example: Adding Instance Methods

module Greetings
  def say_hello
    puts "Hello from a module!"
  end
end

class Person
  include Greetings
end

person = Person.new
person.say_hello  # Output: Hello from a module!

4. Extending Modules

Extending a module adds its methods as class methods.

Syntax

class ClassName
  extend ModuleName
end

Example: Adding Class Methods

module Greetings
  def say_hello
    puts "Hello from a module!"
  end
end

class Person
  extend Greetings
end

Person.say_hello  # Output: Hello from a module!

5. Using Modules for Namespacing

Modules can be used to group related classes or methods, avoiding name collisions in large projects.

Example: Namespacing

module MathTools
  class Calculator
    def add(a, b)
      a + b
    end
  end
end

calc = MathTools::Calculator.new
puts calc.add(5, 3)  # Output: 8

6. Practical Examples

6.1 Using Constants in Modules

Modules can define constants that are accessible with the :: operator.

module Settings
  DEFAULT_LANGUAGE = "English"
end

puts Settings::DEFAULT_LANGUAGE  # Output: English

6.2 Multiple Modules in a Class

A class can include multiple modules, allowing it to inherit behaviors from several sources.

module Greetings
  def greet
    puts "Hello!"
  end
end

module Farewell
  def say_goodbye
    puts "Goodbye!"
  end
end

class Person
  include Greetings
  include Farewell
end

person = Person.new
person.greet       # Output: Hello!
person.say_goodbye # Output: Goodbye!

6.3 Using prepend

The prepend keyword adds module methods before the methods defined in the class.

module Greetings
  def greet
    puts "Hello from the module!"
  end
end

class Person
  prepend Greetings

  def greet
    puts "Hello from the class!"
  end
end

person = Person.new
person.greet  # Output: Hello from the module!

6.4 Mixing in Enumerable

The Enumerable module provides useful methods like map, select, and reduce.

class CustomCollection
  include Enumerable

  def initialize(elements)
    @elements = elements
  end

  def each(&block)
    @elements.each(&block)
  end
end

collection = CustomCollection.new([1, 2, 3, 4])
puts collection.map { |x| x * 2 }.inspect  # Output: [2, 4, 6, 8]

6.5 Using extend for Specific Objects

You can use extend to add module methods to a single object.

module Greetings
  def greet
    puts "Hello from an extended module!"
  end
end

person = Object.new
person.extend(Greetings)
person.greet  # Output: Hello from an extended module!

6.6 Module Aliases and Refinements

Modules can alias methods or refine behavior for specific use cases.

Alias Example

module Greetings
  def say_hello
    puts "Hello!"
  end

  alias greet say_hello
end

class Person
  include Greetings
end

person = Person.new
person.greet  # Output: Hello!

Refinements

module StringExtensions
  refine String do
    def shout
      upcase + "!!!"
    end
  end
end

using StringExtensions
puts "hello".shout  # Output: HELLO!!!

6.7 Combining Namespaces and Mixins

module Utility
  module MathTools
    def add(a, b)
      a + b
    end
  end

  module StringTools
    def reverse_string(str)
      str.reverse
    end
  end
end

class Calculator
  include Utility::MathTools
end

class TextProcessor
  include Utility::StringTools
end

calc = Calculator.new
puts calc.add(5, 7)  # Output: 12

text = TextProcessor.new
puts text.reverse_string("Ruby")  # Output: ybuR

7. Best Practices for Using Modules

  1. Use for Shared Behavior: Use modules to define methods that multiple classes can share.
  2. Avoid Excessive Nesting: Deeply nested modules can make your code harder to read.
  3. Prefer Mixins for Reusability: Use include for instance methods and extend for class methods.
  4. Keep Methods Focused: Group related methods together within a module.
  5. Leverage Namespacing: Use modules to avoid name collisions in large projects.

8. Summary

Key Concepts

  • Modules are collections of methods and constants that cannot be instantiated.
  • Use include for instance methods, extend for class methods, and prepend to prioritize module methods.
  • Modules help organize code and avoid name conflicts through namespacing.

Examples in Context

Ruby modules are a powerful tool for organizing code, sharing behaviors across classes, and creating clean, reusable components.

Related posts

Tutorial: Blocks in Ruby

Tutorial: Methods in Ruby

Tutorial: if…else Statements in Ruby