CrackedRuby logo

CrackedRuby

Module Definition

Overview

Ruby modules serve as containers for methods, constants, and classes that can be mixed into other classes or used as namespaces. Module definition creates a named constant that references a Module object, establishing a scope for organizing related functionality.

The module keyword begins a module definition, followed by the module name written in CamelCase. Module definitions end with the end keyword and create a new constant in the current scope.

module Authentication
  TOKEN_EXPIRY = 3600
  
  def generate_token
    SecureRandom.hex(32)
  end
end

Modules cannot be instantiated directly with new like classes. They exist primarily for code organization, mixins through include and extend, and namespace creation. Ruby automatically creates a Module object when parsing module definitions.

# Module as namespace
module APIClient
  class HTTPError < StandardError; end
  
  BASE_URL = "https://api.example.com"
  
  def self.request(endpoint)
    # implementation
  end
end

# Access namespaced elements
APIClient::BASE_URL
APIClient.request("/users")

Module definitions can contain method definitions, constant assignments, class definitions, nested modules, and executable code. The module acts as a lexical scope, creating a constant lookup path that affects constant resolution within the module.

module DataProcessor
  VERSION = "1.0.0"
  
  class ParseError < StandardError; end
  
  module Formatters
    def self.json(data)
      JSON.generate(data)
    end
  end
  
  def self.process(input)
    # Module methods callable without mixing in
    validate_input(input)
  end
  
  private
  
  def self.validate_input(input)
    # Private module methods
  end
end

Basic Usage

Module definition begins with the module keyword followed by the module name. The name must be a constant (starting with uppercase letter) and follows Ruby constant naming conventions.

module UserHelpers
  def full_name(user)
    "#{user.first_name} #{user.last_name}"
  end
  
  def active_users
    User.where(active: true)
  end
end

# Include in class to add instance methods
class UserController
  include UserHelpers
  
  def show
    user = User.find(params[:id])
    render json: { name: full_name(user) }
  end
end

Modules support constant definition within their scope. Constants defined in modules are accessed using the scope resolution operator ::.

module Configuration
  DEFAULT_TIMEOUT = 30
  RETRY_ATTEMPTS = 3
  API_VERSION = "v2"
  
  def self.timeout
    DEFAULT_TIMEOUT
  end
end

# Accessing module constants
timeout = Configuration::DEFAULT_TIMEOUT
Configuration.timeout  # => 30

Module methods are defined using self as the receiver, creating methods callable directly on the module without mixing in.

module MathUtils
  def self.factorial(n)
    return 1 if n <= 1
    n * factorial(n - 1)
  end
  
  def self.fibonacci(n)
    return n if n <= 1
    fibonacci(n - 1) + fibonacci(n - 2)
  end
  
  # Alternative syntax for module methods
  class << self
    def prime?(num)
      return false if num < 2
      (2..Math.sqrt(num)).none? { |i| num % i == 0 }
    end
  end
end

MathUtils.factorial(5)    # => 120
MathUtils.prime?(17)      # => true

Nested modules create hierarchical namespaces. Inner modules are accessed through the outer module's namespace.

module Company
  NAME = "Tech Corp"
  
  module Products
    class Software
      def initialize(name)
        @name = name
      end
    end
    
    module Support
      HOURS = "9AM - 5PM"
      
      def self.contact_info
        "support@techcorp.com"
      end
    end
  end
end

# Access nested elements
software = Company::Products::Software.new("CRM")
hours = Company::Products::Support::HOURS
contact = Company::Products::Support.contact_info

Modules can include other modules, creating composition chains. When a module includes another module, it gains access to the included module's instance methods.

module Trackable
  def created_at
    @created_at ||= Time.now
  end
end

module Auditable
  include Trackable
  
  def audit_trail
    "Created: #{created_at}"
  end
end

class Document
  include Auditable
  
  def initialize(title)
    @title = title
  end
end

doc = Document.new("Report")
doc.audit_trail  # => "Created: 2024-01-15 10:30:00 UTC"

Advanced Usage

Module definition supports metaprogramming through dynamic method creation and module callbacks. The included hook executes when the module is included in another class or module.

module Configurable
  def self.included(base)
    base.extend(ClassMethods)
    base.class_eval do
      @config = {}
    end
  end
  
  module ClassMethods
    def configure
      yield(@config)
    end
    
    def config
      @config
    end
  end
  
  def configured_value(key)
    self.class.config[key]
  end
end

class Service
  include Configurable
  
  configure do |config|
    config[:timeout] = 60
    config[:retries] = 3
  end
end

service = Service.new
service.configured_value(:timeout)  # => 60

Modules support prepend for method interception and decoration patterns. Prepended modules are inserted before the class in the ancestor chain.

