Broadcast on Broadcast off
The Documentation for Project Zero has moved. Please update your bookmarks to: http://www.projectzero.org/documentation/

Table of
Contents...
Hide

Project Zero Developer’s Guide

Concepts and components
Basic concepts overview
Event processing
Writing Java handlers
Writing Groovy handlers
Firing events
Global Context
Global Context reference
Application directory layout
Virtualized directories
Assemble
PHP
Features and configuration
Configuration
Debugging
Dependencies
Packaging
Application classpath
Logging and tracing
RESTful resources
RESTful documentation
File serving
Response rendering
Validators and validation
HTTP error handling
Calling a remote resource
Using the Connection API
Sending an email using EmailConnection
Configuring destinations
Configuring protocols
Configuring connection handlers
Creating a connection handler
Creating a custom protocol transport
Simple logging connection handlers
Protocol reference
Client programming with Dojo
Runtime options
Deployment modifications
HTTP configuration
SSL configuration
Proxy configuration
Extending the CLI
Security considerations
Authentication
OpenID authentication
Extending security
Security tokens
CSRF prevention support
Extending token support
Leveraging TAI
User service
File based user service
LDAP user service
Extending user service
Security Utilities
Leveraging XOREncoder
Extensions
Atom support
RSS support
JSON support
XMLEncoder
REST to SOAP extension
URIUtils
Developer Web tools
Database setup tools
Configuring data access
Common query patterns
Advanced query patterns
Update patterns
Local database transactions
Extending data access
Configuration vendor differences
PHP data access
Resource model
Configuring ZRM
Resource model declaration
Programmatic model API
HTTP REST API
A ZRM mini tutorial
Active content filtering support
Default filters
Custom filters
Runtime management
Management commands
Zero socket opener
Other extension modules
Amazon E-commerce service
Flickr service
WeatherZero forecast service
Wikipedia service
Reference
Zero command line interface
JavaDoc - Public API
JavaDoc - Public SPI
JavaDoc - All Classes

 

Programmatic Model API

The Model API accesses resources programmatically using collection-like constructs. In addition to the basic CRUD programming model, ZRM introduces a list-function, the LCRUD model. The list function allows for listing and filtering collection members using simple conditions.

Object types

The involved object types are Collection, List, and Member

Collection
Implements the LCRUD interface that returns List and Member objects.
List
The a standard Java List.
Member
The construct that contains the definitions of the fields and their values. Each Member has also a unique identifier and last updated the timestamp field.

The following example shows some common interactions with the Model API:

// retrieve the default collection for model 'persons'
def collection = TypeCollection.retrieve('persons')

// create a new member in the collection, create returns Member instance
def joe = collection.create([firstname: 'Joe'])

// update member in the collection, update returns Member instance
joe.firstname = 'Joseph'
joe = collection.update(joe)
assert 'Joseph' == joe.firstname

// delete member from collection
collection.delete(joe.id)

// retrieve member from collection
def id = joe.id
joe = collection.retrieve(id)

// list all collection results
def all_people = collection.list()

// filtered list using conditions
def some_people = collection.list(firstname: 'Joseph')
people = collection.list(firstname__contains: 'se')
people = collection.list(firstname__endswith: 'ph')

// paged results from 11th to 20th items in the overall collection
def paged_people = collection[11..20].list()

// paged and filtered results
def filtered_paged = collection.filter(firstname__endswith: 'ph')[201..300].list()

Basic LCRUD

For these examples, the following simple resource model is defined in the /app/models folder:

// File: /app/models/persons.groovy
fields = [
    firstname: [type: 'CharField', max_length: 30],
    birthdate: [type: 'DateField'],
    ischild: [type: 'BooleanField'],
]

collections = [
    children: [member_filters: [ischild: true]],
]

For this example, a collection of persons is instantiated :

import zero.resource.TypeCollection
...
def persons = TypeCollection.retrieve('persons')

List all members

A List of all collection Member objects can be retrieved from the database with the list() method:

def allPersons = persons.list()
allPersons.each { person ->
        println person.firstname
}

List a subset of members

A List of subset of the Member objects can be retrieved by passing a filter condition to the list method as an argument (see Filter Conditions section below for further details):

def somePersons = persons.list(firstname__startswith: 'Jo')
somePersons.each { person ->
        println person.birthdate
}

Create a new member

A new Member object can be created in the database with the create(member) method:

def person = persons.create(firstname:'Joe', birthdate:'1978-01-21', ischild: true)

Retrieve a member

An individual collection member can be retrieved from the database with the retrieve(id) method:

def person = persons.retrieve(1)

Update a member

A collection member can be updated in the database with the update(member) method:

def result = persons.update(id: '1', firstname:'Bob', birth_date:'1961-12-05')

The update -method returns true if the update operation was successful, otherwise it return false .

Delete a member

A collection member can be deleted with the delete(id) method:

def result = persons.delete('1')

