Put Your Rails models on a diet
Models are fat
Usually models are very fat. Rails conventions encourages us to put too much responsibility into the poor model. That leads us very quickly very far from Object Oriented Programming. One of the reasons is lots of validations that we usually put into the model.
There is a built in mechanism to extract validations out of the model - validates_with - but it forces you to put all the validations inside one method: validate(record). This means you lose the very handy validation helper methods.
Influenced by Marcin Kostrzewa, I conducted an experiment to get rid of this responsibility from model by extracting all validations, but keeping the very handy validation dsl.
This is how model looked before a diet:
And this is how it looks after:
All the validations are in UserValidator class:
The instance of the UserValidator is created and populated with the data from the model by Validator::Factory class:
The idea is that in the model you register a specific validator in the Validator::Factory and use built-in Rails mechanism with the general Validator:
Validator has the method validate which is responsible for building the specific validator using Validator::Factory, populating it with the data from model, invoking valid? on it and reassigning errors from the validator to the model. That’s why in the UserValidator we need to define fields method; It needs to return all fields to be validated and then attr_accessor(*fields) will define accessors for them. It’s used by Validator::Factory to populate UserValidator with the data from model.
In the end we have code which is more object oriented, follows single responsibility principle and extracts responsibility which the model should not have. There is better communication between objects; Code is built with more small objects, each having it’s own specific job to do. We have a closed Validator::Factory class, which can build any specific validator and is open for extension; We can easily add new validators. Our models are leaner so they look more attractive!