Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Where does Grail's errors property come from?

Grails has a bug with regards to databinding in that it throws a cast exception when you're dealing with bad numerical input. JIRA: http://jira.grails.org/browse/GRAILS-6766

To fix this I've written the following code to manually handle the numerical input on the POGO class Foo located in src/groovy

void setPrice(String priceStr)
{
    this.priceString = priceStr

    // Remove $ and , 
    priceStr = priceStr.trim().replaceAll(java.util.regex.Matcher.quoteReplacement('$'),'').replaceAll(',','')

    if (!priceStr.isDouble()) {
        errors.reject(
            'trade.price.invalidformat',
            [priceString] as Object[],
            'Price:[{0}] is an invalid price.')

        errors.rejectValue(
            'price',
            'trade.price.invalidformat')
    } else {
        this.price = priceStr.toDouble();
    }
}

The following throws a null reference exception on the errors.reject() line.

foo.price = "asdf" // throws null reference on errors.reject()
foo.validate()

However, I can say:

foo.validate()
foo.price = "asdf" // no Null exception
foo.hasErrors() // false
foo.validate()
foo.hasErrors() // true

Where does errors come from when validate() is called? Is there a way to add the errors property without calling validate() first?

like image 507
Jim Wallace Avatar asked Dec 12 '22 17:12

Jim Wallace


2 Answers

I can't exactly tell you why, but you need to call getErrors() explicitly instead of accessing it as errors like a property. For some reason, Groovy isn't calling the method for it. So change the reject lines in setPrice() to

getErrors().reject(
        'trade.price.invalidformat',
        [priceString] as Object[],
        'Price:[{0}] is an invalid price.')

getErrors().rejectValue(
        'price',
        'trade.price.invalidformat')

That is the easiest way to make sure the Errors object exists in your method. You can check out the code that adds the validation related methods to your domain class.

like image 112
doelleri Avatar answered Jan 28 '23 15:01

doelleri


The AST transformation handling @Validateable augments the class with, among other things

  • a field named errors
  • public methods getErrors, setErrors, clearErrors and hasErrors

The getErrors method lazily sets the errors field if it hasn't yet been set. So it looks like what's happening is that accesses to errors within the same class are treated as field accesses rather than Java Bean property accesses, and bypassing the lazy initialization.

So the fix appears to be to use getErrors() instead of just errors.

like image 35
Ian Roberts Avatar answered Jan 28 '23 14:01

Ian Roberts