I’m pretty satisfied about how you can achieve common tasks with Rails. I’m
thinking about routing, validations, internationalization and etc. But there
is just one thing I didn’t really like. And it’s, of course, grouping
validations. I’m talking about the simple situations where you have a model
that needs to group validations per steps. Something that the awesome
DataMapper guys call contextual validations.
DataMapper provides the following great API:
Even if it’s the first time you see this DataMapper API I’m pretty sure you’d
like to have the opportunity to write something like the following:
Now, I’m not going to reproduce this feature using ActiveRecord, although it
would be nice to have that. I just want to share with you a recipe for doing
something similar to contextual validations in a nice and clean way.
To cook this recipe with need the following ingredients:
I promised myself I won’t use a blog in my examples. Unfortunately, I’m
going to use something very similar. Sorry about that.
Let’s start with the fancy example: Suppose you have a client that works with
articles. In order to publish an article, the following fancy requirements
must be meet:
A draft article should have at least one page and a title.
A article can be queued for review only if it has at least three tags and a
An article queued for publication can be actually published only it has a
long description and at least two keywords for SEO fancy people.
Now that we’ve got the business rules we can say an article can be in the
I confess I don’t like the naming very much. And yes, I know that naming is
hard but, for me, invent a reasonable example is probably more difficult.
Well, using the neat state_machine gem, we can do something like the
I have to say I really like the DSL. Basically, the event macro creates an
instance method, executing the method will transit to the proper state if any.
An example will surely explain how it works better than me:
Simple and effective. If you’re not familiar with state_machine I strongly
recommend you to take a look at the README. You’ll find out a lot of
interesting and useful features.
As we just saw, now we have an easily way to track the current status of an
article for our workflow. We just have to add the validations. Before we
start writing them, let’s take a quick look at the other ingredients of this
recipe: with_options. This class macro gives us the opportunity of writing
less verbose code. A lot of people would say that it will help you to write
DRY code. Actually, DRY does not mean “don’t type a lot”. It means:
Every piece of knowledge must have a single, unambiguous, authoritative
representation within a system.. So, I don’t like very much when people
makes their code just shorter and say “It’s DRY”.
Well, we were talking about with_options. In Rails, there are a lot of
methods that take an Hash as the last argument, and when you have pass several
methods the very same options you can use with_options to make the call
shorter. It has the nice side-effect of logically grouping calls:
OK, now we know how to use with_options and we have a nice state_machine.
The only thing we have to do now is writing the validations. We’ll proceed by
state, starting with the draft:
That’s all. The article.status?(:draft) is a nice API kindly offered by
state_machine. And now we can give it a try:
It seems to work as expected. Let’s just save it:
Then we have to add validations for the second step. We have to ensure that an
article can be queued for publication only if it has a short description and
at least three tags. The first one is very simple because we can just use the
presence validation. But validating tags requires a custom validator. Let’s
assume tags are stored in one string and are comma-separated values, a
reasonable approach could be the following:
Let’s we should give it a try:
It works as expected.
Now the last step, We can queue an article if it has a long description and at
least two keywords. Quite similar to the previous step:
Now just some quick tests in the console:
It seems to work as we wanted. We grouped validations in a nice and
readable way. Let’s see how the article model looks like now:
This technique can be a good starting point if you want to build a wizard. You
can create the next and the previous events to handle the transitions.
Regarding the controllers and the view, you can simplify your job if you use
the steps as the name of views. Actually, if you’re interested I can write
about that too.
There is a demo project if you want to play with it.