Extending Active Record to reduce redundancy
Suppose I have two classes in a Rails application:
class Subject < ActiveRecord::Base
def children?
Subject.where(:parent_id => self.id).length > 0
end
def children
Subject.where(:parent_id => self.id)
end
end
class Region < ActiveRecord::Base
def children?
Region.where(:parent_id => self.id).length > 0
end
def children
Region.where(:parent_id => self.id)
end
end
What would be the best way to reduce the redundant class methods? Would I extend ActiveRecord with two new methods? If so, how could I wri开发者_JAVA百科te those two new methods to be available for both classes?
Thanks, Mike
Actually what are you dealing with is has_many
association.
DRY
principle is very good one, but not for this case. You want to extract very simple and native stuff out off model, while it will complicate main picture.
So you can just refactor a little
class Subject < ActiveRecord::Base
has_many :children, :class_name => "Subject", :foreign_key => :parent_id
def children?
children.present?
end
end
Have a look at acts_as_tree to see what it does or use it as it looks like you are trying to perform the same tasks.
if you are dealing with small objects a quick fix is to create 2 generic methods in your application helper file:
def children?(myObject)
myObject.where(:parent_id => myObject.id).length > 0
end
def children(myObject)
myObject.where(:parent_id => myObject.id)
end
Edit: for anything more resource intensive you can define a method in ActiveRecord::Base since they both inherit from it.
def children?
self.where(:parent_id => self.id).length > 0
end
def children
self.where(:parent_id => self.id)
end
I agree with @abdollar's suggestion for using acts_as_tree. Or you could just create an association onto the same table (which is what acts_as_tree does).
If you wanted to roll your own using those methods you've given as examples, you could create a module in lib which you can require and include in your models...
# lib/children.rb
module Children
def children
self.class.where(:parent_id => self.id)
end
def children?
children.present?
end
end
# app/models/subject.rb (or any other model)
require 'children'
class Subject < ActiveRecord::Base
include Children
end
If you're using rails 2, you won't need to do require 'children'
since lib is autoloaded. If you're using rails 3, you could put that into an initializer or something to clean it up.
精彩评论