In a project I’m working on we have chemicals that belongs to a department and a department has many chemicals:
# app/models/chemical.rb
class Chemical < ActiveRecord::Base
belongs_to :department
end
# app/models/department.rb
class Department < ActiveRecord::Base
has_many :chemicals
end
In our project we use named scope extensively. This is an example:
# app/models/chemical.rb
class Chemical < ActiveRecord::Base
belongs_to :department
named_scope :belongs_to_department, lambda { |department| { :conditions => { :department_id => department } } }
end
With this scope all chemicals that belongs to a department can be fetched with:
Chemical.belongs_to_departments(department)
But, chemicals was far from the only model belonging to a department. People for example, were this scope also came in handy.
# app/models/person.rb
class Person < ActiveRecord::Base
belongs_to :department
named_scope :belongs_to_department, lambda { |department| { :conditions => { :department_id => department } } }
end
Works fine, but now that same code is in two models. This way of doing things is bad and we need to find a way to refactor this in order to make our code DRY. So the scope needs to be shared through all models that belongs to a department (and needs that scope), and that with our code still DRY.
This is usually achived by including a module in the models, as it is in this case. But since sharing a named scope is very common, I want to have them nicely structured.
So in lib, I have a folder named named_scope. And in that folder, all modules with common named scopes are. To get extra nice structure I create a module called NamedScope:
# lib/named_scopes/named_scope.rb
module NamedScope
end
Now I create the module that will hold the common department scope. And that can be done using self.included:
# lib/named_scopes/department.rb
module NamedScope::Departments
def self.included(base)
base.class_eval do
named_scope :belongs_to_departments, lambda { |departments| { :conditions => { :department_id => departments } } }
end
end
end
So now in my models I can include that module:
# app/models/chemical.rb
class Chemical < ActiveRecord::Base
include NamedScope::Departments
belongs_to :department
end
# app/models/person.rb
class Person < ActiveRecord::Base
include NamedScope::Departments
belongs_to :department
end
Now I can, with a DRY code and a nice structure, do this on both models:
Chemical.belongs_to_department(department)
Person.belongs_to_department(department)