This was a minor release that removed custom validation assertion syntax. (http://blog.jayfields.com/2007/09/ruby-creating-custom-assertions.html)
You can replace custom validation assertions with the newly introduced validate_only instance method. The validate_only method is my latest attempt to make it easy to test model validations one at a time.
The following code should provide a decent example.
include Validatable
validates_presence_of :name, :address
attr_accessor :name, :address
end
unit_tests do
test "individual validation for name is executed" do
instance = SampleModel.new
instance.validate_only("presence_of/name")
assert_equal "can't be empty", instance.errors.on(:name)
end
test "individual validation for address is not executed" do
instance = SampleModel.new
instance.validate_only("presence_of/name")
assert_equal nil, instance.errors.on(:address)
end
end
The validate_only method accepts an argument that is a DSL for running a single validation. The format of the DSL is "[validation type]/[key or attribute]". If the validation has a key set you should provide the key, otherwise the attribute will suffice. The documentation provides the following examples.
validates_presence_of :name
can be run withobj.validate_only("presence_of/name")
validates_presence_of :birthday, :key => "a key"
can be run withobj.validate_only("presence_of/a key")
Jay, have you thought about adding support for contextual-validations?
ReplyDeleteI do something like:
validates_prescense_of :account_number, :on => :save
validates_presence_of :login
That way I can do something like:
u =User.new(:login => 'bob')
u.valid? => true
u.valid?(:save) => false
The point is that I often use models in multiple roles, not just within the web-app, so it makes sense that the business rules for an import script to pull legacy users from a CSV would have different business rules than the user edit form on an admin page.
So... my whole point is, if you're open to it, I'd love to ditch my own validation code in DataMapper and go with yours if you were open to the idea of adding context for validations (beyond Rails' limited :create and :update contexts).
Sam,
ReplyDeleteValidatable already has support for contextual-validations.
What you want to do can be accomplished using groups.
This entry contains an example of how to use groups.
Using your example, you could write:
class User
validates_presence_of :login
validates_presence_of :account_number, :groups => :save
end
u = User.new(:login => 'bob')
u.valid? # => true
u.valid_for_saving? # => false
I'm definitely open to improving the contextual validations if you find them inadequate.
Cheers, Jay
Jay, that's pretty cool. Any way I can convince you to drop by #datamapper on IRC sometime and we can discuss this a little more? I'd love to rip out my own validations and insert a dependency on your gem instead.
ReplyDeleteOne last concern: I'm using a String#t no-op to allow for translated versions if you implement String#translate. Would you consider adding support for the same? It's such a small coding challenge, I really want to make it easy for international users to take advantage of DM without having to hack away at the internals.
Feel free to shoot me an email or IM if you're open to any of this. :)
Hey,
ReplyDeleteNow if I need to verify validates_presence_of on an attribute using rspec, I need to say something like:
@email.validate_only(
"presence_of/body") @email.errors.on(:body).
should_not be_nil
Do you think it's valueable to add some methods like has_error_on into Validatable so that I can say something like
@email.should have_error_on(:body)
Thanks
Hey Yi,
ReplyDeleteI'm not sure.
@email.should have_error_on(:body)
reads nicely; however, it only makes sense in the context of RSpec
@email.has_error_on?(:body)
reads nicely in general; however it causes the RSpec version to become
@email.should has_error_on?(:body)
which is not so nice.
I'm leaning towards has_error_on?
Thoughts?
Hey Jay,
ReplyDeleteThanks for the reply. If you have method has_something on obj, you can say obj.should have_something in Rspec. It's a built-in feature of Rspec. so if you put has_error_on, in Rspec I can say email.should have_error_on, which is perfect.
Actually, thinking twice, maybe it's not a good idea indeed. Because I can say
ReplyDeleteactive_record_model.should
have(3).errors_on(:attribute)
This statement will do valid? and check the error for you.
So maybe a better way is to make a plugin connecting Rspec to Valitable so the same syntax can be applied on a validatable object as well. I will make this happen in my project. thanks
I did wrote an extension so that I can say:
ReplyDeleteemail.should
have(1).error_on_presence_of(:body)
it looks pretty neat.
The only thing is that I still cannot say:
email.should
have(:no).errors_on_presence_of(:attachment)
because validate_only('presence_of/attachment')
will raise an ArgumentError if I don't declare
validates_presence_of :attachment.
Validatable is here for anyone looking for it:
ReplyDeletehttp://validatable.rubyforge.org/