As I've
previously blogged about, the JavaScript framework ExtJS provides an excellent support for developing models in Controller-Model-View architectures in web-browser client applications. One of the tools Ext provides is the function
Ext.data.Record.create: using this one can easily create constructor functions for domain concepts. For example, in our room-booking application, TriBook, we defined a Reservation type concisely like this:
com.trifork.tribook.model.Reservation = Ext.data.Record.create([
{name: 'start_at', type:'date', dateFormat:'d/m/Y-H:i'},
{name: 'end_at', type:'date', dateFormat:'d/m/Y-H:i'},
{name: 'room'}
]);
That is: a Reservation object has 'start_at' and 'end_at' Date properties as well as a 'room' property (which refers to a 'Room' domain object, defined of course using
Ext.data.Record.create).
One point made in a previous article is that
Ext.data.Record.create returns a JavaScript
constructor function for the defined domain concept. This means that we can add domain logic to our domain types by augmenting their prototypes, e.g.,
Ext.apply(com.trifork.tribook.model.Reservation.prototype, {
isPreLunchReservation: function(){
var sh = this.get('start_at').getHours(),
eh = this.get('end_at').getHours();
return 6 < sh && eh < 11;
}
});
This is all very useful, particularly because
Ext.data.Record objects are supported by a bunch of other Ext functions, as discussed previously.
However, there is no built-in support for inheritance in domain concepts. For example, suppose that we like to create a sub-type of our reservation domain type: a RecurringReservation, which one can use e.g., to reserve a room the same time each week. It would be nice to be able to write the following:
var m = com.trifork.tribook.model;
m.RecurringReservation = Ext.data.Record.extend(m.Reservation, [
{name:'interval', type:'string'}
]);
var rr = new m.RecurringReservation({
start_at:'17/03/2008-08:00',
end_at:'17/03/2008-09:30',
room:room_ref,
interval: 'weekly'
});
Now, the object referred to by 'rr' should have all the specified state,
and all the domain logic of Reservation should be inherited, e.g., 'isPreLunchReservation' also works for RecurringReservation objects.
The following is an Ext extension that makes the above snipplet work:
Ext.data.Record.extend = function(sp,fields){
var sb = Ext.extend(sp,{}),
sb_f = sb.prototype.fields.clone(),
Field = Ext.data.Field,
i,N;
for (i=0,N=fields.length; i<N; i++) {
sb_f.add(new Field(fields[i]));
}
sb.prototype.fields = sb_f;
return sb;
};