With Rails 4, the concept of using concerns has been in highlights. Concerns provide a easy yet powerful technique to group similar code of a model or across multiple models in a module (that extends ActiveSupport::Concern ). This concern module is then included in the required models.
Concerns in models can be used both for
1) DRYing up model codes (by domain based grouping of common codes between models )
2) Skin-nizing Fat Models (by dividing the code based on domains and creating a concern for each domain.)
1) DRYing up model codes
Concerns in models can be used both for
1) DRYing up model codes (by domain based grouping of common codes between models )
2) Skin-nizing Fat Models (by dividing the code based on domains and creating a concern for each domain.)
1) DRYing up model codes
Consider a Article model, a Event model and a Comment Model. A article or A event has many comments. A comment belongs to either article or event.
Traditionally, the models may look like this:
Comment Model:
class Comment < ActiveRecord::Base
belongs_to :commentable, polymorphic: true
end
Article Model:
class Article < ActiveRecord::Base
has_many :comments, as: :commentable
def find_first_comment
comments.first(created_at DESC)
end
def self.least_commented
#return the article with least number of comments
end
end
Event Model
class Event < ActiveRecord::Base
has_many :comments, as: :commentable
def find_first_comment
comments.first(created_at DESC)
end
def self.least_commented
#returns the event with least number of comments
end
end
As we can notice, there is a significant piece of code common to both Event and Article Model. Using concerns we can extract this common code in a separate module Commentable.
For this create a commentable.rb file in app/model/concerns.
module Commentable
extend ActiveSupport::Concern
included do
has_many :comments, as: :commentable
end
# for the given article/event returns the first comment
def find_first_comment
comments.first(created_at DESC)
end
module ClassMethods
def least_commented
#returns the article/event which has the least number of comments
end
end
end
As you can see while using concerns:
* The included block is executed within the context of the class that is including the module.
* All the class methods go into the module ClassMethods of the concern (no need to add
'self.' to these methods).
* The instance methods are written directly in the concern module.
And Now your models look like this :
Comment Model:
class Comment < ActiveRecord::Base
belongs_to :commentable, polymorphic: true
end
Article Model:
class Article < ActiveRecord::Base
include Commentable
end
Event Model:
class Event < ActiveRecord::Base
include Commentable
end
2) Skin-nizing Fat Models.
Consider a Event model. A event has many notes and comments.Typically, the event model might look like this:
class Event < ActiveRecord::Base
has_many :comments
has_many :attenders
def find_first_comment
# for the given article/event returns the first comment
end
def find_comments_with_word(word)
# for the given event returns an array of comments which contain the given word
end
def self.least_commented
# finds the event which has the least number of comments
end
def self.most_attended
# returns the event with most number of attendes
end
def has_attendee(attendee_id)
# returns true if the event has the mentioned attendee
end
end
Models with many associations and otherwise have tendency to accumulate more and more code and become unmanageable.Concerns provide a way to skin-nize fat modules making them more modularized and easy to understand.
The above model can be refactored using concerns as below: Create a attendable.rd and commentable.rb file in app/model/concern/event folder
attendable.rb
module Attendable
extend ActiveSupport::Concern
included do
has_many :attenders
end
def has_attender(attender_id)
# returns true if the event has the mentioned attendee
end
module ClassMethods
def most_attended
# returns the event with most number of attendes
end
end
end
commentable.rb
module Commentable
extend ActiveSupport::Concern
included do
has_many :comments
end
def find_first_comment
# for the given article/event returns the first comment
end
def find_comments_with_word(word)
# for the given event returns an array of comments which contain the given word
end
module ClassMethods
def least_commented
# finds the event which has the least number of comments
end
end
end
And now using the above Concerns , your Event model reduces to
class Event < ActiveRecord::Base
include Commentable
include Attendable
end
* It is advisable to make concerns domain based rather than technical based. For instance, concerns like 'Commentable', 'Photoable' are examples of domain based concerns. While concerns like 'FinderMethods', 'ValidationMethods' are examples of technically grouped concerns.
Here are links of some write ups for Model Concerns that I found very useful :
No comments:
Post a Comment