Modules and Mixins
18.1 Modules as Namespaces
module Payments
VERSION = "1.0"
class Invoice; end
module Gateways
class Stripe; end
end
def self.configure = yield(config) # module function
end
Payments::Gateways::Stripe.new18.2 Mixins: include, extend, prepend
M's methods become instance methods. M goes after the class in ancestors.
M goes before the class in ancestors (wraps/overrides methods, used for decorators)
M's methods become singleton methods of the receiver (class methods when used in a class)
Makes methods callable as M.method and private as mixins (like Math.sqrt)
module Greetable
def greet = "Hello, I'm #{name}"
end
module Trackable
def self.included(base) # hook: add class methods when included
base.extend(ClassMethods)
end
module ClassMethods
def tracked_fields = @tracked_fields ||= []
def track(*fields) = tracked_fields.concat(fields)
end
end
class User
include Greetable # instance methods
include Trackable
include Comparable # requires <=>
attr_reader :name, :age
track :name, :email
def initialize(name, age) = (@name, @age = name, age)
def <=>(other) = age <=> other.age
end
user = User.new("Ana", 30)
user.greet
User.tracked_fields
User.new("A", 20) < User.new("B", 30) # Comparable gives <, >, between?, clamp
module Loud
def speak = super.upcase + "!" # prepend: runs BEFORE the class's method
end
class Dog < Animal
prepend Loud
end
obj = Object.new
obj.extend(Greetable) # extend: adds module methods to a single object (or a class's class methods)18.3 Method Lookup Path
Lookup order: singleton class → prepended modules → the class → included modules (last included first) → superclass chain → method_missing.
User.ancestors
# [User, Comparable, Trackable, Greetable, Object, Kernel, BasicObject]18.4 Built-in Mixins
Comparable (define <=>), Enumerable (define each), Kernel (puts, require, loop), Forwardable (def_delegators), Singleton, Observable (removed from the default gems in 3.4; install the gem), DRb.
require "forwardable"
class Queue2
extend Forwardable
def_delegators :@items, :size, :empty?, :first, :each
def_delegator :@items, :push, :enqueue
def initialize = @items = []
end