topics: functional programming, concurrency, web-development, REST, dynamic languages

Tuesday, March 18, 2008

Extending Ext.data.Record to support inheritance for domain types

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(){//just an example...
var sh = this.get('start_at').getHours(),
eh = this.get('end_at').getHours();
return 6 <= sh && eh <= 11;//must end before noon...
}
});

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'}//can be 'daily','weekly', 'monthly' or 'yearly'
]);
var rr = new m.RecurringReservation({
start_at:'17/03/2008-08:00',
end_at:'17/03/2008-09:30',
room:room_ref,//assuming room_ref refers to a m.Room object.
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;
};

1 comment:

Anonymous said...

brilliant - i needed this record extension ... i didn't see the code at the bottom for awhile which caused me some frustration - haha - but this is exactly, exactly what i was looking for. thanks!!!!