The delete -method returns true if the delete operation was successful, otherwise it will return false .

Filters

Filtered collections

A filter can be applied to a collection to create a new collection that is a subset of the original. Each filtered collection is a distinct collection instance that that can be stored, used, and reused independent of its parent. The filter conditions can be passed in dynamically, which eliminates the need for writing a different service for each subset of the data that the client might need. The filters can be chained, therefore each filter creates a new logical subset-collection. However, data is not retrieved from the database until a collection member is actually accessed.

def somePersons = persons.filter(firstname__startswith: 'Jo')
def fewerPersons = somePersons.filter(firstname__endswith: 'e')
....or....
def fewerPersons = persons.filter(firstname__startswith: 'Jo')
.filter(firstname__endswith: 'e')

Filter conditions

The filter conditions are specified using the following convention: [field name][double underscore][operator]. This convention makes the programmatic API and HTTP API symmetric (this convention makes it easy to pass filters as URI parameters). The supported filters are:

Comparison Operand Operator Example
Equal to any field equals firstname__equals: 'Joe'
Equal to shortcut any field   firstname: 'Joe'
Greater than any field gt firstname__gt: 'Joe'
Greater than or equal any field gte firstname__gte: 'Joe'
Less than any field lt firstname__lt: 'Joe'
Less than or equal any field lte firstname__lte: 'Joe'
Ends with CharField,EmailField,USPhoneField,USStateField endswith firstname__endswith: 'oe'
Starts with CharField,EmailField,USPhoneField,USStateField startswith firstname__startswith: 'Jo'
Contains CharField,EmailField,USPhoneField,USStateField contains firstname__contains: 'Jo'
In any field in firstname__in: ['Joe', 'Bob', 'Dan']
Year equal to DateField, DateTimeField year birthdate__year: '2005'
Month equal to DateField, DateTimeField month birthdate__month: '12'
Day equal to DateField, DateTimeField day birthdate__day: '5'
After date DateField, DateTimeField, TimeField after birthdate__after: '2005-12-12'
Before date DateField, DateTimeField, TimeField before birthdate__before: '2005-12-12'
Between DateField, DateTimeField, TimeField between birthdate__between: ['2005-12-12', '2006-12-12']

More than one filter condition can be combined with logical AND using a comma as a separator between the filters:

def somePersons = persons.filter(firstname__startswith: 'Jo', birthdate__before: '1990-12-12')

Paging collections

Because it is impractical and inefficient to return the full results of a collection, the Model API supports a paging scheme you can use to indicate what slice of data a list() or filter() call should return.

Note: Paging collections uses a 0=-based index. For example, the first member in a collection is indexed at =0 and the 10th at 9. Ordering of members in a collection is transient and not fixed from collection to collection.

Paging using ranges

You can specify a range of the data using the following syntax:

def paged_people = collection[10..19].list()

Paging ranges can also be combined with filters:

def filtered_paged = collection.filter(firstname__endswith: 'ph')[200..299].list()

Paging using start and count

Some clients might need to specify the start index and the page size, or count, rather than an explicit range of data. To do this, use the limit(start,count) method on a collection as shown in the following example:

def filtered_paged = collection.limit(10,10).list()

This has the same effect as using [11..20]. You can also use limit(start, count) with filtering as shown in the following example:

def filtered_paged = collection.filter(firstname__endswith: 'ph').limit(201,100).list()

Named collections

Filters can be specified in the model declaration by defining a named collection:

// File: /app/models/persons.groovy
fields = [ . . .]

collections = [
    children: [member_filters: [ischild: true]],
]

A named collection is accessed in the following way:

def children = persons.children.list()
def childrenNamedBob = persons.children.list(firstname: 'Bob')


warning Note that persons.list(ischild:true) returns a List while persons.filter(ischild:true) and persons.children return a Collection

Data Validation

Zero Resource Models provides a robust data validation API. A call to Member.validate() returns a data structure that contains error information. A data structure is useful because you can decide how to represent this information back to the HTTP response:

def member = new Member('person', [firstname: 'Joe', birthdate:'1978-01-21'])
def messages = member.validate()

if(messages) {
  // do something useful with messages
} else {
  TypeCollection.retrieve('person').create(member)
}

The data structure is simply a map of lists. The map prevents more than one field from having validation errors. The map's key is the field name. The value is a list of error messages. The list as values in the map prevents each field from being in violation of more than one validation rule.

The validate() method is also called in the create() or update() methods if the data is not yet validated. If a validation error occurs, the zero.resource.exceptions.ValidationException is thrown:

try {
    persons.create(firstname:'Joe', birthdate:'1978-01-21')
} catch (ValidationException e) {
    def messages = e.messages
    if (messages) {
      // do something useful with messages
    }
}

r17 - 09 Feb 2008 - 18:21:27 - paynel
Syndicate this site RSS ATOM
Copyright 2007 © IBM Corporation | Privacy | Terms of Use | About this site