You are given the task of validating that no two users register with the same name.
In ActiveRecord it can be implemented like this:
validates_uniqueness_of :name
You may now add an index to your database and consider your task done, but it’s also true that you can improve the user experience by using a better error message:
validates_uniqueness_of :name, :message => "The name {{value}} is taken. <a href="/login">Log in</a> instead?"
Did you see the use of {{value}} in there? That’s because ActiveRecord’s DSL for validations operates on a class level, and that means that when you are declaring the validation, you don’t have access to the instance you will be dealing with. Note also that there’s HTML in the error message. It’s nice to point the user in the right direction, and here it only makes sense to send the user to the login page. But HTML in the models is wrong in many levels, not to mention that the URL helpers are not available there.
It would make much more sense to declare the validations at the instance level, something ActiveRecord allows with the validate method. If we had validates_uniqueness_of as an instance method, this would be possible:
def validate
validates_uniqueness_of :name, :message => "The name #{name} is taken. <a href="/login">Log in</a> instead?"
end
A detail here is that we can build the message with Ruby interpolations instead of the {{value}} macro. The HTML is still there, but I will deal with that in another post.
Code smells in redundant DSLs
Another example of why a class level DSL for validations is a bad code smell:
validates_presence_of :card_number, :if => :paid_with_card? def paid_with_card? payment_type == "card" end
What’s wrong here? The validations DSL is reimplementing Ruby functionality, but not because it’s an improvement but because it’s operating on the wrong scope. Returning to the instance level approach, this is how it would look:
def validate validates_presence_of :card_number if paid_with_card? end
There are two other examples in the Rails guides, which I reproduce here:
validates_presence_of :surname, :if => "name.nil?"
As you may know, this evaluates the string within the instance. Needless to say, it’s another case where the DSL reinvents the wheel for no good reason. The counter example:
def validate validates_presence_of :surname if name.nil? end
And now the proc approach:
validates_confirmation_of :password,
:unless => Proc.new { |a| a.password.blank? }
The counter example:
def validate validates_confirmation_of :password unless password.blank? end
As you can see, these solutions are shorter and make the DSL look superfluous. It means that the DSL is unnecessary.
When you count the lines of code of your models, you should take into account how much code you are injecting. Your models could be hundreds of lines shorter if irrelevant DSLs like this one were removed.

{ 1 trackback }
{ 13 comments… read them below or add one }
You’re right when you know you’re right.
How much effort would it take to refactor all rails code to put validations on the instance scope? Should be doable. Maybe still having wrappers with deprecation warnings supporting the class ones.
I was always finding ActiveRecord validation rules a bit dirty. If you are doing a simple thing, they work great. But once you need more advanced validation, things are becoming dirty and in the end I was writing my own validation methods.
I think it is not all black and white after all.
FWIW, this is how Sequel validations operate with the built-in validation_helpers plugin.
There are many times I have wanted access to these validation methods inside an instance. This would provide more flexibility in how validations work in multi-step forms and such. It would also make testing easier. Have you considered opening up a discussion on rails-core list about this?
On a similar note, when I was designing CanCan, initially I was trying to force the DSL to exist at the class level. This kept getting uglier with “:if” options and instance_eval. Placing it at the instance level made the code much simpler and improved the power and flexibility.
What are the downsides of an implementation this way? I can only think of the amount of refactoring required in the Rails source. Is anyone familiar with the code enough to comment?
Good post. What if there was a class level block that implemented your idea (realizing the irony of suggesting yet another DSL…):
class Person< ActiveRecord::Base
validates do
presence_of :surname if name.nil?
# etc...
end
end
To define validation at the instance level instead of using the current DSL sound nice, however I see one small downside with the manner you specify the validations: when you use personalized error messages with “#{…}” interpolations, each time your validate() method is called, all your message strings will be interpolated before the actual validation is checked to know whether or not you need them.
With the DSL, the strings are only interpolated in the validator body when the validation fails.
great post. great idea. this should really be discussed at the rails-core list.
I don’t really see what the big deal is. If you want instance-level validations, why not just re-implement the class methods so that they work by defining instance methods? It seems more verbose to do it your way, and define a validate instance method if you don’t need one.
An unhealthy obsession with DSLs is a major gripe for me with rails. Nearly all code is for developers only, to whom code path clarity is more important english readability.
@Frederic: even if the difference in performance is tiny, you are right that interpolating before the check is worse than no interpolation at all. That’s why I prefer to return error codes instead of messages, and define the proper messages in the views. Check http://ohm.keyvalue.org/, the section about Errors.
@Clay Shentrup: if the instance level validations seem more verbose, consider the verbosity of the DSL versus plain Ruby, then consider the amount of code needed to translate the class level DSL into the instance level Ruby code that does the actual work.
Hi –
I wrote a Singleton Validations plugin a while back. Like most things I write, no one knows it exists and no one uses it. (I do announce things but I don’t seem to have the magic touch for getting noticed :-)
It may not be an exact fit for what people are describing here, but it might be of interest. However, it doesn’t work with Rails 3, and I haven’t updated it.
http://www.risleydale.net/svn/plugins/singleton_validations
David