This lesson is from Full Stack Rails Mastery.
In Ruby, include and extend are two Module methods that allow you to share functionality between classes, but they do so in subtly different ways.
This can lead to confusion, especially in a Rails context where both are commonly used.
In short: Use include for instance-level behaviour and extend for class-level functionality.This article looks at include and extend in detail, breaking down their differences and providing real-world examples from Rails applications to help you decide when to use each. The Basics of include and extend include: When you include a module, its methods become instance methods of the including class. This means every instance of the class gains access to the module’s methods.
Example:
module Greetable
def greet
"Hello!"
end
endclass User
include Greetable
enduser = User.new
puts user.greet # Outputs "Hello!"
extend: When you extend a module, its methods become class methods of the extending class (or object). This means the methods are available on the class itself, not its instances.
Example:
module Greetable
def greet
"Hello!"
end
endclass User
extend Greetable
endputs User.greet # Outputs "Hello!"
Using extend on individual objectsYou can also use extend by calling it on an object. In our example above, if we didn't include the Greetable module in the User class, we can still run this on a User object like this:
module Greetable
def greet
"Hello!"
end
enduser = User.new
user.extend(Greetable)puts user.greet # Outputs "Hello!"
When you call extend on an object (rather than a class), the methods from the module are added as singleton methods to that specific object. This allows you to modify the behaviour of individual objects dynamically, without affecting the class or other instances.
Here: 1. extend adds the greet method to this specific user object. 2. Other instances of User remain unaffected:
another_user = User.new
another_user.greet # Raises a NoMethodError
Real-Life Use Cases in Rails Let’s explore common scenarios in Rails where include and extend are used effectively. 1. Using extend: Class-Level Behaviour Example: Adding user-friendly ids and slugs to a Model with the FriendlyId gem
class Article < ApplicationRecord
extend FriendlyId
friendly_id :title, use: [:slugged, :history, :finders]
end
Here, extend is used because FriendlyId provides methods like friendly_id that operate at the class level, configuring how slugs are generated and handled for the entire model. Other Examples: • Enumerize for defining enumerated attributes. • Utility methods for querying or filtering records (Searchable module).
2. Using include: Instance-Level Behaviour Example: Adding Background Job Functionality with Sidekiq::Job
class AddUserToMailingListJob
include Sidekiq::Jobdef perform(user_id)
user = User.find(user_id)
MailingList.add(user.email)
end
end
Here, include is used because Sidekiq::Job provides methods that operate on instances of the job class. Sidekiq creates a new instance of AddUserToMailingListJob for each job execution. Other Examples: • Models: Adding reusable instance methods (e.g., Taggable for tags). • Controllers: Sharing logic via concerns (e.g., authentication filters).
Key Scenarios with Code Examples Scenario 1: Sharing Class-Level Utility Methods
module Searchable
def search_by_name(name)
where("name LIKE ?", "%#{name}%")
end
endclass Product < ApplicationRecord
extend Searchable
endProduct.search_by_name("Gadget") # Class-level behaviour
Here, extend makes the search_by_name method available to the Product class for querying records. Scenario 2: Adding Reusable Instance Methods
module Taggable
def tags_list
tags.map(&:name).join(", ")
end
endclass Post < ApplicationRecord
include Taggable
endpost = Post.new
puts post.tags_list # Instance-level behaviour
Here, include allows each instance of Post to access tags_list, which operates on instance-specific data. Scenario 3: Reusable Controller Logic
module Authenticated
def authenticate_user
redirect_to login_path unless current_user
end
endclass AdminController < ApplicationController
include Authenticated
before_action :authenticate_user
end
Here, include adds authentication logic as reusable instance methods for controllers.
Another example is for pagination with the Pagy gem:
class ApplicationController < ActionController::Base
include Pagy::Backend
end
In summary, use include for instance-level behaviour and extend for class-level functionality. By following the examples and guidelines above, you can write cleaner, more maintainable code in your Rails applications.
Top comments (0)