Ars Machina
Software development isn't just technique, it is an art too

Tapestry CRUD Concepts

This package defines some useful concepts, each one defined as a Java inteface. All the *Source interfaces are Tapestry-IoC services, so their configuration is made through a contribute*() method in AppModule or any other module class.

SingleTypeSelectModelFactory

Interface that defines a factory of SelectModel instances for a given type.

Tapestry CRUD provides a class, DefaultSingleTypeSelectModelFactory, that can be used to easily implement SingleTypeSelectModelFactory without writing a specific class, just instantiating it.

SingleTypeSelectModelFactory is not meant to be used directly in page classes. Instead, use a SelectModelFactory.

DefaultSingleTypeSelectModelFactory projectSMF = 
	new DefaultSingleTypeSelectModelFactory(
		projectController, projectEncoder);

SelectModelFactory

Interface that defines a factory of SelectModelcode> instances for any given type, provided it has a configured SingleTypeSelectModelFactory for it.

It provides two methods: create(Class clasz), that returns a SelectModel containing all the instances of the given class; and
<T> create(Class<T> clasz, List<T> objects), that returns a SelectModel containing just the objects specified in its second parameter.

Example configuration:

public void contributeSelectModelFactory(
	MappedConfiguration<Class, SingleTypeSelectModelFactory> contributions) {

	DefaultSingleTypeSelectModelFactory projectSMF = 
		new DefaultSingleTypeSelectModelFactory(
				projectController, projectEncoder);
				
	contributions.add(Project.class, projectSMF);
	
}				

ActivationContextEncoder

Encapsulates the Tapestry 5 activation context logic for objects of a given type. This way, there is only one place in the whole application that will change when the activation context value for one given class is changed.

This concept is used by the crud/ActivationContextPageLink component: it receives the object as a parameter, not its activation context value. The activation context value is obtained through an ActivationContextEncoder instance.

Tipically, one ActivationContextEncoder implementation will be created for each entity class.

ActivationContextEncoderSource

Maps classes to their ActivationContextEncoder instances. If no ActivationContextEncoder is found for a given class, it returns the Encoder configured for that given class.

Example configuration:

public void contributeActivationContextEncoderSource(
		MappedConfiguration<Class, ActivationContextEncoder> contributions) {
				
	contributions.add(User.class, userActivationContextEncoder);
}				

LabelEncoder

Encapsulates the logic of giving an user-presentable description (label) for objects of a given type. This way, there is only one place in the whole application that will change when this logic changed.

LabelEncoderSource

Maps classes to their LabelEncoder instances.If no LabelEncoder is found for a given class, it returns the Encoder configured for that given class.

Example configuration:

public void contributeLabelEncoderSource(
		MappedConfiguration<Class, LabelEncoder> contributions) {
				
	contributions.add(User.class, userLabelEncoder);
}				

Encoder

Interface that is a single place to look for all encoding-related logic for object of a given type. It extends ActivationContextEncoder, LabelEncoder and Tapestry's PrimaryKeyEncoder, ValueEncoder, and ValueEncoderFactory. Instead of implementing one class for each of the above interfaces for each class that needs a CRUD, we implement a single Encoder for all them. Notice that these interfaces have a lot in common, so there is a lot of room for code reuse.

EncoderSource

Maps classes to their Encoder instances.

Example configuration:

public void contributeEncoderSource(
		MappedConfiguration<Class, LabelEncoder> contributions) {
				
	contributions.add(User.class, userEncoder);
}				

ControllerSource

Maps classes to their Controller (from Generic Controller package) instances.

Example configuration:

public void contributeControllerSource(
		MappedConfiguration<Class, Controller> contributions) {

	contributions.add(User.class, userController);
}