Code Concerns in Rails 4 Models
This tutorial will teach you how to use code concerns in Ruby on Rails 4.0.
Published on:June 28, 2013
Ruby on Rails Concerns This tutorial will teach you how to use ActiveModel concerns in Ruby on Rails.
You may have noticed that Rails 4 creates a new folder called concerns. Concerns are pieces of code that allow you to better organize the code that you write. This feature has actually been around for a long time prior to Rails 4, Rails 4 merely creates the folders for you and sets up the environment. In this article, we focus primarily on Rails 4.0, but it should apply equally to 3.2.x or even 3.0.x and earlier.
Here is a simple example, lets say we have a model called user. In this model we would typically have something like this:
class User < ActiveRecord::Base
has_secure_password
def self.authenticate(email, password)
user = find_by_email(email)
user if !user.nil? && user.authenticate(password)
end
def create_password_reset_token
logger.warn "Create password reset token code goes here."
false
end
end
We can create a a file in the app/models/concerns folder called authentication.rb (app/models/concerns/authentication.rb) and place the following code in it:
module Authentication
extend ActiveSupport::Concern
included do
has_secure_password
end
module ClassMethods
def authenticate(email, password)
user = find_by_email(email)
user if user && user.authenticate(password)
end
end
def create_password_reset_token
logger.warn "Create password reset token code goes here."
false
end
end
Now, lets refactor the User model to use the new code. Change the users model (app/models/user.rb) so that it looks like this:
class User < ActiveRecord::Base
include Authentication
end
Now if you start the rails server and attempt to authenticate, you'll notice that the functionality hasn't changed at all. That's the idea! You can use this method for organizing and DRYing up your code.
Now lets examine this code step by step.
The extend ActiveSupport::Concern
tells rails that we are creating a concern.
The code contained within the included
block will be executed within the context of the class that is including the module. This is perfect for including functionality provided by 3rd party gems, etc.
Next you will notice the module ClassMethods
block. The code contained within this block will be added to the Class itself. For example, the code above adds an authenticate function to the User class. This allows you to do User.authenticate(email, password) instead of User.find_by_email(email).authenticate(password).
Finally you will see the last bit of code, the create_password_reset_token
function. Code not included in the ClassMethods
block or the included
block will be included as instance methods. For example, You could do @user = User.find(params[:id])
and then do @user.create_password_reset_token
to create a password reset token for the specified user.
Now, all of this is great, but what benefit do you get from organizing your code in this fashion? Well, lets look at a good example of how utilizing this functionality of Rails can save you time and make your code much cleaner. Lets say we have a number of different models, BlogPosts, Articles, Comments, etc and we want to add tagging functionality that will allow the user to tag each object as something. Utilizing concerns we can quickly and easily do this:
module Taggable
extend ActiveSupport::Concern
included do
has_many :taggings, as: :taggable, dependent: :destroy
has_many :tags, through: :taggings
end
def tag_names
tags.map(&:name)
end
end
In this simple example, you'll see that simply including Taggable on your modules will (with the database structure in place of course) make your models taggable. This code can quickly and easily be spread upon as many models as needed. Well that's it! Concerns are a great way to keep your code organized and DRY.