I am trying to write a generic custom validator for a property, to make it generic i need a reference to the field name within the closure, the code is as follows
In config.groovy
grails.gorm.default.constraints = {
nameShared(validator: {val, obj, errors->
Pattern p = Pattern.compile("[a-zA-Z0-9-]{1,15}")
Matcher m = p.matcher(val)
boolean b = m.matches()
if(!b)
errors.rejectValue('name', "${obj.class}.name.invalid", "Name is invalid")
})
in My domain class
class Student{
String name
static constraints = {
name(shared:'nameShared')
}
}
class Teacher{
String firstName
String lastName
static constraints = {
firstName(shared:'nameShared')
}
}
I want to use the same validator for both name and firstName, but since i am hardcoding the fieldName in the validator, it will always work for name and not firstName, so i want to know if there is anyway i can get a reference to the fieldname and make the validator generic, please help
You could use the variable propertyName to get the name of the validated property.
From the grails docs:
The Closure also has access to the name of the field that the constraint applies to using propertyName
myField validator: { val, obj -> return propertyName == "myField" }
You could wrap your validator-closure inside another function like this:
def getValidator(myValue) {
return { val, obj, errors ->
// make use of myValue
}
}
myField validator: getValidator('foo')
Related
Is there some way to mock a domain field validator?
Currently, my code in the domain class is looking like this:
isPrimary(validator: { Boolean value, Person obj ->
.......
}
And I need to mock this function.
I tried to use it like:
Person.metaClass.static.isPrimary.validator = { Boolean value, Person obj ->
.......
}
And it didn't work, any suggestions how to solve this issue ?
Here is an example:
class Person {
Boolean isPrimary
static constraints = {
isPrimary validator: isPrimaryValidator
//or this for a fully qualified validator
//isPrimary validator: Person.isPrimaryValidator
}
static isPrimaryValidator = { Boolean value, Person obj ->
//some validation
}
}
//in Test
Person.metaClass.'static'.isPrimaryValidator = { Boolean value, Person obj ->
//Do something else
}
I have a Command Object as follows :
class TestCreationCommand {
String title
List testItems = [].withLazyDefault {new VocabQuestion()}
static constraints = {
title(blank:false,maxLength:150)
testItems( validator: { ti ->
ti.each {it.validate()}
} )
}
}
Test Items is a list of VocabQuestion objects. VocabQuestion is as follows :
class VocabQuestion {
static constraints = {
question(blank:false,maxLength:150)
answer(blank:false,maxLength:40)
}
static belongsTo = [vocabularyTest: VocabularyTest]
String question
String answer
}
I'm attempting to validate the constraints on the VocabQuestion using a custom vaildator ( in the constraints of the Command class above ), but I keep getting the following error message.
Return value from validation closure [validator] of property [testItems] of class [class vocabularytest.TestCreationCommand] is returning a list but the first element must be a string containing the error message code
I have had many different attempts at this .....
I am not sure what the message is telling me or how to go about debugging what the return value from the closure is.
Can anyone provide me any pointers?
You are not returning what Grails understands. You can't expect to just return anything and have Grails know what to do with it. Return a boolean or an error string.
static constraints = {
title(blank:false,maxLength:150)
testItems( validator: { ti ->
Boolean errorExists = false
ti.each {
if (!it.validate()) {
errorExists = true
}
}
errorExists
})
}
Check out this SO question It might be the format of the validator you need.
The .every will return a boolean.
I am trying to find a way to add dynamic fields to a grails domain class. I did find the dynamic domain class plugin based on Burt's article, but this is way too much for our needs.
Supposed we have a domain class of person:
class Person extends DynamicExtendableDomainObject {
String firstName
String lastName
static constraints = {
firstName(nullable: false, blank: false, maxSize: 50)
lastName(nullable: false, blank: false)
}
}
Now customer a wants to also have a birthdate field in this. By using some sort of management tool, he adds this extra field in the database.
Customer b wants to also have a field middle name, so he is adding the field middle name to the person.
Now we implemented a DynamicExtendableDomainObject class, which the Person class inherits from. This adds a custom field to each Domain class inheriting from this to store the dynamic properties as JSON in it (kind of like KiokuDB in Perl stores them).
Now when Person is instantiated, we would like to add those dynamic properties to the Person class, to be able to use the standard Grails getter and setter as well as Templating functions for those.
So on customer a we could use the scaffolding and person would output firstName, lastName, birthDate, on customer b the scaffolding would output firstName, lastName, middleName.
The storing of the properties will be implemented by using the saveinterceptor, to serialize those properties to JSON and store them in the special field.
But we have not yet found a way to add these JSON properties dynamically to the domain class during runtime. Is there a good way to handle this? And if so, how to best implement this?
You can try to add the properties at runtime to the DomainClass of type DynamicExtendableDomainObject by expanding getProperty(), setProperty(), setProperties() in the metaClass and then use beforeUpdate(), beforeInsert() and afterLoad() to hook into Persistence.
For example in Bootstrap (or service):
def yourDynamicFieldDefinitionService
for(GrailsClass c in grailsApplication.getDomainClasses()){
if(DynamicExtendableDomainObject.isAssignableFrom(c.clazz)){
Set extendedFields = yourDynamicFieldDefinitionService.getFieldsFor(c.clazz)
//getProperty()
c.clazz.metaClass.getProperty = { String propertyName ->
def result
if(extendedFields.contains(propertyName)){
result = delegate.getExtendedField(propertyName)
} else {
def metaProperty = c.clazz.metaClass.getMetaProperty(propertyName)
if(metaProperty) result = metaProperty.getProperty(delegate)
}
result
}
//setProperty()
c.clazz.metaClass.setProperty = { propertyName , propertyValue ->
if(extendedFields.contains(propertyName)){
delegate.setExtendedField(propertyName, propertyValue)
delegate.blobVersionNumber += 1
} else {
def metaProperty = c.clazz.metaClass.getMetaProperty(propertyName)
if(metaProperty) metaProperty.setProperty(delegate, propertyValue)
}
}
//setProperties()
def origSetProperties = c.clazz.metaClass.getMetaMethod('setProperties',List)
c.clazz.metaClass.setProperties = { def properties ->
for(String fieldName in extendedFields){
if(properties."${fieldName}"){
delegate."${fieldName}" = properties."${fieldName}"
}
}
origSetProperties.invoke(delegate,properties)
}
}
}
with
abstract DynamicExtendableDomainObject {
String yourBlobField
Long blobVersionNumber //field to signal hibernate that the instance is 'dirty'
Object getExtendedField(String fieldName){
...
}
void setExtendedField(String fieldName, Object value){
...
}
def afterLoad(){
//fill your transient storage to support getExtendedField + setExtendedField
}
def beforeUpdate(){
//serialize your transient storage to yourBlobField
}
def beforeInsert(){
//serialize your transient storage to yourBlobField
}
}
I added a custom validator in domain class for a property. But whenever I run the unit test and run validate() method I get an error message that the property could not be recognized in the class. when I remove my custom validator everything is working properly.
Thanks your your help!
class Region {
int identifier
BigDecimal leftUpLatitude
BigDecimal leftUpLongitude
BigDecimal rigthDownLatitude
BigDecimal rigthDownLongitude
static constraints = {
identifier unique:true, validator: {return identifier > 0}
leftUpLatitude min:-90.00, max:90.00
leftUpLongitude min:-180.00, max:180.00
rigthDownLatitude min:-90.00, max:90.00
rigthDownLongitude min:-180.00, max:180.00
}
boolean isValidRegion(){
if ((leftUpLatitude > rigthDownLatitude) && ( leftUpLongitude < rigthDownLongitude))
return true
else
return false
}
String toString(){
return "${identifier}"
}
}
Accessing the objects properties in a custom validator is a little bit different than just referencing the property. The validator closure takes one or two parameters, 1) the current property's value and 2) the object itself, if you need access to the rest of the object.
validator: { val -> val > 0 }
I have a property that can be nullable or required depending on the status of another variable.
class Person{
name()
civilStatus(inList:['Single','Married','Divorced','Widowed'])
partnerOrSpouse()
}
the partnerOrSpouse property is nullable or not depending on the value of the civilStatus property.
You can use a custom validator. Using the two-parameter version, the first is the value being validated and the second is the domain class instance. You can refer to other properties via the 'obj' parameter:
class Person {
...
static constraints = {
name()
civilStatus inList:['Single','Married','Divorced','Widowed']
partnerOrSpouse validator: { val, obj ->
if (obj.civilStatus == 'Single') {
return 'some.error.code'
}
}
}
}