module Loggable
  def save
    puts "Saving #{self.class.name}"
    result = super  # Calls the original method
    puts "Saved successfully" if result
    result
  end
end

class User
  def save
    @saved = true
  end
end

# Prepend allows method wrapping
User.prepend(Loggable)
user = User.new
user.save
# Output:
# Saving User  
# Saved successfully

Module refinements provide scoped monkey patching without global modifications. Refinements are activated with using and only affect the current scope.

module StringExtensions
  refine String do
    def palindrome?
      self == self.reverse
    end
  end
end

# Without using refinement
"racecar".palindrome?  # => NoMethodError

class TextAnalyzer
  using StringExtensions
  
  def analyze(text)
    if text.palindrome?
      "#{text} is a palindrome"
    else
      "#{text} is not a palindrome"
    end
  end
end

analyzer = TextAnalyzer.new
analyzer.analyze("racecar")  # => "racecar is a palindrome"

Anonymous modules created with Module.new support dynamic module creation and runtime composition.

def create_validator_module(rules)
  Module.new do
    rules.each do |field, validation|
      define_method("validate_#{field}") do |value|
        case validation
        when :presence
          !value.nil? && value != ""
        when :numeric
          value.is_a?(Numeric)
        when Proc
          validation.call(value)
        end
      end
    end
    
    define_method(:validate_all) do |data|
      rules.keys.all? do |field|
        send("validate_#{field}", data[field])
      end
    end
  end
end

# Create dynamic validation module
ValidationRules = create_validator_module(
  name: :presence,
  age: :numeric,
  email: ->(val) { val.include?("@") }
)

class User
  include ValidationRules
  
  def initialize(data)
    @data = data
  end
  
  def valid?
    validate_all(@data)
  end
end

Module constants support dynamic assignment and introspection. Constants can be assigned conditionally and accessed programmatically.

module EnvironmentConfig
  # Conditional constant assignment
  if ENV['RAILS_ENV'] == 'production'
    API_ENDPOINT = "https://api.production.com"
    DEBUG_MODE = false
  else
    API_ENDPOINT = "https://api.staging.com"  
    DEBUG_MODE = true
  end
  
  # Dynamic constant creation
  %w[USER ADMIN MODERATOR].each_with_index do |role, index|
    const_set("#{role}_LEVEL", index + 1)
  end
  
  def self.constants_hash
    constants.each_with_object({}) do |const_name, hash|
      hash[const_name] = const_get(const_name)
    end
  end
end

EnvironmentConfig::USER_LEVEL     # => 1
EnvironmentConfig::ADMIN_LEVEL    # => 2
EnvironmentConfig.constants_hash  # => {:API_ENDPOINT=>"...", :DEBUG_MODE=>true, ...}

Common Pitfalls

Module constant lookup follows lexical scoping rules that can produce unexpected results when constants are referenced from different contexts.

GLOBAL_CONSTANT = "global"

module A
  CONSTANT = "module A"
  
  def self.lookup_constant
    CONSTANT  # Finds A::CONSTANT
  end
  
  def lookup_from_instance_method
    CONSTANT  # Also finds A::CONSTANT due to lexical scope
  end
end

module B  
  CONSTANT = "module B"
  
  def self.call_a_method
    # This creates a new context for constant lookup
    A.new.extend(A).lookup_from_instance_method  # Finds A::CONSTANT
  end
  
  def self.dynamic_lookup
    # Dynamic contexts may not follow lexical scoping
    eval("CONSTANT")  # Finds B::CONSTANT in this context
  end
end

# Unexpected behavior with include
class Example
  CONSTANT = "class constant"
  include A
  
  def check_constant
    CONSTANT  # Finds Example::CONSTANT, not A::CONSTANT
  end
end

Method name collisions occur when including multiple modules with identical method names. Ruby uses the last included module's methods.

module A
  def helper_method
    "from module A"
  end
end

module B  
  def helper_method
    "from module B" 
  end
end

class Service
  include A
  include B  # B's methods override A's methods
end

service = Service.new
service.helper_method  # => "from module B"

# Check method lookup chain
Service.ancestors  # => [Service, B, A, Object, ...]

# Accessing overridden methods requires explicit module reference
class Service
  def call_a_method
    A.instance_method(:helper_method).bind(self).call
  end
end

Module extension with extend adds methods as singleton methods, not instance methods, causing confusion about method availability.

module Utilities
  def format_date(date)
    date.strftime("%Y-%m-%d")
  end
end

class Report
  extend Utilities  # Methods become class methods
end

# This works
Report.format_date(Date.today)

# This fails
report = Report.new
report.format_date(Date.today)  # => NoMethodError

