While working on Ignitious, I wrote some code that looked something like this:
class Commit < ActiveRecord::Base
has_many :files, :class_name => 'CommitFile'
has_many :added_files, :class_name => 'CommitFile', :conditions => {:state => 'added'}
has_many :removed_files, :class_name => 'CommitFile', :conditions => {:state => 'removed'}
has_many :modified_files, :class_name => 'CommitFile', :conditions => {:state => 'modified'}
end
class CommitFile < ActiveRecord::Base
belongs_to :commit
end
'Created new records like this:'
Commit.files.create(:name => 'foo', :state => 'added')
Then I realized, using named scopes, I could move all state logic into the CommitFile class like so:
class Commit < ActiveRecord::Base
has_many :files, :class_name => 'CommitFile'
end
class CommitFile < ActiveRecord::Base
belongs_to :commit
named_scope :added, :conditions => {:state => 'added'}, :order => 'filename'
named_scope :removed, :conditions => {:state => 'removed'}, :order => 'filename'
named_scope :modified, :conditions => {:state => 'modified'}, :order => 'filename'
end
'Create new records like this:'
Commit.files.added.new(:name => 'foo')
That’s it! The Commit class no longer cares about the implementation of the status logic. It’s a subtle change, but one that I think will help with future refactorings.






3 Comments
Nice illustration for the difference between the two approaches: has_many and named_scope.
Also it’s probably more efficient in that the named scope is not adding a bunch of methods and associations that the has_many would. You could probably even add a default scope in there and get rid of all those order statements.
Never thought about adding a default scope, great idea. Thanks!