Skip to content

Commit d5671c3

Browse files
authored
Merge pull request #19 from rmorrise/vdog-merge
Merged in virtualdogbert's changes from grails consortium; tested with grails 3.3.4
2 parents 11cd68c + 7ae8a55 commit d5671c3

File tree

7 files changed

+162
-86
lines changed

7 files changed

+162
-86
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ repositories {
1313
1414
dependencies {
1515
//CSC custom plugin for 'cascade' constraint
16-
compile ":cascade-validation:3.0.0"
16+
compile "org.grails.plugins:cascade-validation:3.0.1"
1717
}
1818
```
1919

build.gradle

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,16 @@ buildscript {
77
classpath "org.grails:grails-gradle-plugin:$grailsVersion"
88
}
99
}
10-
version "3.0.0"
10+
version "3.0.1"
1111
group "org.grails.plugins"
1212

1313
apply plugin: 'eclipse'
1414
apply plugin: 'idea'
1515
apply plugin: "org.grails.grails-plugin"
1616
apply plugin: "org.grails.grails-plugin-publish"
1717

18-
sourceCompatibility = 1.7
19-
targetCompatibility = 1.7
18+
sourceCompatibility = 1.8
19+
targetCompatibility = 1.8
2020

2121
repositories {
2222
mavenLocal()
@@ -35,6 +35,8 @@ dependencies {
3535
testCompile "cglib:cglib-nodep:3.2.5"
3636
testCompile "org.grails:grails-gorm-testing-support"
3737
testCompile "org.grails:grails-plugin-testing"
38+
testCompile "org.grails:grails-web-testing-support"
39+
testCompile 'net.bytebuddy:byte-buddy-dep:1.8.12'
3840
}
3941

4042
bootRun {

grails-wrapper.jar

5.33 KB
Binary file not shown.

src/main/groovy/com/cscinfo/platform/constraint/CascadeValidationConstraint.groovy renamed to src/main/groovy/com/cscinfo/platform/constraint/CascadeConstraint.groovy

Lines changed: 42 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package com.cscinfo.platform.constraint
22

3-
import org.grails.datastore.gorm.validation.constraints.AbstractVetoingConstraint
3+
import groovy.transform.CompileDynamic
4+
import groovy.transform.CompileStatic
5+
import org.grails.datastore.gorm.validation.constraints.AbstractConstraint
46
import org.springframework.context.MessageSource
57
import org.springframework.validation.Errors
68
import org.springframework.validation.FieldError
@@ -16,67 +18,84 @@ import org.springframework.validation.FieldError
1618
* @author Eric Kelm
1719
* @author Russell Morrisey
1820
*/
19-
class CascadeValidationConstraint extends AbstractVetoingConstraint {
20-
public static final String NAME = "cascadeValidate"
21+
@CompileStatic
22+
class CascadeConstraint extends AbstractConstraint {
23+
static final String CASCADE_CONSTRAINT = "cascade"
2124

22-
private final boolean cascade
23-
24-
CascadeValidationConstraint(Class<?> constraintOwningClass, String constraintPropertyName, Object constraintParameter, MessageSource messageSource) {
25+
CascadeConstraint(Class<?> constraintOwningClass, String constraintPropertyName, Object constraintParameter, MessageSource messageSource) {
2526
super(constraintOwningClass, constraintPropertyName, constraintParameter, messageSource)
26-
this.cascade = (Boolean) constraintParameter
27+
28+
if (!(constraintParameter instanceof Boolean)) {
29+
throw new IllegalArgumentException("Parameter for constraint [$CASCADE_CONSTRAINT] of property [$constraintPropertyName] of class [$constraintOwningClass] must be a boolean")
30+
}
2731
}
2832

29-
String getName() { NAME }
33+
boolean supports(Class type) {
34+
Collection.isAssignableFrom(type) || type.metaClass.respondsTo(type, 'validate')
35+
}
36+
37+
String getName() {
38+
return CASCADE_CONSTRAINT
39+
}
40+
41+
protected void processValidate(Object target, Object propertyValue, Errors errors) {
3042

31-
@Override
32-
protected boolean processValidateWithVetoing(target, propertyValue, Errors errors) {
3343
boolean result = false
3444

3545
if (propertyValue instanceof Collection) {
3646
propertyValue.eachWithIndex { item, pvIdx ->
37-
result = validateValue(target, item, errors, pvIdx) || result
47+
validateValue(target, item, errors, pvIdx) || result
3848
}
3949
} else {
40-
result = validateValue(target, propertyValue, errors)
50+
validateValue(target, propertyValue, errors)
4151
}
42-
43-
return result
4452
}
4553

46-
private boolean validateValue(target, value, errors, index = null) {
54+
/**
55+
* Processes the validation of the propertyValue, against the checks patterns set, and setting and calling rejectValue
56+
* if the propertyValue matches any of the patterns in the checks list.
57+
*
58+
* @param target The target field to verify.
59+
* @param propertyValue the property value of the field.
60+
* @param errors Errors to be sent by rejectValues,.
61+
*/
62+
@CompileDynamic
63+
private void validateValue(target, value, errors, index = null) {
4764
if (!value.respondsTo('validate')) {
4865
throw new NoSuchMethodException("Error validating field [${constraintPropertyName}]. Unable to apply 'cascade' constraint on [${value.class}] because the object does not have a validate() method. If the object is a command object, you may need to add the @Validateable annotation to the class definition.")
4966
}
5067

68+
if (!getParameter()) {
69+
return
70+
}
71+
5172
if (value.validate()) {
52-
return false
73+
return
5374
}
5475

5576
String objectName = target.errors.objectName
5677
Errors childErrors = value.errors
5778
List<FieldError> childFieldErrors = childErrors.fieldErrors
79+
5880
childFieldErrors.each { FieldError childFieldError ->
5981
String field
60-
if(index != null) {
82+
83+
if (index != null) {
6184
field = "${propertyName}.${index}.${childFieldError.field}"
6285
} else {
6386
field = "${propertyName}.${childFieldError.field}"
6487
}
88+
6589
FieldError fieldError = new FieldError(objectName, field, childFieldError.rejectedValue, childFieldError.bindingFailure, childFieldError.codes, childFieldError.arguments, childFieldError.defaultMessage)
6690
errors.addError(fieldError)
6791
}
68-
return true
69-
}
70-
71-
boolean supports(Class type) {
72-
Collection.isAssignableFrom(type) || type.metaClass.respondsTo(type, 'validate')
7392
}
7493

7594
@Override
7695
protected Object validateParameter(Object constraintParameter) {
7796
if (!(constraintParameter instanceof Boolean)) {
7897
throw new IllegalArgumentException("Parameter for constraint [" +
79-
NAME+ "] of property [" +
98+
CASCADE_CONSTRAINT + "] of property [" +
8099
constraintPropertyName + "] of class [" + constraintOwningClass +
81100
"] must be a boolean value")
82101
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package grails.cascade.validation
2+
3+
import com.cscinfo.platform.constraint.CascadeConstraint
4+
import grails.plugins.Plugin
5+
import org.grails.datastore.gorm.validation.constraints.factory.DefaultConstraintFactory
6+
import org.grails.datastore.gorm.validation.constraints.eval.ConstraintsEvaluator
7+
import org.grails.datastore.gorm.validation.constraints.eval.DefaultConstraintEvaluator
8+
import org.grails.datastore.gorm.validation.constraints.registry.ConstraintRegistry
9+
import org.springframework.context.ApplicationContext
10+
11+
class CascadeValidationGrailsPlugin extends Plugin {
12+
13+
def grailsVersion = "3.3.0 > *"
14+
def title = "Cascade Validation Plugin"
15+
def author = "Russell Morrisey"
16+
def authorEmail = "rmorrise@cscinfo.com"
17+
def description = '''\
18+
Establishes a 'cascade' constraint property for validateable objects. If "cascade:true" is set
19+
on a nested object, the nested object's validate() method will be invoked and the results will
20+
be reported as part of the parent object's validation.
21+
22+
Based on a blog post by Eric Kelm:
23+
http://asoftwareguy.com/2013/07/01/grails-cascade-validation-for-pogos/
24+
Used with permission.
25+
'''
26+
def documentation = "https://github.com/rmorrise/grails-cascade-validation/wiki/How-to-use-cascade-validation"
27+
def license = "APACHE"
28+
def organization = [name: "CSC", url: "http://www.cscglobal.com/"]
29+
def issueManagement = [system: 'GITHUB', url: 'https://github.com/rmorrise/grails-cascade-validation/issues']
30+
def scm = [url: 'https://github.com/rmorrise/grails-cascade-validation']
31+
32+
def developers = [
33+
[name: "Burt Beckwith"],
34+
[name: "Soeren Glasius", email: "soeren@glasius.dk"],
35+
[name: "Eric Kelm"],
36+
[name: "Russell Morrisey", email: "russell.morrisey@cscglobal.com"],
37+
[name: "Christian Oestreich"],
38+
[name: "Virtualdogbert"]
39+
]
40+
41+
void doWithApplicationContext() {
42+
registerCustomConstraints(applicationContext)
43+
}
44+
45+
private void registerCustomConstraints(ApplicationContext ctx) {
46+
//This method for registering constraints came from longwa
47+
List<ConstraintRegistry> registries = []
48+
DefaultConstraintEvaluator evaluator = ctx.getBean(ConstraintsEvaluator) as DefaultConstraintEvaluator
49+
50+
// Register with both the default constraint as well as the gorm registry (it's stupid that it needs both)
51+
// Also the ConstraintsEvaluator evaluator constructs a new internal registry and doesn't seem to expose it
52+
// so we are forced to invade it's privacy if we want custom constraints for Validateable instances.
53+
registries << evaluator.constraintRegistry
54+
registries << ctx.getBean("gormValidatorRegistry", ConstraintRegistry)
55+
56+
registries.each { ConstraintRegistry registry ->
57+
registry.addConstraint(CascadeConstraint)
58+
}
59+
}
60+
61+
}

src/main/groovy/grails/cascade/validation/GrailsCascadeValidationGrailsPlugin.groovy

Lines changed: 0 additions & 34 deletions
This file was deleted.

0 commit comments

Comments
 (0)