Last updated by houbie 5 months ago
Overview
The rich domain classes plugin provides dependency injection and validation support for POGO's (plain old groovy objects) that are NOT Grails domain classes.
Dependency injection (auto wiring)
An AST transformation makes sure that auto wiring of dependencies is performed automatically after each constructor call and after deserialization. Hence no agents or other Spring AOP stuff is necessary.
Usage
Add the
WiredBean annotation to your class and use standard annotations like
Inject and
Resource to mark members that should be auto wired:
import be.ixor.grails.richdomain.autowire.WiredBean
import javax.annotation.Resource
import javax.inject.Inject
import org.springframework.context.MessageSource
import org.springframework.web.servlet.LocaleResolver@WiredBean
class MyRichDomainClass {
@Inject
MessageSource injectedMessageSourceByType @Resource(name = "localeResolver")
LocaleResolver injectedLocaleResolverByName LocaleResolver notInjectedLocalResolver
}
Classes that implement
Serializable will be wired again after deserialization. Injected fields should be marked as
transient in this case.
Testing
All objects are wired when running integration and functional tests. In unit tests, there is no appliction context and hence no wiring.
You can however inject properties with a
AutoWireBeanListener :
ApplicationContextUtils.autoWireBeanListener = {bean ->
if (bean instanceof MyBean) {
bean.foo = bar
}
} as AutoWireBeanListenerBut don't forget to reset the listener, otherwise following tests may fail:
@After
void clearAutoWireBeanListener() {
ApplicationContextUtils.autoWireBeanListener = null
}Validation support
The extended validation plugin adds declarative constraints and validation capabilities to classes that are not grails domain classes.
The plugin is similar to the build-in
ValidationGrailsPlugin , but has a couple of extra features:
- constraint groups
- cascaded validation
- instance validators
- partial validation
- errors that survive serialization/de-serialization
The rich domain plugin supersedes the (deprecated) extended-validation plugin. The new plugin is based on AST transformations from Grails 2.x which has the advantage that validateable beans are not automatically added to the application context anymore. Furthermore the errors are now stored inside the bean and thus will survive (de)serialization.
Usage
Add the
be.ixor.grails.richdomain.validation.Validateable annotation to your class and define a static constraints closure:
@Validateable
class Person {
…
static constraints = …
}
Listing classes in Config.groovy to make them validateable is not supported anymore.
Defining constraints
In addition to the standard grails constraints, the plugin provides some extra features.
Constraint groups
You can define logical constraint groups. This is particularly useful in wizards, where the information comes from multiple pages and where one only wants to validate the fields on a certain page.
static constraints = {
personalDetails {
firstName(blank: false)
lastName(blank: false)
}
preferences {
uiPrefs(validator: {...})
}
}Cascaded validation
Validation of nested objects can be achieved with the
cascade constraint:
class Person {
…
Address address static constraints = {
address(cascade: true)
...Instance validation
Constraints that validate an instance rather than one field, don't need to be bound to a single field.
static constraints = {
maxNameLength(validator: {
if(firstName.size() + lastName.size() > 50) return "nameToLong"
})
...Partial validation
The dynamic
validate method accepts three arguments:
includes ,
excludes and
groups // check all constraints
person.validate()// only check included constraints
person.validate(includes: ["firstName", "lastName"])// exclude specific (cascaded) constraints
person.validate(excludes: ["firstName", "address.city"])// check only constraints in the specified groups, this will also limit cascaded validation to these groups
person.validate(groups: ["personalDetails"])// exclude the constraints in the specified groups (excludeGroups are cascaded)
person.validate(excludeGroups: ["personalDetails"])// all possible combinations are also valid: excludes are removed from the union of includes and groups
person.validate(includes: ["address.*", "curriculum.companies.*"], groups: ["personalDetails"], excludes: ["address.street"])
Nested errors
All errors can recursively be fetched and cleared :
assert person.allErrorsRecursive.size() == 3
person.clearErrorsRecursive()
Testing
Constraints and validation can be tested in plain unit tests without the need of special setup or mocks.
Combining Validateable and WiredBean
Add the
be.ixor.grails.richdomain.RichDomain annotation to your class to get both auto wiring and validation support:
@RichDomain
class MyRichDomainClass {
@Inject
MessageSource injectedMessageSourceByType static constraints = {...}
}Changes
1.0.5
- Put all precompiled classes in lib/rich-domain-binaries.jar so that the AST transformations are always available
- Fixed bug in WiredBeanASTTransformation
- Added AutoWireBeanListener for easier unit testing