Skip to content

Commit c92e81d

Browse files
authored
Merge pull request #13 from gpc/chore/rename-classes
refactor: Rename classes Cascade* to Cascaded*
2 parents 375ca57 + b60de0a commit c92e81d

File tree

10 files changed

+115
-40
lines changed

10 files changed

+115
-40
lines changed

build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,3 +66,4 @@ tasks.withType(Test).configureEach {
6666
}
6767

6868
apply from: layout.projectDirectory.file('gradle/docs-config.gradle')
69+
apply from: layout.projectDirectory.file('gradle/post-publish.gradle')

cascade-validation-example/src/test/groovy/cascade/PersonCascadeSpec.groovy

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
package cascade
22

33

4-
import grails.cascade.validation.internal.CascadeConstraintRegistration
4+
import grails.cascade.validation.internal.CascadedConstraintRegistration
55
import grails.testing.gorm.DataTest
6+
import grails.util.Holders
67
import spock.lang.Specification
78

89
class PersonCascadeSpec extends Specification implements DataTest {
@@ -11,8 +12,10 @@ class PersonCascadeSpec extends Specification implements DataTest {
1112
mockDomains(Person, PhoneNumber, TelephoneType)
1213
// Important in unit-tests: Register the CascadeConstraints
1314
// tag::initialize[]
14-
CascadeConstraintRegistration.register(applicationContext)
15+
CascadedConstraintRegistration.register(applicationContext)
1516
// end::initialize[]
17+
// Default
18+
Holders.config.setAt('constraints.cascaded.legacy', false)
1619
}
1720

1821
void "cascade validation on Person with empty phoneNumbers"() {
@@ -38,6 +41,25 @@ class PersonCascadeSpec extends Specification implements DataTest {
3841
person.errors.getFieldError('phoneNumbers[0].telephoneType').code == 'nullable'
3942
}
4043

44+
void "cascade validation on Person with non-valid phoneNumber in phoneNumbers (legacy)"() {
45+
given:
46+
Holders.config.setAt('constraints.cascaded.legacy', true)
47+
48+
and:
49+
Person person = new Person(firstName: 'John', lastName: 'Doe').tap {
50+
addToPhoneNumbers(countryCode: '+1', areaCode: '800', number: '555-2345')
51+
}
52+
53+
expect: 'that the validation fails due to errors in the phone-number'
54+
!person.validate()
55+
56+
and: 'there is one error in the phone-number'
57+
person.errors.errorCount == 2 // Also has the Gorm cascade validation errors
58+
59+
and: 'that error is on the first phone-number telephone-type'
60+
person.errors.getFieldError('phoneNumbers.0.telephoneType').code == 'nullable'
61+
}
62+
4163
void "cascade validation on Person with multiple non-valid phoneNumbers in phoneNumbers"() {
4264
given:
4365
Person person = new Person(firstName: 'John', lastName: 'Doe').tap {
@@ -49,12 +71,34 @@ class PersonCascadeSpec extends Specification implements DataTest {
4971
!person.validate()
5072
5173
and: 'there are two error in the list of phone numbers'
52-
person.errors.errorCount == 2
74+
person.errors.errorCount == 3 // Due to GORM cascade validations
5375
5476
and: 'that error is on the first phone number telephone type'
5577
person.errors.getFieldError('phoneNumbers[0].telephoneType').code == 'nullable'
5678
5779
and: 'the error on the second phone number is cascading to telephoneType'
5880
person.errors.getFieldError('phoneNumbers[1].telephoneType.countryCodeRecommended').code == 'nullable'
81+
}
82+
83+
void "cascade validation on Person with multiple non-valid phoneNumbers in phoneNumbers (legacy)"() {
84+
given:
85+
Holders.config.setAt('constraints.cascaded.legacy', true)
86+
and:
87+
Person person = new Person(firstName: 'John', lastName: 'Doe').tap {
88+
addToPhoneNumbers(countryCode: '+1', areaCode: '800', number: '555-2345')
89+
addToPhoneNumbers(countryCode: '+1', areaCode: '800', number: '555-1234', telephoneType: new TelephoneType())
90+
}
91+
92+
expect: 'that the validation fails due to errors in the phone-number'
93+
!person.validate()
94+
95+
and: 'there are two error in the list of phone numbers'
96+
person.errors.errorCount == 5 // Due to GORM cascade validations
97+
98+
and: 'that error is on the first phone number telephone type'
99+
person.errors.getFieldError('phoneNumbers.0.telephoneType').code == 'nullable'
100+
101+
and: 'the error on the second phone number is cascading to telephoneType'
102+
person.errors.getFieldError('phoneNumbers.1.telephoneType.countryCodeRecommended').code == 'nullable'
59103
}
60104
}

gradle/post-publish.gradle

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
tasks.register('printPublicationInfo') {
2+
group = 'publishing'
3+
doLast {
4+
publishing.publications.each { pub ->
5+
logger.lifecycle "📦 ${pub.name}: ${pub.groupId}:${pub.artifactId}:${pub.version}"
6+
}
7+
}
8+
}
9+
10+
tasks.withType(AbstractPublishToMaven).configureEach {
11+
finalizedBy(tasks.named('printPublicationInfo'))
12+
}

src/main/asciidoc/1. introduction.adoc

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,21 @@
33
The Cascade Validation Plugin provides a small, focused extension to Grails constraints that makes it easy to validate associated objects (domain associations or command object) as part of normal validation.
44
Instead of manually calling validate() on every association, the plugin supplies a constraint (see usage) that marks an association to be validated automatically.
55

6-
== Breaking change.
6+
== Breaking changes.
77

88
Due to a change in Hibernate, there is a name clash with the `cascade` constraint keyword.
99
For this reason `cascade` was renamed to `cascaded`.
1010
Please update all your domain and command classes accordingly.
11-
Search for `cascade: true` (or `cascade: false`) and replace with `cascaded: true` (or `false`).
11+
Search for `cascade: true` (or `cascade: false`) and replace with `cascaded: true` (or `false`).
12+
13+
The constraint class was renamed from `CascadeConstraint` to `CascadedConstraint` and the registration class from `CascadeConstraintRegistration` to `CascadedConstraintRegistration`
14+
15+
The previous format for the `field` name on collections was `field.0.childProperty`, but since
16+
Grails 7 does cascade validations on Domain objects, and the format for errors in this validation is `field[0].childProperty` a configuration property was added, to control if the legacy format should be used, or if the new format is used:
17+
18+
[source,yaml]
19+
----
20+
constraints:
21+
cascaded:
22+
legacy: true # default to false
23+
----

src/main/asciidoc/3. usage.adoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ include::{example}/src/integration-test/groovy/cascade/PersonDataServiceSpec.gro
6767

6868
== Databinding usage
6969

70-
When binding data to `grails.validation.Validateable` objects (e.g., via controllers or REST endpoints), call `validate()` or rely on automatic validation in data-binding flows, the `CascadeConstraint` ensures marked associations are validated as part of the root object validation.
70+
When binding data to `grails.validation.Validateable` objects (e.g., via controllers or REST endpoints), call `validate()` or rely on automatic validation in data-binding flows, the `CascadedConstraint` ensures marked associations are validated as part of the root object validation.
7171

7272
This can be illustrated with two examples:
7373

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package grails.cascade.validation
22

33

4-
import grails.cascade.validation.internal.CascadeConstraintRegistration
4+
import grails.cascade.validation.internal.CascadedConstraintRegistration
55
import grails.plugins.Plugin
66

77
class CascadeValidationGrailsPlugin extends Plugin {
@@ -38,7 +38,7 @@ Used with permission.
3838

3939

4040
void doWithApplicationContext() {
41-
CascadeConstraintRegistration.register(applicationContext)
41+
CascadedConstraintRegistration.register(applicationContext)
4242
}
4343

4444
}

src/main/groovy/grails/cascade/validation/internal/CascadeConstraint.groovy renamed to src/main/groovy/grails/cascade/validation/internal/CascadedConstraint.groovy

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package grails.cascade.validation.internal
22

3-
3+
import grails.util.Holders
44
import groovy.transform.CompileDynamic
55
import groovy.transform.CompileStatic
66
import org.grails.datastore.gorm.validation.constraints.AbstractConstraint
@@ -20,12 +20,12 @@ import org.springframework.validation.FieldError
2020
* @author Russell Morrisey
2121
*/
2222
@CompileStatic
23-
class CascadeConstraint extends AbstractConstraint {
23+
class CascadedConstraint extends AbstractConstraint {
2424

2525
static final String CASCADE_CONSTRAINT = "cascaded"
2626
private final Closure<Boolean> enabledEvaluator
2727

28-
CascadeConstraint(Class<?> constraintOwningClass, String constraintPropertyName, Object constraintParameter, MessageSource messageSource) {
28+
CascadedConstraint(Class<?> constraintOwningClass, String constraintPropertyName, Object constraintParameter, MessageSource messageSource) {
2929
super(constraintOwningClass, constraintPropertyName, constraintParameter, messageSource)
3030
this.enabledEvaluator = constraintParameter instanceof Closure ? (constraintParameter as Closure<Boolean>) : { (constraintParameter as boolean) }
3131
if(this.enabledEvaluator.maximumNumberOfParameters > 2) {
@@ -90,7 +90,7 @@ class CascadeConstraint extends AbstractConstraint {
9090
String field
9191

9292
if (index != null) {
93-
field = "${propertyName}.${index}.${childFieldError.field}"
93+
field = useLegacyNaming ? "${propertyName}.${index}.${childFieldError.field}" : "${propertyName}[${index}].${childFieldError.field}"
9494
} else {
9595
field = "${propertyName}.${childFieldError.field}"
9696
}
@@ -99,6 +99,10 @@ class CascadeConstraint extends AbstractConstraint {
9999
errors.addError(fieldError)
100100
}
101101
}
102+
103+
private static boolean getUseLegacyNaming() {
104+
Holders.config?.getProperty('constraints.cascaded.legacy', Boolean, Boolean.FALSE)
105+
}
102106

103107
private boolean isEnabled(Object target, Object propertyValue) {
104108
switch(enabledEvaluator.maximumNumberOfParameters) {

src/main/groovy/grails/cascade/validation/internal/CascadeConstraintRegistration.groovy renamed to src/main/groovy/grails/cascade/validation/internal/CascadedConstraintRegistration.groovy

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@ import org.grails.datastore.mapping.validation.ValidatorRegistry
99
import org.springframework.context.ApplicationContext
1010

1111
@Slf4j
12-
class CascadeConstraintRegistration {
12+
class CascadedConstraintRegistration {
1313
static void register(ApplicationContext applicationContext) {
14+
1415
registerCascadeConstraintOnBeans(applicationContext, ConstraintsEvaluator, DefaultConstraintEvaluator) {
1516
it.constraintRegistry
1617
}
@@ -26,7 +27,7 @@ class CascadeConstraintRegistration {
2627
evaluators.each { name, evaluator ->
2728
if (clazz.isAssignableFrom(evaluator.getClass())) {
2829
ConstraintRegistry reg = closure.call(evaluator)
29-
reg.addConstraint(CascadeConstraint)
30+
reg.addConstraint(CascadedConstraint)
3031
}
3132
log.debug("Registered CascadeConstraint on $name evaluator on $interfaceClass")
3233
}

src/test/groovy/grails/cascade/validation/CascadeValidationConstraintSpec.groovy

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
package grails.cascade.validation
22

3-
import grails.cascade.validation.internal.CascadeConstraint
3+
import grails.cascade.validation.internal.CascadedConstraint
44
import grails.cascade.validation.support.ValidateableParent
55
import grails.cascade.validation.support.ValidateableProperty
66
import grails.validation.ValidationErrors
7+
import org.grails.testing.GrailsUnitTest
78
import org.springframework.validation.Errors
89
import org.springframework.validation.FieldError
910
import spock.lang.Specification
@@ -12,9 +13,9 @@ import spock.lang.Specification
1213
* @author: rmorrise
1314
* @author Eric Kelm
1415
*/
15-
class CascadeValidationConstraintSpec extends Specification {
16+
class CascadeValidationConstraintSpec extends Specification implements GrailsUnitTest {
1617

17-
CascadeConstraint constraint
18+
CascadedConstraint constraint
1819
ValidateableParent parent
1920
ValidationErrors errors = Mock()
2021

@@ -26,7 +27,7 @@ class CascadeValidationConstraintSpec extends Specification {
2627

2728
void "constraint name should be cascade"() {
2829
given:
29-
constraint = new CascadeConstraint(
30+
constraint = new CascadedConstraint(
3031
ValidateableParent,
3132
'property',
3233
true,
@@ -39,7 +40,7 @@ class CascadeValidationConstraintSpec extends Specification {
3940

4041
void "validateWithVetoing fails when constraint is set on non-validatable type"() {
4142
given:
42-
constraint = new CascadeConstraint(
43+
constraint = new CascadedConstraint(
4344
ValidateableParent,
4445
'property',
4546
true,
@@ -57,7 +58,7 @@ class CascadeValidationConstraintSpec extends Specification {
5758

5859
void "validateWithVetoing returns valid when constraint is set to validateable type and constraints pass"() {
5960
given:
60-
constraint = new CascadeConstraint(
61+
constraint = new CascadedConstraint(
6162
ValidateableParent,
6263
'property',
6364
true,
@@ -76,7 +77,7 @@ class CascadeValidationConstraintSpec extends Specification {
7677

7778
void "validateWithVetoing returns invalid when constraint is set to validateable type and constraints fail"() {
7879
given:
79-
constraint = new CascadeConstraint(
80+
constraint = new CascadedConstraint(
8081
ValidateableParent,
8182
'property',
8283
true,
@@ -116,7 +117,7 @@ class CascadeValidationConstraintSpec extends Specification {
116117

117118
void "validateWithVetoing returns invalid when constraint is set to validateable type and constraints fail on list"() {
118119
given:
119-
constraint = new CascadeConstraint(
120+
constraint = new CascadedConstraint(
120121
ValidateableParent,
121122
'property',
122123
true,
@@ -154,7 +155,7 @@ class CascadeValidationConstraintSpec extends Specification {
154155

155156
void "constraint only validates if enabled evaluates to true"() {
156157
given:
157-
constraint = new CascadeConstraint(
158+
constraint = new CascadedConstraint(
158159
ValidateableParent,
159160
'anotherProperty',
160161
truth,
@@ -185,7 +186,7 @@ class CascadeValidationConstraintSpec extends Specification {
185186

186187
void "constraint does not support non-validateable types"() {
187188
given:
188-
constraint = new CascadeConstraint(
189+
constraint = new CascadedConstraint(
189190
ValidateableParent,
190191
'property',
191192
true,
@@ -198,7 +199,7 @@ class CascadeValidationConstraintSpec extends Specification {
198199

199200
void "constraint supports validateable types"() {
200201
given:
201-
constraint = new CascadeConstraint(
202+
constraint = new CascadedConstraint(
202203
ValidateableParent,
203204
'property',
204205
true,
@@ -211,7 +212,7 @@ class CascadeValidationConstraintSpec extends Specification {
211212

212213
void "constraint supports collection types"() {
213214
given:
214-
constraint = new CascadeConstraint(
215+
constraint = new CascadedConstraint(
215216
ValidateableParent,
216217
'property',
217218
true,
@@ -224,7 +225,7 @@ class CascadeValidationConstraintSpec extends Specification {
224225

225226
void "constraint can handle constraintParameter when is a closure with one or two params"() {
226227
when:
227-
constraint = new CascadeConstraint(
228+
constraint = new CascadedConstraint(
228229
ValidateableParent,
229230
'property',
230231
closure,
@@ -240,7 +241,7 @@ class CascadeValidationConstraintSpec extends Specification {
240241

241242
void "constraint cannot handle constraintParameter when is a closure more than two parameters"() {
242243
when:
243-
constraint = new CascadeConstraint(
244+
constraint = new CascadedConstraint(
244245
ValidateableParent,
245246
'property',
246247
{ a, b, c -> true },
@@ -254,7 +255,7 @@ class CascadeValidationConstraintSpec extends Specification {
254255

255256
void "constraint cannot handle constraintParameter other than boolean"() {
256257
when:
257-
constraint = new CascadeConstraint(
258+
constraint = new CascadedConstraint(
258259
ValidateableParent,
259260
'property',
260261
['x'],

0 commit comments

Comments
 (0)