# Common mistake: mixing extend and include
class Document
  include Utilities  # Instance methods
  extend Utilities   # Class methods - now available both ways
end

# Both work but may indicate design confusion
Document.format_date(Date.today)      # Class method
Document.new.format_date(Date.today)  # Instance method

Private and protected method visibility in modules does not behave identically to classes when methods are mixed in.

module SecureOperations
  def public_method
    private_helper  # This works within module context
  end
  
  private
  
  def private_helper
    "secret operation"
  end
end

class Processor
  include SecureOperations
  
  def process
    private_helper  # This works - private methods become private in including class
  end
  
  def external_call
    other_processor = Processor.new
    other_processor.private_helper  # => NoMethodError: private method
  end
end

# Module privacy affects constants differently
module PrivateConstants
  PRIVATE_CONST = "hidden"
  private_constant :PRIVATE_CONST
end

class User
  include PrivateConstants
  
  def access_constant
    PRIVATE_CONST  # This works within the class
  end
end

# External access fails
PrivateConstants::PRIVATE_CONST  # => NameError: private constant

Circular dependencies between modules can cause loading issues and infinite recursion in method calls.

# file: module_a.rb
require_relative 'module_b'

module ModuleA
  include ModuleB
  
  def method_a
    method_b + " -> A"
  end
end

# file: module_b.rb  
require_relative 'module_a'

module ModuleB
  include ModuleA
  
  def method_b
    method_a + " -> B"  # Infinite recursion
  end
end

# Loading either file causes circular require
# Using the modules causes stack overflow

# Solution: break circular dependency
module SharedBehavior
  def shared_method
    "shared"
  end
end

module ModuleA
  include SharedBehavior
  
  def method_a
    shared_method + " -> A"
  end
end

module ModuleB
  include SharedBehavior
  
  def method_b  
    shared_method + " -> B"
  end
end

Autoloading modules can cause issues when module names don't match file names or directory structure, leading to LoadError or NameError exceptions.

# Incorrect: file named user_helper.rb
module UserHelpers  # Should be UserHelper (singular)
  def format_name
    # implementation
  end
end

# Rails autoloader expects:
# app/models/user_helper.rb -> UserHelper
# app/models/user_helpers.rb -> UserHelpers

# Nested module autoloading issues
module API
  module V1
    class UsersController  # Expected file: app/controllers/api/v1/users_controller.rb
    end
  end
end

# Incorrect nesting can break autoloading
module API::V1  # This syntax can cause issues with autoloading
  class UsersController
  end
end

# Prefer explicit nesting for autoloading compatibility
module API
  module V1
    class UsersController
    end
  end
end

Reference

Module Definition Syntax

Syntax Description
module Name Basic module definition
module Name::Nested Nested module shorthand (avoid with autoloading)
module Name; end Empty module definition
Module.new { } Anonymous module creation

Module Methods

Method Parameters Returns Description
#include(module) Module self Adds module methods as instance methods
#extend(module) Module self Adds module methods as singleton methods
#prepend(module) Module self Inserts module before class in ancestor chain
#included_modules None Array Returns array of included modules
#ancestors None Array Returns complete ancestor chain
#const_defined?(name) String/Symbol Boolean Checks if constant is defined
#const_get(name) String/Symbol Object Retrieves constant value
#const_set(name, value) String/Symbol, Object Object Sets constant value
#constants None Array Returns array of constant names

Module Callbacks

Callback Parameters Description
included(base) Class/Module Called when module is included
extended(base) Object Called when module extends an object
prepended(base) Class/Module Called when module is prepended
const_missing(name) Symbol Called when undefined constant is referenced
method_added(name) Symbol Called when method is defined in module

Visibility Modifiers

Modifier Scope Description
public Methods defined after Methods accessible everywhere
protected Methods defined after Methods accessible within class hierarchy
private Methods defined after Methods accessible only within same object
private_constant Specific constants Makes constants private
deprecate_constant Specific constants Marks constants as deprecated

Module Introspection

Method Returns Description
#name String Module name
#to_s String String representation
#inspect String Detailed string representation
#instance_methods Array Public and protected instance methods
#private_instance_methods Array Private instance methods
#singleton_methods Array Module methods (class methods)

Refinement Methods

Method Parameters Returns Description
refine(class) Class Module Creates refinement for class
using(module) Module self Activates refinements in current scope
import_methods(module) Module self Imports methods from module

Common Constants

Constant Value Description
Module Class The Module class itself
Object Class Root class for all objects
BasicObject Class Minimal root class

Error Classes

Exception Description
NameError Undefined constant or variable
NoMethodError Undefined method
ArgumentError Wrong number or type of arguments
LoadError Cannot load required file
CircularInclusionError Circular inclusion detected