Rich Domain classes plugin

  • Tags: validation
  • Latest: 1.0.6
  • Last Updated: 24 January 2012
  • Grails version: 1.3.7 > *
  • Authors: null
1 vote
Dependency:
compile ":rich-domain:1.0.6"

 Documentation

Summary

This plugin provides dependency injection and validation support for POGO's that are NOT Grails domain classes.

Installation

grails install-plugin rich-domain

Description

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 AutoWireBeanListener

But 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