DEV Community

Hrishi Mittal
Hrishi Mittal

Posted on • Originally published at learnetto.com

What is the difference between include and extend in Ruby?

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
end

class User
include Greetable
end

user = 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
end

class User
extend Greetable
end

puts 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
end

user = 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::Job

def 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
end

class Product < ApplicationRecord
extend Searchable
end

Product.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
end

class Post < ApplicationRecord
include Taggable
end

post = 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
end

class 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. 

This lesson is from Full Stack Rails Mastery.

Top comments (0)