Ruby ha ereditarietà singola: una classe ha un solo genitore. I moduli aggirano il limite condividendo comportamenti tra classi diverse tramite include.
module Tracciabile
def log(evento)
puts "[#{Time.now}] #{self.class}: #{evento}"
end
end
class Launch
include Tracciabile
end
class Payload
include Tracciabile
end
Launch.new.log("creato") # entrambe hanno .log
Con include i metodi del modulo diventano metodi di istanza; con extend diventano metodi di classe. È il pattern dei mixin: componi comportamenti come mattoncini invece di ereditare da una super-classe gigante.
Rails ne è pieno (Comparable, Enumerable, i Concern). Un modulo per responsabilità, incluso dove serve: codice condiviso senza gerarchie fragili.