Reason #136 • May 16th, 2026

Extending classes on inclusion via Module#included

Earlier this week we looked at how to include and extend classes with modules.

For simple cases, that's all we need. However, a common pattern is to provide both instance methods and class methods from the same module. Such modules commonly define the included hook, which is called when the module is included in a class. This allows the module to extend the class with additional methods at the moment of inclusion:

Ruby
module Discountable
  def self.included(base)
    base.extend ClassMethods
  end

  def sale_price(discount_percent = self.class.discount_percent)
    price * (1 - discount_percent / 100.0)
  end

  module ClassMethods
    def discount_by(percent)
      @discount_percent = percent
    end

    def discount_percent
      @discount_percent || 0
    end
  end
end

class Tea
  include Discountable
  discount_by 10

  attr_reader :name, :price

  def initialize(name, price)
    @name = name
    @price = price
  end
end

class Vinyl
  include Discountable
  discount_by 25

  attr_reader :artist, :price

  def initialize(artist, price)
    @artist = artist
    @price = price
  end
end

Tea.new("Chai Latte", 5.50).sale_price
# => 4.95

Vinyl.new("The Dark Side of the Moon", 32.00).sale_price
# => 24.0
    

In this new take on the Discountable module, we can set a default discount percentage for each class that includes it, and the instance method sale_price will use that percentage when calculating the sale price.

Note the idiomatic pattern of defining a ClassMethods module inside the main module, and then extending the base class with it in the included hook, keeping the class methods nicely separated from the instance methods.

History

The included hook shipped in Ruby 1.8.0, released in August 2003.

The companion extended hook followed in Ruby 1.8.1, released in December 2003.

Reason #137 ?