...
| Code Block |
|---|
| borderStyle | dashed |
|---|
| title | Step 1 |
|---|
|
import groovytools.builder.*
MetaBuilder mb = new MetaBuilder(getClass().getClassLoader())
|
...
| Code Block |
|---|
| borderStyle | dashed |
|---|
| title | Step 2 |
|---|
|
class Customer {
def name
def dateOfBirth
def ssn
def phone
}
mb.define {
customer(factory: Customer) {
properties {
name()
dateOfBirth()
ssn()
}
}
}
|
...
| Code Block |
|---|
| borderStyle | dashed |
|---|
| title | Step 3 |
|---|
|
def aCustomer = mb.build {
customer {
name = 'J. Doe'
dateOfBirth = '1/1/1900'
ssn = '555-55-5555'
}
}
// this is equivalent
aCustomer = mb.build {
customer ( name: 'J. Doe', dateOfBirth: '1/1/1900', ssn: '555-55-5555')
}
// you can even mix up the styles:
aCustomer = mb.build {
customer ( name: 'J. Doe', dateOfBirth: '1/1/1900') {
ssn = '555-55-5555'
}
}
|
...
| Code Block |
|---|
| borderStyle | dashed |
|---|
| title | Catching Errors |
|---|
|
aCustomer = mb.build {
customer {
name = 'J. Doe'
dob = '1/1/1900' // should have been 'dataOfBirth'
ssn = '555-55-5555'
phone = '1-555-555-5555' // not allowed
}
}
|
...
Despite the fact that phone is a an actual property of your class, MetaBuilder only goes by what's in your the schema. That kind of checking is nice to have and can protect private or sensitive properties. Read on to see how MetaBuilder can do even more with some additional information.
...
This next example demonstrates how one might use closure to create
Customer objects:
| Code Block |
|---|
| borderStyle | dashed |
|---|
| title | Specifying a Factory using a Closure |
|---|
|
mb.define {
customer2(factory: { new Customer() } )) {
properties {
name()
dateOfBirth()
ssn()
}
}
}
def aCustomer2 = mb.build {
customer2 {
name = 'J. Doe'
dateOfBirth = '1/1/1900'
ssn = '555-55-5555'
}
}
|
...
def: the default value or a Closure to produce the default value, as neededreq: if true, a value must be specfied (it could be null though)property: an alternative property name or Closure to use to set the valuemin: the minimum length for a property valuemax: the maximum length for a property valuecheck: causes an exception if the value fails the following test: | Code Block |
|---|
|
switch(value) {
case check: return true
}
return false
|
| Tip |
|---|
|
The check attribute accepts the following types of objects which makes enforcing constraints easy: - Closures
- Patterns
- Classes
- Numbers
- Strings
- Ranges
- Collections
|
Here's an example showing combinations of some of the above: | Code Block |
|---|
| borderStyle | dashed |
|---|
| title | MetaBuilder Property Attributes in Action |
|---|
|
mb.define {
customer3(factory: Customer) {
properties {
name(req: true, min: 1)
dob(property: 'dateOfBirth')
ssn(check: ~/\d{3}-\d{2}-\d{4}/)
}
}
def aCustomer3 = mb.build {
customer4 {
name = 'J. Doe'
dob = '1/1/1900'
ssn = '555-55-5555'
}
}
|
...
| Code Block |
|---|
| borderStyle | dashed |
|---|
| title | Adding a Phone Class and Schema |
|---|
|
class Phone {
def type
def number
}
mb.define {
phone (factory: Phone) {
properties {
type(check: ['home','cell','work'], def: 'home')
number(req: true, check: ~/\d{3}-\d{3}-\d{4}/)
}
}
}
|
...
| Code Block |
|---|
| borderStyle | dashed |
|---|
| title | Using the Phone Schema |
|---|
|
mb.define {
customer4(factory: Customer) {
properties {
name(req: true, min: 1)
dob(property: 'dateOfBirth')
ssn(check: ~/\d{3}-\d{2}-\d{4}/)
phone(schema: 'phone', req: true)
}
}
}
def aCustomer4 = mb.build {
customer4 {
name = 'J. Doe'
dob = '1/1/1900'
ssn = '555-55-5555'
phone {
type = 'home'
number = '123-456-7890'
}
}
}
|
...
| Code Block |
|---|
| borderStyle | dashed |
|---|
| title | Defining Schema with Indexed and Non-Indexed Collections |
|---|
|
class Customer2 extends Customer {
def phoneList = []
def addresses = [:]
}
mb.define {
customer5(factory: Customer2) {
properties {
name(req: true, min: 1)
dob(property: 'dateOfBirth')
ssn(check: ~/\d{3}-\d{2}-\d{4}/)
phone(schema: 'phone', req: true)
}
collections {
phoneList(min: 1) {
phone(schema: phone)
}
addresses (key: 'type', min: 1, max:2) {
address() {
properties {
type(check: ['billto', 'shipto'], def: 'billto')
street()
city()
state()
zip()
}
}
}
}
}
}
|
...
Another thing to note is the use of the key attribute in the addresses collection. Presenece Presence of the key attribute tells MetaBuilder that the parent-child relationship is indexed. In the following example, you can see that customer5 has both a list of phone and map of addresses using the address's type
and the key.
| Code Block |
|---|
| borderStyle | dashed |
|---|
| title | Building Objects with Indexed and Non-Indexed Collections |
|---|
|
def aCustomer5 = mb.build {
customer5 {
name = 'J. Doe'
dob = '1/1/1900'
ssn = '555-55-5555'
phone {
type = 'home'
number = '123-456-7890'
}
phoneList {
phone(type: 'work', number: '111-222-3333')
phone(type: 'cell', number: '444-555-6666')
}
address {
type = 'billto'
street = '1234 Some St.'
city = 'Some City'
zip = '12345'
}
address {
type = 'shipto'
street = '1234 Some Other St.'
city = 'Some Other City'
zip = '12345'
}
}
}
|
...