Groovy / Grails: How are constraints implemented?

In Getting Started with Grails-2e , p. 42 (an electronic page, not a paper page), the following example.

class Race { static constraints = { name(blank:false, maxSize:50) // NOTE: This doesn't do // what you think it does startDate(min: new Date()) // And this is what we're supposed // to be using: // startDate(validator: {return (it > new Date())}) } String name Date startDate // ... } 

The reader is then encouraged to use the startDate version commented above. The reason given is that the static constraints property will be evaluated only once (during server startup), while our intention is for it to evaluate each instance and the subsequent Race check.

My question is:. Why then does the non-clos style for the name constraint work for every check attempt, but not for the startDate constraint? Conversely, if startDate requires a closure flavor, then why is it not required for name ?

If I understand Groovy’s syntax above correctly, it seems that each of the constraints listed in the static constraint block is a syntax call to a function that accepts different validation attributes like Map . Now, since the static block will be evaluated (once) during server startup, both function calls will also occur (once) during server startup and should lead to identical and consistent behavior in their form without closing. Is that not so?

+7
source share
2 answers

If you follow these steps:

  startDate(min: new Date()) 

Then new Date() will be evaluated at server startup and will never change. Therefore, next week (assuming that the server will work), it will check the date last week.

Second form:

  startDate(validator: {return (it > new Date())}) 

It will be evaluated every time a constraint is checked, so it will always be checked today, no matter how long the server is running.

On the other hand, when it comes to name , it is checked for static content, namely maxSize is 50, which makes sense to have it as a pair of key values ​​instead of using a validator closure, since a value of 50 not evaluated every time the check is performed on name , how to do it for startDate .

Edit:

When a call to name( maxSize:50 ) is evaluated, it actually creates a MaxSizeConstraint object for the name field. This property->constraints map is then used by grails to check properties when an object is checked. As you can see in this class, maxSize is a private property. Indeed, if you want maxSize change over time, you need to use a special validator, as with Date

+10
source

I did some experiments, and here are some conclusions. This is all empirical research; please correct me if I am wrong.

First of all, it's easy to see when the constraints call is computed. Just add debug output:

 static constraints = { println "Entering constraints..." name(blank:false, maxSize:50) // etc. println "Exiting constraints..." } 

You will see that it is really evaluated when the application starts. For some reason, I do not understand, this is usually evaluated twice. Also note that constraints marked as static, so it has nothing to do with specific instances of class Race .

Further, it is easy to find out that name , startDate , etc., are indeed functional calls. Just try specifying restrictions on a nonexistent property:

 static constraints = { no_such_thing(nullable:true) } 

You cannot do it! Result:

 Error Error executing script Shell: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'transactionManagerPostProcessor': Initialization of bean failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'transactionManager': Cannot resolve reference to bean 'sessionFactory' while setting bean property 'sessionFactory'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'sessionFactory': Invocation of init method failed; nested exception is org.codehaus.groovy.runtime.InvokerInvocationException: groovy.lang.MissingMethodException: No signature of method: Race.no_such_thing() is applicable for argument types: (java.util.LinkedHashMap) values: [[nullable:true]] 

Of course, you never defined all of these methods, i.e. name , startDate , etc., and you have not yet inherited your domain class from anything else. But since Grails recognizes it as a domain class, it uses the power of Groovy to inject methods into an object, bypassing the limitations of traditional object-oriented programming.

Now it does not literally introduce methods into the object. You can easily check:

 static constraints = { println Race.metaClass.methods*.name.sort().unique() // You can even construct an object if you really want to mess with the framework println new Race().metaClass.methods*.name.sort().unique() } 

You will not see any methods called name , startDate , etc., and you cannot println Race.name inside the constraints { } block. I think Groovy intercepts calls to nonexistent methods Race.name , Race.startDate , etc., and writes this restriction information elsewhere for future use. If you want, try to apply methods, for example, for example. Race.name ; I think I managed to prevent the restrictions from working, but I cannot reproduce it.

Regarding the question of what is being assessed when, I think we have some confusion here about Groovy closing. Take a look at

 startDate(validator: {return (it > new Date())}) 

Here we have a closure: {return (it > new Date())} . If Groovy was a pure interpreted language such as Python, it would just save that literal code and reinterpret it with any call. This way you will also get the latest date. I assume that Groovy mimics this behavior, even if the code seems to be compiled: it will transfer this code to the closure object and will call this object every time a check is requested. This closure will be saved somewhere ; presumably in the same place where all other restrictions are stored. The confusion arises from the nature of closures: if you store 3 , it will always remain 3 ; if you store a closure (function), it can evaluate different results in different cases.

To repeat, code

 {return (it > new Date())} 

Not “running” or “evaluating” when the application starts; It is just kept for future use. You can easily check this:

 static constraints = { startDate(validator: {println "Validating startDate..."}) } 

Then run grails shell and

 groovy> (new Race()).validate() 
+1
source

All Articles