Using Concern To Refactor Rails Model
Background
Recently I’m working on upgrading a Rails 2.3 application to Rails 4.2 (Yes, it’s true, our company is still using 2.3 now, ( ‘Θ’)ノ( ‘Θ’)ノ( ‘Θ’)ノ). There are 5 models containing codes which are 90% the same. I tried to refactor them with Rails 4’s concern module.
What is concern
Rails 2.3 way
In Rails 2.3, if you extract code from model to a module, you need to create a module and put it into your /lib
directory. To realize validations and class methods, you need to add a hook method in it, which was not an elegant way.
module Taggable
def self.included(base)
base.extend ClassMethods
base.class_eval do
scope :disabled, -> { where(disabled: true) }
# scope, validations, after_save .....
end
end
module ClassMethods
def find_with_tag(tag)
# .....
end
end
end
Realization with concern
In Rails 4, you can eaily realize it by creating a module extending ActiveSupport::Concern
and include this module in your model.
module Taggable
extend ActiveSupport::Concern
included do
scope :disabled, -> { where(disabled: true) }
end
class_methods do
def find_with_tag(tag)
# ...
end
end
#put instance methods here
end
Notice
- If you want to know which class is using this module in a class method, you can simply call
self
. For example, I want to cache class methodfind
, then in my taggble module, I use
class_methods do
def some_class_method(id)
Rails.cache.fetch("#{self.name}.find(#{id})", :expires_in => 15.minutes.to_i)
end
end
- If it’s in an instance method, use
self.class
instead
def some_instance_method(id)
Rails.cache.fetch("#{self.class.name}.find(#{id})", :expires_in => 15.minutes.to_i)
end
And the code is more elegant (^m^ )クスッ.