If you setup a Rails 4 app, you’ll notice the app/models/concerns and app/controllers/concerns directories. Concerns are modules that can be mixed into your models and controllers to share code between them.
Some developers falsely classify mixins as composition when they are actually a form of inheritance. When you include a module in a class, that module’s methods are added to the inheritance chain just like a parent class’ methods are added to a subclass. So, don’t think you’ve solved the problem of inheritance by simply splitting your inherited code into separate files!
That being said, mixins can be a valuable tool to share code between classes that are otherwise unrelated. Here’s an example of how I chose to use it recently.
I am adding admin reporting features to an app I’m hoping to launch soon. I have an admin controller with a simple before filter to redirect if the current user is not an administrator.
class AdminController < ApplicationController
before_filter :check_admin_user
private
def check_admin_user
unless current_user.admin
flash[:alert] = "You can't be here!"
redirect_to root_path
end
end
end
I need two admin reports that allow filtering by month. To stick with my routing scheme, I want a separate controller for each report. Both controllers need to load months from the database, build a list of months, get the selected month from the query string or month list, and find records where a specific date field falls within the given month/year. The only difference is how those records are processed before being used in the views.
These are the only two admin features so far. So, I started by putting the month filtering code in the AdminController so it could easily be shared by both. However, it’s likely that more admin features will be added later that don’t need month filters. More importantly, month filtering isn’t intrinsic to the admin section of the site. The purpose of the AdminController is to prevent non-admins from accessing the actions. That’s it. The month filtering code doesn’t really belong there.
Where should I put it? How about in a concern?
# app/controllers/concerns/month_filtering_for_contests.rb
module MonthFilteringForContests
extend ActiveSupport::Concern
included do
attr_reader :month, :contests
before_filter :build_month_lists
before_filter :set_current_month
before_filter :load_contests_for_current_month
end
def build_month_lists
@months = Contest.months_with_ended_contests
@month_list = @months.map { |date|
[date.strftime("%B %Y"), date.strftime("%Y-%m")]
}
end
def set_current_month
@month =
if params[:month]
Date.new(*params[:month].split(/-/).map {|part| part.to_i})
else
@months.first
end
end
def load_contests_for_current_month
@contests = Contest.ended_during(@month)
end
end
Now, my AdminController can remain clean and the month filtering module can be included in each report controller.
class Admin::SampleReportController < AdminController
include MonthFilteringForContests
def show
@totals = contests.each_with_object({}) { |contest, hash|
# code to process reporting data here
}
end
end
Now that I look at it, one can argue that everything in AdminController can be moved into it’s own concern and the class can be removed completely. However, it’s 3:02am on Christmas Eve. So, that decision can wait.
source: http://elegantbrew.tumblr.com/post/70990048275/controller-concerns-in-rails-4