There are three ways to add methods from a module to a class in Ruby.
Include
It adds methods as instance methods
module Swimmable
def swim
"I can swim!"
end
end
class Fish
include Swimmable
end
fish = Fish.new
puts fish.swim # => "I can swim!"
puts Fish.ancestors # => [Fish, Swimmable, Object, Kernel, BasicObject]
Extend
It adds methods as class methods
module Flyable
def fly
"I can fly!"
end
end
class Bird
extend Flyable
end
puts Bird.fly # => "I can fly!"
puts Bird.ancestors # => [Bird, Object, Kernel, BasicObject]
Prepend
It adds methods as instance methods but with higher precedence.
module Logger
def log_action
"Logged: #{super}"
end
end
class Action
prepend Logger
def log_action
"Some action"
end
end
action = Action.new
puts action.log_action # => "Logged: Some action"
puts Action.ancestors # => [Logger, Action, Object, Kernel, BasicObject]
Normally, prepend is defined at the end of the class definition.
You can also prepend a module outside the class definition
Action.prepend(Logger)
Consecutive prepends
module Formatter
def display
"Formatted: #{super}"
end
end
module Validator
def display
"Validated: #{super}"
end
end
class Report
prepend Formatter
prepend Validator
def display
"Report content"
end
end
report = Report.new
puts report.display # => "Validated: Formatted: Report content"
puts Report.ancestors # => [Validator, Formatter, Report, Object, Kernel, BasicObject]
When there are multiple prepends, the first lookup is from the last prepend.
Hence the lookup flow would be as follows.
prepend(n) -> prepend(n-1) -> class -> include(n) -> include(n-1)
Prepend is used to insert a module's methods at the beginning of a class's method lookup chain, before the class's own methods. This means that when a method is called, Ruby will first look for it in the prepended module before looking in the class itself.