Thursday, 24 April 2008

Consistency in custom validators

There are 4 different ways you can get custom validators to attach errors to your domain objects.
  1. Return true/false/null
  2. Return an error code String
  3. Return a List consisting of the error code and args
  4. Attach the errors yourself (only available in the 3 argument validator)
The last of these seems to be mainly useful if you may need to add multiple errors to the field but you lose some of the magic Grails does for you. For example the validators of the following class:
class Lolcat {
String cheezburger
String bukkit
String flavr
static constraints = {
cheezburger(validator: {
return false
})
bukkit(validator: {
return 'epic.fail'
})
flavr(validator: { value, target, errors ->
errors.rejectValue('flavr', 'epic.fail')
})
}
}
will attach the following error codes to an instance's fields when validate is called (the bolded value is the default returned by obj.error.code):
  • cheezburger
    • lolcat.cheezburger.validator.error.Lolcat.cheezburger
    • lolcat.cheezburger.validator.error.cheezburger
    • lolcat.cheezburger.validator.error.java.lang.String
    • lolcat.cheezburger.validator.error
    • lolcat.cheezburger.validator.invalid.Lolcat.cheezburger
    • lolcat.cheezburger.validator.invalid.cheezburger
    • lolcat.cheezburger.validator.invalid.java.lang.String
    • lolcat.cheezburger.validator.invalid
    • validator.invalid.Lolcat.cheezburger
    • validator.invalid.cheezburger
    • validator.invalid.java.lang.String
    • validator.invalid
  • bukkit
    • lolcat.bukkit.validator.error.Lolcat.bukkit
    • lolcat.bukkit.validator.error.bukkit
    • lolcat.bukkit.validator.error.java.lang.String
    • lolcat.bukkit.validator.error
    • lolcat.bukkit.epic.fail.Lolcat.bukkit
    • lolcat.bukkit.epic.fail.bukkit
    • lolcat.bukkit.epic.fail.java.lang.String
    • lolcat.bukkit.epic.fail
    • epic.fail.Lolcat.bukkit
    • epic.fail.bukkit
    • epic.fail.java.lang.String
    • epic.fail
  • flavr
    • epic.fail.Lolcat.flavr
    • epic.fail.flavr
    • epic.fail.java.lang.String
    • epic.fail
You can see that the Grails convention of adding "${class}.${field}." to the front of the error code you return is not happening for the 'flavr' validator.
In addition, on the 'cheezburger' and 'bukkit' validators Grails will automatically attach three arguments:
  1. field name
  2. class name
  3. field value
This is also lost on the 'flavr' validator.
I've created a helper method ValidationUtils.rejectValue(target, errors, fieldName, code, args) that will do all this for you which enables error codes and message arguments to be consistently standardised no matter which type of validator you're writing.

No comments: