Concerned With: Skinny Controller, Skinny Model
2:33 PM EDT Saturday, August 30 2008
A small Rails technique that I've been using lately is concerned_with. I first saw Matthew Bass use it on the => 3-2-1, but I've gleaned from a couple of articles on the web that it was invented by Rick Olsen.
The purpose of concerned_with is to break up larger models up into concerns. Let's say you have a User model, and you have several macros, includes, validations, callbacks, class methods and instance methods all related to authentication, maybe sort of like this:
class User << ActiveRecord::Base
include AuthenticationSupport
validates_presence_of :login
validates_uniqueness_of :login
before_create :set_default_password
class << self
def authenticate(login, password)
login == "root" && password == "too_many_secrets"
end
end
def set_default_password
self.password = "pa$$w0rd"
end
end
As your model grows in complexity, eventually what will happens is that you will have more validations, callbacks, class methods, instance methods, etc. They will get mixed in with the authentication ones, app/models/user.rb will grow to become 500 lines of code and it will be hard to keep track of what's what. Instead, just put this into app/models/user.rb:
class User << ActiveRecord::Base
concerned_with :authentication
end
And then throw all of that into app/models/user/authentication.rb:
class User
include AuthenticationSupport
validates_presence_of :login
validates_uniqueness_of :login
before_create :set_default_password
class << self
def authenticate(login, password)
login == "root" && password == "too_many_secrets"
end
end
def set_default_password
self.password = "pa$$w0rd"
end
end
So now your app/models/user.rb is clean and can have other stuff in there, and all the authentication related stuff gets loaded from app/model/user/authentication.rb. All you have to do to enable this is is throw this snippet into an initializer, like config/initializers/concerned_with.rb:
class << ActiveRecord::Base
def concerned_with(*concerns)
concerns.each do |concern|
require_dependency "#{name.underscore}/#{concern}"
end
end
end



