Varying validations
As great as Rails validations are, they’ve left much to be desired for me. I find that I often want to only do partial validations, or use a different set of validations on my model based on who is creating it (admins should be trusted more than visitors) or where it is being created (creating a user on the command line or automated process shouldn’t require acceptance of terms or confirmation of a password).
I’m getting close to the point where I’m so annoyed about this problem that I’m ready to solve it, but I can’t quite figure out the best way to do it. So, I ask you, how do we remedy this?
Here are a couple ideas that I’ve had for changing how validations work.
Fatter model
My first thought is to just add it to the model by declaring “sets” of validations:
class User < ActiveRecord::Base validations :terms do validates_acceptance_of :terms, :privacy_policy end validations :confirmation do validates_confirmation_of :password end end
And then when you save a model, you can specify what sets to use.
user = User.new(params[:user]) user.valid?(:terms, :confirmation) user.save(:terms, :conditions) # or all, possibly the default user.save(:all)
I could also see this being used for a wizard like interface.
class Foo < ActiveRecord::Base validations :step1 do validates_presence_of :name, :stuff, :thing end validations :step2 do validates_presence_of :bar, :baz validates_acceptance_of :terms end validations :step3 do validates_confirmation_of :password end end
But that shouldn’t really be part of the model
You’re right, these problems really don’t concern the model. The model shouldn’t care were it is being created from, or if it’s being created in one step or five. So what about going to the opposite end of the spectrum, and completely removing validations from the model and declaring them in observer-like classes.
class TermsValidation < ActiveRecord::Validations validate :user, :supplier validates_acceptance_of :terms, :privacy_policy, :on => :create end class Step1Validation < ActiveRecord::Validations validate :foo validates_presence_of :name, :stuff, :thing end
And then in the controller:
class UsersController < ActionController::Base validate :terms def create user = User.new(params[:user]) if user.save # all the validations passed else # a validation failed, handle just like you would normally end end end
If we had an action that required different validations than the rest of the controller, we could also use similar approach to what we talked about earlier and declare what validations to use:
class UsersController < ActionController::Base validate :terms def update user = User.find(params[:id]) user.update_attributes(params[:user], :step2) end end
I like this better, because choosing what set of validations to use does seem more like a controller decision, and not model one, but it is the model’s responsibility to make sure that the data is “valid”, so how do we reconcile this?
How are you working around this problem? Is there an obvious solution that I’m just not seeing?
5 Comments
I quite like this approach and it may be good in a plugin, but I doubt rails core would accept it.
Also, validate :terms, :only => %w(create)
validate :step2, :only => %w(update)
Just like filters!
I’ve been thinking the same thing. The best solution I’ve seen so far is the one that Jay Fields is using in his Validatable library: contextual validation.
http://blog.jayfields.com/2007/04/ruby-validatable-122-released.html
He uses the keyword
:groups, but I’d prefer:context. Here’s what it could look like.See also Martin Fowler’s entry on the subject:
http://www.martinfowler.com/bliki/ContextualValidation.html
Justin: you’re probably right. At least, at first they wouldn’t. But, if there’s a good solution that is backwards compatible and it gains enough traction as a plugin, I think they would be open to it.
Jeffrey: thanks, somehow I missed Jay Field’s post. That’s exactly along the lines of what I want. I’ll give it a try and see how it goes.
Also sounds a lot like activespec from Luke Redpath
Or you can use the Validate Attributes plugin written by Sur(my colleague).
Plugin Url: http://expressica.com/plugins/validate_attributes/
Post a Comment