Medryx Observations

July 24, 2008

JsonRestStore — Custom Services, Schemas, and Lazy Loading

Filed under: dojo, javascript — Tags: , , — maulin @ 7:02 am

The newest and most complete store in the dojox.data arsenal is dojox.data.JsonRestStore. Its primary purpose is to make interaction between a RESTful data source and dojo.data seamless and simple, and it accomplishes that goal with ease. But a quick peek under the covers and you will find that you may very well be using JsonRestStore for all your Json dojo.data needs. This article aims to show you how you can use JsonRestStore and its more advanced features like lazy entity and property loading and client side filtering, and how you can adapt JsonRestStore to any data source that provides JSON data to your client.

RESTful web services map CRUD services to the HTTP protocol: Create = Put, Read = Get, Update = Post, and Delete = Delete. You can easily sort through the data that would be passed to each of these services as well — Create gets the full data of a new object without an id, Update gets the full data of the updated object with an id, read gets an id who’s object should be returned and delete gets an id whose object should be deleted. Its not rocket science. But if you broaden your thinking from REST, you’ll see this is essentially an API for interfacing with server side data stores. REST is simply one way of implementing it. Instead of REST, you could use an RPC service, or any other service, so long as you could map the service to this API.

JsonRestStore expects you to provide it a service that implements the REST API. The easiest way to do this is to simply specify a target url as a String. JsonRestStore will then automatically send that URL the commands in the API using the REST protocol described above. However, instead of simply providing a target URL, you can actually initialize a JsonRestStore with your own service. The service is simple an object that has the following methods that each return a dojo.Deferred:

  • A directly callable function that takes an id or a query/queryOptions pair and returns the results of the query
  • A put function that takes an id and value and creates the value at the given id
  • A post function that takes an id and value and appends/updates the value at the given id
  • A delete function that takes an id and deletes the value at that id

It is important to know that if you do provide a custom service, you must still provide a unique string as the “target” attribute for the JsonRestStore. As I will explain later — JsonRestStore actually manages interdependencies between objects and between data stores — and the target attribute is how it uniquely identifies one store from another, as well as how it identifies which store a particular object belongs to.

So lets start coding. First lets define a custom service that we will be providing to JsonRestStore. Lets say we are building a system for paging physicians. Each physician has a pager. We’ll start simple (this is illustrative code, not real code!):

var mdService = function(query, queryOptions) {
return dojo.xhrGet({url:"queryUrl.php", handleAs:'json', content:{query:query, queryOptions:queryOptions}});
}
mdService.put = function(id, value) {
mdService.post(id, value);
}
mdService.post = function(id, value) {
return dojo.xhrPost({url:"actionUrl.php", handleAs:'json', content:{action:"update", id:id, value:value}});
}
mdService['delete'] = function(id) {
return dojo.xhrPost({url:"actionUrl.php", handleAs:'json', content:{action:"delete", id:id}});
}

And that is it. Now lets feed this service to JsonRestStore and create new store for our physicians:

var mdStore = new dojox.data.JsonRestStore({target:"md", service:mdService});

You can now use this store as you would any other store — feed it to a tree, or a grid, or to any other dojo.data enabled widget, and everything just works. But it gets better. Much better.

As I stated before, lets build our paging system. In our system we have about 10 pagers and 30 physicians. At any one time, each pager is assigned to 0 or 1 physicians. So in addition to being able to navigate from physician to pager given a list of physicians, we need to be able to navigate from pager to physician given a list of pagers. So lets build that store:

var pagerService = function(query, queryOptions) {
return dojo.xhrGet({url:"pagers.php", handleAs:'json',content:{query:query, queryOptions:queryOptions}});
}
pagerService.put = function(id, value) {
  var d = new dojo.Deferred();
  d.callback(); //put is a noop for pagers
  return d;
}
pagerService.post = function(id, value) {
return dojo.xhrPost({url:"updatePager.php", handleAs:'json', content:{id:id, value:value}});
}
pagerService['delete'] = function(id) {
  var d = new dojo.Deferred();
  d.callback(); //delete is a noop for pagers
  return d;
}
 
var pagerStore = new dojox.data.JsonRestStore({target:"pager", service:pagerService});

This where the magic starts. Lets create some data:

[
  {"id":"1", "name":"Maulin Shah, MD", "pager":{$ref:"pager/1"} },
  {"id":"2", "name":"Doogie Howser, MD", "pager":{$ref:"pager/2"} }
]

and here are some pagers

[
  {"id":"1", "md":{$ref:"md/1"}, "number":"12345", "name":"Yellow Pager"},
  {"id":"2", "md":{$ref:"md/2"}, "number":"54321", "name":"Triage Pager"}
]

Notice the “$ref” notation. This is JsonSchema style notation for indicating references to other objects. This lets you simply define the reference without loading the referenced object, and the referenced object will be lazily loaded when necessary. Notice the the $ref is essentially a “path” to the referenced object — or in less RESTy lingo, its the “class” of the referenced object along with its id (or perhaps its the “table” of the referenced object along with its primary key, if that’s the lingo that floats your boat).

Note that you do not have to make a reference lazy, or you make the reference partially lazy. I’ll go over these concepts a bit later.)

So what happens when we load some physicians into a grid?

<table border="0">
<thead>
<tr>
<th>Physician</th>
<th>Pager</th>
</tr>
</thead>
</table>

This would create a grid that looks something like:

Physician Pager
Maulin Shah, MD [Object object]
Doogie Howser, MD [Object object]

Woops. Not quite what we wanted. The problem here is that the pager object has no toString() method. So we have two options to solve this. Thie first involves using a schema and object prototype with your store.

Each store accepts a “schema” attribute as an initialization parameter. The schema is a JsonSchema style object describing the properties of the json objects that are returned for that store. In addition to the usual “properties” attribute of the schema, the schema can have a special “prototype” attribute that defines an object prototype that is used when the object of this class are instantiated. Thus you can mixin your own custom methods or properties into each instance of objects managed by your store. So lets create the prototype for the pager store:

 var pagerSchema = { prototype: {
      toString:function() {
        return this.number;
      }
    }
  }

Feed this to the store when it is created, and we’ve solved our problem:

  var pagerStore = new dojox.data.JsonRestStore({target:"pager", service:pagerService, schema:pagerSchema});

Now your table will look like you expect:

Physician Pager
Maulin Shah, MD 12345
Doogie Howser, MD 54321

(The second way to solve this problem involves over-riding the getValue method of JsonRestStore to allow dot-path notation. I’ll describe that in a future post that discusses my BeanStore extension of JsonRestStore.)

So what happened behind the scenes in rendering this table? The grid called physicianStore.fetch with no query (which is the same as “fetch all”). The Json described above was returned, with its references to the pager objects. JsonRestStore magically instantiates a new first class javascript object for each item in the Json, using the prototype in our schema as the prototype for the object. When mdStore.getValue(item, “pager”) is called by the Grid, the mdStore initiates a synchronous lazy call to pagerStore’s fetchItemByIdentity method which returns the pager, instantiated with the pager schema, which has a toString method which returns the number.

You could see how laziness could get out of hand very quickly. If we have a hundred physicians, we would have 100 synchronous calls to the server to lazy load pagers. That is not ideal. Lazy loading should really be reserved for more of a master-detail type usage when most lazily referenced items are never actually returned. So how could we make this more efficient? We can modify the data returned in our physicians data to include the pager number as well as a reference to the pager.

[
  {"id":"1", "name":"Maulin Shah, MD", "pager":{$ref:"pager/1", "number":"12345"} },
  {"id":"2", "name":"Doogie Howser, MD", "pager":{$ref:"pager/2", "number":"54321"} }
]

Now this is really cool. Now when rendering the grid, we incur no new requests to the server, since the pager number is already available locally. But now lets say we added a function where clicking on a row of our grid popped up an alert with the name of the pager. Since the name is not available, it would now lazily load the pager (i.e. call pagerStore.fetchItemByIdentity) and flesh out the rest of the pager object. Pretty clever eh?

In this article I have touched on just a few advanced features available with JsonRestStore — using a custom service, using a schema to provide an object prototype, lazily loading objects, and sparsely loading objects. But there is plenty more goodness to be found in this store! Stay tuned for an article on client-side caching and other great features of JsonRestStore!

June 24, 2008

MedryxORM and dot-path notation

Filed under: MedryxORM, dojo, javascript — Tags: , , — maulin @ 7:05 pm

Another nifty feature of MedryxORM is dot-path support. Since this ORM is all about relationships beween domain objects, it only makes sense to give the you access to traversing your relationship paths. This is used in two places primarily: getValue and properties. Let me give you a couple examples.

Lets say you are making a table of patients. You want to display their name, room number, and insurance. The object model looks something like:

In javascript, you would represent this model as:

 
dojo.declare("model.BaseEntity", null, {
	id:new medryx.orm.Identifier()
});
 
dojo.declare("model.Patient", model.BaseEntity, {
	firstName:new medryx.model.Property({label:2}),
	lastName:new medryx.model.Property({label:1}),
	visit:new medryx.model.Association({associationClass:"model.Visit"})
 
	getDisplayName:function() {
		return this.getLastName() + ", " + this.getFirstName();
	}
 
	queries:[
		{name:"AllPatients", properties:["*", "visit.*"]}
	]
});
 
dojo.declare("model.Visit", model.BaseEntity, {
	room:new medryx.model.Property(),
	bed:new medryx.model.Property(),
	insurance:new medryx.model.Association({associationClass:"model.Insurance")
});
 
dojo.declare("model.Insurance", model.BaseEntity. {
	companyName:new medryx.model.Property(),
	groupNumber:new medryx.model.Property()
});
 
entityManager.registerClass("model.Patient", "Patient");
entityManager.registerClass("model.Visit", "Visit");
entityManager.registerClass("model.Insurance", "Insurance");

So after running all this code, you now have a managed model that you can query (assuming you have set up your perisistenceService) and display the results, lets say in a grid. So lets define a grid:

	var view = {
		cells:[
			[
				{name:"Name", field:"displayName"},
				{name:"Room", field:"visit.room"},
				{name:"Bed", field:"visit.bed"},
				{name:"Insurance", field:"visit.insurance.companyName"},
		]]
	};
	var grid = new dojox.grid.DataGrid({
		store:entityManager,
		query:{},
		queryOptions:{entityClass:"Patient", namedQuery:"AllPatients", properties:dojo.map(view.cells[0], function(column) {
			return column.field;
		})},
		structure:[view]
	}, dojo.byId("patientsGridNode");

And that is it. So what have we done here?

As you can see, I’ve declare 3 simple classes. Each has some simple properties, and for kicks, I’ve declared a simple getDisplayName() method on my Patient.

I’ve defined a named query that get all patients from the server, and for each, returns all properties of the Patient and the patient’s associated Visit. (Note that this means the server will return the id of the insurance association, but not any attributes of the insurance company (i.e. the companyName and the groupNumber are not returned.)

Next we declare a simple dojox.grid.DataGrid. (This uses the grid model from Dojo 1.2, where DojoData has been removed and essentially plunked into DataGrid itself.)

As you can see, the “field” name in the view attached to the grid’s stucture uses “dot-path” notation to get to the desired field to display. Since our query is returning Patient’s, we need to navigate from the patient to the desired display field. This fails gracefully, retuning null if any part of the path does not exist.

Now lets look at the queryOptions in the grid. First, see how there is no query? I don’t absolutely have to have one — which means by default I will show all patients. The queryOptions are used to tell the entityManager which query to run. We then send in a new list of properties for the entityManager. Since we know that we will certainly need all the displayed fields in our data set, we can simply map() the view into a list of properties that we would like to instruct the entityManager to fetch.

Here is where things get really nice. Without any work on your part, the entityManager will have only made one call to the server when all of this is done, and it will have all the data you need to display your grid.

Now lets say you wanted to create a “detail” view that allowed people to see the patient’s insurance groupNumber if they clicked on the insurance information. Simple!

dojo.connect(grid, "onRowClick", function(e) {
	var patient = grid.getItem(e.rowIndex);
	alert("GroupNumber: " + patient.getVisit().getInsurance().getGroupNumber());
});

Notice how there is no query here, or anything else for you to write! You just use your getter, and you get lazy initialization of the groupNumber for free!

MedryxORM Part 2 — Server Queries, Client Queries and Named Queries

Filed under: MedryxORM, dojo, javascript — Tags: , — maulin @ 5:28 am

In my last post, I talked about a system I have built for managing domain objects on the client side, and making communication with services that communicate with the server more opaque to the average developer.

In this post, I would like to further describe this system, which for the time being I have called MedryxORM. Aside from the “free” lazy loading and relationship management you get with this system, you get a simplified method of handling queries. While there is a wrapper that allows for usual “fetch” access, there are several advanced features you get with the ORM.

First, lets look at what is different from a usual fetch and a fetch done from the ORM. Since the ORM manages multiple domain object classes (Managed Classes), you must specify the “root” class of your query. This is analogous to the “FROM” statement in SQL. The results are always a list of Managed Entities of the root class of your query.

Using the native api, you run a query as follows:

entityManager.query("Patient", {query:{lastName:"Smi*"}, sort:["lastName", "firstName"]});

and using the dojo ReadApi you would do:

entityManagerStore.fetch({query:{dischargeDate:null, lastName:"Smi*"}, sort:["lastName", "firstName"], queryOptions:{entityClass:"Patient"});

(I’ve left out some of the details for brevity).

So far, nothing to earth-shattering. But the ORM gives you a couple of neat features that can simplify your queries. Queries can be either client-side or server-side. A client-side query is always a subquery of a server-side query that has already been executed. So you can do filtering and sorting of the server-side query without having to re-query your server or marshall more data back and forth. This is quite useful for most queries — up to a point. If you have an enormous number of records that make server-side paging necessary (i.e., your server query does not return the entire set of records), then client queries don’t work. But this is really an exception to many circumstances. In my world, I will usually have on the order of a couple hundred patients and a couple dozen physicians as my major queries. I can easily load all that data into the client on a single call. But your results may vary. :-)

So how do you set this up? In a further attempt to keep end-developer (i.e. page developer and perhaps widget-developer) usage as simple as possble, we have created a concept of “Registered Queries”. This is similar to PreparedStatements in Java or Stored Procedures in SQL. Or, perhaps even more closly, this resembles NamedQueries in Hibernate.

A Registered Query is simply a query that is created when the Entity Manager is first configured. Its just another field in the mapping of an entity. You register a query in one of two ways:

entityManager.registerQuery("Patient", "AllPatients", {query:{dischargeDate:null}, sort:["lastName", "firstName"]});

or you can include a “queries” property on your mapping:

dojo.declare("model.Patient", null, {
	firstName:null,
	lastName:null,
	//snip...
	queries:[
		{name:"AllPatients", parameters: {query:{dischargeDate:null}, sort:["lastName", "firstName"]}}
	]
});

Once you have done this, then executing your query becomes even easier:

entityManager.query("Patient", {query:{lastName:"Smi*"}}, "AllPatients");

This will first load all patients from the server with dischargeDate = null, and then filter them on the client to all those whose lastName starts with “Smi”. Suppose you then want to filter on a different name, or sort in a different order? Just run query() again, with your new parameters. This also enables easy client-side paging.

To use the ReadApi, you add a registeredQueryName parameter in either your keywordArgs or query args or query options, and it will map correctly to the EntityManager’s query method.

entityManager.fetch({query:{lastName:"Smi*"}, queryOptions:{entityClass:"Patient", root:"AllPatients"}});

Since the EntityManager is built on the concept of sparsely-populated objects that get their more complete representation lazily or eagerly if you know you will be using them, all query operations also support a “properties” queryOption. This is similar to the properties valuein loadDeferred described in the previous post. You provide a list of properties that you know you will be using. If this is a server-query, then this is passed to the server during the request. However, if this is a client query, and the properties passed are different than those in the server query, then the results of the query are iterated, and each object that is missing a required property has those properties eagerly fetched from the server. This is transparent to the end user. (In fact, if your sub-query contains fields that are not yet loaded from the server, then this loads those in one request prior to running the sub-query). This functionality all assumes that your server can handle a query where the identifier field is passed an array of id’s to load.

June 22, 2008

Dojo(x) Grid Plugin — AutoFilter

Filed under: dojo, javascript — Tags: , , — maulin @ 1:50 am

The Dojo(x) Grid widget is fabulous. It makes creating incredibly fast, functional tables a breeze.

But periodically, you want to extend the Grid. For example, right now I have a need for “auto-filter”-like capability for a grid. Right click on a header row, show a dropdown of values, and let me filter the grid on that value. This isn’t a universal need, but certainly one that has been asked for on the dojo forum.

Now, aside from actually building the auto-filter, I’d like to propose creating a way of systematically extending Grid. Perhaps a concept of “plugins” would work. I could see something like this:

<div dojoType="dojox.Grid" store="store">
	<div dojoType="dojox.Grid.AutoFilterPlugin"></div>
</div>

Each plugin could decide how it incorporated itself into the grid. Mine, for example adds an optional attribute to each column in your structure called “autoFilter” which, if you set to true, makes autoFiltering just work if you are using a dojo.data store. And if you aren’t you simply provide a hook to your own custom “filter()” method, and it still works.

In any case, since the above declarative style plugin capability doesn’t yet exist (though I am thinking hard about how to do it), my AutoFilterPlugin is actually a mixin you use in declaring your own subclass of Grid like so:

	dojo.declare("MyGrid", [dojox.grid.DataGrid, medryx.widgets._GridPlugins.AutoFilter], {});

Then you can create your layout with the autoFilter field:

var layout = [
	{name: 'Alpha', field: 'col1', autoFilter:true},
	{name: 'Beta', field: 'col2'},
	{name: 'Gamma', field: 'col3'},
	{name: 'Delta', field: 'col4', width: "150px", autoFilter:true},
	{name: 'Epsilon', field: 'col5'},
	{name: 'Nexilon', field: 'col6'},
	{name: 'Zeta', field: 'col7'},
	{name: 'Eta', field: 'col1'},
	{name: 'Omega', field: 'col8'}
];

The rest of the usage is the same, and you end up with a Grid that looks like this:

A little explanation on how this works: The plugin over-rides postCreate and connects the onHeaderContextMenu event to a showAutoFilterDialog method in the plugin. That method opens a TooltipDialog with the select list of values. Chaning the value on the select list fires updateFilterValue which in turn first this.filter. The DataGrid already has a filter() method. If you are not using DataGrid, you need to mix in a method called filter() as well (in the dojo.declare of your subclass is where this would go).

How does the tooltip know what the values of the column are? This was the trickiest bit to figure out. Because the get() method is called from the context of a cell, not from the context of the grid, you couldn’t do a simple over-ride with a call to this.inherited(arguments) . That took a while to figure out. So instead, I create the modified get() method in my postCreate method. This runs the original get() and then stores the value before returning. Of course, this implies that you can only filter on values that the grid itself knows about and has rendered. For me that is fine. You could always create a different way of getting your filter values (for example by querying you store) if you wanted to.

So, if you understand any of that mess, I applaud you. And without further ado… here is the code:

/**
 * This is an aspect to your grid that will add in AutoFilter ability to the grid
 * You must provide the filter function for your grid. If will be called with an object
 * {
 *      fieldName:filterValue,
 *      fieldName2:filterValue2
 *      
 * }
 * to attach this plugin to your grid you mix this in to your custom grid (last)
 * and then define a filter() method
 * 
 * TODO: [Blank] and multiple filters is still a little buggy
 * 
 * @author maulin
 */
dojo.provide("medryx.widgets._GridPlugins.AutoFilter");
dojo.require("dojox.lang.aspect");
dojo.require("medryx.widgets._GridPlugins._AutoFilterTooltip");
 
dojo.declare("medryx.widgets._GridPlugins.AutoFilter", null, {
 
	constructor:function() {
		this._filterableValues = [];
 
	},
 
	/**
	 * return a list of values that this column can be filter by,
	 * sorted alphabetically
	 * @param {Object} column
	 */
	getFilterableValues: function(column) {
	   	var values = this._filterableValues[column.index];
		return this.cleanFilterValues(values);
	},
 
	cleanFilterValues:function(values) {
		var unique = {};
 
        //get rid of duplicates
        return dojo.filter(values, function(value) {
            if (!unique[value]) {
                unique[value] = true;
                return true;
            }
            return false;
        }).sort();
	},
 
	/**
	 * add a string value to the list of all filterable values
	 * for a column
	 * @param {Object} column
	 * @param {Object} value
	 */
	addFilterableValue:function(column, value) {
		var columnIndex = column.index;
		if (!this._filterableValues[columnIndex]) {
			this._filterableValues[columnIndex] = [];
		}
		if (typeof(value) !== 'undefined' && value !== null) {
			this._filterableValues[columnIndex].push(value);
		}
	},
 
	updateFilter:function(filter) {
        if (this.filter) {
			this.filter(filter, true);
		}
    },
 
	updateFilterValue:function(column, value) {
		var filterField = {};
		filterField[column.field] = value;
 
		if (!this.filterFields) {
            this.filterFields = {};
        }
        if (filterField) {
            dojo.mixin(this.filterFields, filterField); //makes any other filters "sticky"
        }
 
		this.updateFilter(this.filterFields);
	},
 
	getTooltip:function(column) {
		if (!this.tooltips) {
			this.tooltips = [];
		}
		if (!this.tooltips[column.index]) {
			this.tooltips[column.index] = new medryx.widgets._GridPlugins._AutoFilterTooltip({column:column.name});
			dojo.connect(this.tooltips[column.index], "onValueChanged", dojo.hitch(this, "updateFilterValue", column));
		}
		return this.tooltips[column.index];
	},
 
	/**
	 * set "autoFilter:true" in your cell properties to get this to work
	 * @param {Object} e
	 */
	showFilterDialog:function(e) {
        if (e.cell.autoFilter) {
			this.getTooltip(e.cell).show(e.pageX, e.pageY, this.getFilterableValues(e.cell));
        }
    },
 
	postCreate:function() {
		var originalGet = this.get;
 
		this.get = function(inRowIndex) {
	        var value =  originalGet.apply(this, arguments);
	        this.grid.addFilterableValue(this, value);
	        return value;
         };
 
		dojo.connect(this, "onHeaderContextMenu", this, "showFilterDialog");
		return this.inherited(arguments);
	},
 
	toString:function() {
		return "AutoFilter Plugin";
	},
 
});

And for the AutoFilterTooltip:

/**
 * Used by AutoFilter as a tooltip to set the filter value for a column.
 * Pass the values and coords and this will fire onValueSelected with 
 * a new filter value
 * 
 * @author maulin
 */
dojo.provide("medryx.widgets._GridPlugins._AutoFilterTooltip");
dojo.require("dijit._Widget");
dojo.require("dijit._Templated");
dojo.require("dijit.Dialog");
 
 
//TODO: upgrade select to ComboBox
dojo.declare("medryx.widgets._GridPlugins._AutoFilterTooltip", [dijit._Widget, dijit._Templated], {
	templatePath: dojo.moduleUrl("medryx","widgets/_GridPlugins/_AutoFilterTooltip.html"),
	widgetsInTemplate:true,
	column:"Column",
 
	onValueChanged:function(value) {
    },
 
	_onChange:function(e) {
		if (this.values.selectedIndex === -1) {
			value = null;
		} else {
			value = this.values.options[this.values.selectedIndex].value;
		}
		this.onValueChanged(value);
		this.currentValue = value;
		this.close();
	},
 
	/**
	 * set the values of the selector
	 * @param {Object} values, array of strings
	 */
	updateValues:function(values) {
		var sel = this.values;
		sel.options.length = 0;//reset the options array
        sel.options[0] = new Option("Any", "*");
 
        var currentValue = typeof(this.currentValue) !== 'undefined'  ? this.currentValue : "*";
		var selectedIndex = 0;
 
		dojo.forEach(values, function(value) {
			if (value === currentValue) {
                selectedIndex = sel.options.length;
            }
			if (value)
            sel.options[sel.options.length] = new Option(value, value);
		})
 
	    if (currentValue == "") {
            selectedIndex = sel.options.length;
        }
		if (currentValue === "*") {
            selectedIndex = 0;
        }
		sel.options[sel.options.length] = new Option("[Blank]", "");
		sel.selectedIndex = selectedIndex;
	},
 
	/**
	 * show this dialog at the given coordinates, values
	 * @param {Object} x
	 * @param {Object} y
	 */
	show:function(x, y, values) {
		this.updateValues(values);
		dijit.popup.open({popup:this.tooltip, x:x, y:y});
	}, 
	close:function() {
		dijit.popup.close(this.tooltip);
	}
})

June 21, 2008

Programming with Assertions — in Javascript

Filed under: dojo, javascript — Tags: , — maulin @ 6:45 pm

I’ve notice a lot of wordy and potentially expensive operations creeping into my code lately, checking my arguments to methods (especially since my current projects are intended to allow people other than me to use the code, so I want intelligent behavior and checking of usage). So there’s a lot of:

function doSomething(entityClass) {
	if (!entityClass) {
		throw new medryx.exceptions.IllegalArgumentException("MUST include entityClass");
	}
	if (!this.isManagedClass(entityClass)) {
	throw new medryx.exceptions.IllegalArgumentException("entityClass MUST be a managed class");
	}
 
	//snip...
}

In Java, I’ve really taken to programming with assertions, and so I’ve built a little utility to allow me to do the same in Javascript. I think that testing that you are in a known and predictable state while programming is one of the best ways to find bugs in your program early and stamp them out.

So, if I used assertions, my code could look like (I’ll explain why it is a bit more complicated than this in a minute):

function doSomething(entityClass) {
	assert(entityClass, "entityClass is required");
	assert(this.isManagedClass(entityClass), "entityClass MUST be a managed class");
 
	//snip...
}

By default, I throw an AssertionFailedException if the assertion evaluates to false. You can provide a 3rd argument — “throwException” and set it to false if you would prefer to just get a “console.warn()” for this. But this simplistic approach has a problem: Assertions can be computationally expensive, and you wouldn’t necessarily want them in production code. Java has a simple technique of turning off assertions so they will have almost no overhead in production code. Javascript has no equivalent.

I see two ways to easily eliminate any computational expense of executing assertions in your production code. The first is to create a build tool that erases all assert() lines. And ultimately, I suspect this is where you want to be. But lets look at a different, and perhaps simpler approach.

First off, I think it is useful in any failed assertion to know what you evaluated that failed. The easiest way to do this, would be to provide your assertion as a string, that is eval’d, and if it failed, then it would have a useful message about what failed. This comes with the ancillary benefit of not actually running your assertion until you are in the “assert” method — meaning that if you stub out your assert method with an empty function for a production environment, you would have almost no overhead from your assertions, even without erasing them in some build process.

But there is a glitch: sending a string to a method that then evals the string loses access to your local variables and context. Now we could solve this by sending a scope variable, and perhaps an array of arguments, but now the assertion is becoming a complicated beast, and the orginal code might actually be easier to use.

In my solution, I gain the benefit of never having to evaluate my expression unless I am in the actual assert method, so I can stub it out easily. I also gain the benefit of complete access to all local variables and scope without any additional coding. We just change the syntax slightly:

function doSomething(entityClass) {
	assert(assertion("entityClass", "entityClass is required"));
	assert(assertion("this.isManagedClass(entityClass)", "entityClass MUST be a managed class"));
 
	//snip...
}

That is not too much uglier than a simple “assert” method, and using the “assertion” method inside the assert, you get to provide your expression as a string and gain the benefits listed above.

I made assert/assertion global functions to make them as easy as possible to use.

Now lets look at my assert.js file:

dojo.provide("medryx.assert");
 
assert = function() {
}
 
assertion = function() {
}
 
dojo.require("medryx.util.assert"); //in a build, you just comment out this line to stub out your assertions

So, you can see that by default, the assertion does nothing, and there is essentially no cost to your code, even without using a build tool. But if you keep the final “dojo.require” statement — then “assert” is over-ridden by the actual implementation, which looks like this:

/**
 * BSD license 
 * @author maulin
 */
dojo.provide("medryx.util.assert")
dojo.require("medryx.exceptions.AssertionFailedException");
 
 
assert = eval;
//in non-production environment, assert = eval, but in production, it would = function() {}
 
assertion = function(/* string |  Boolean */expression, /* string? */message, /*Boolean? default TRUE*/throwException) {
	//  summary: assertion for testing that you are in the state you expect in your method
	//  description: assertion based programming can root out bugs very quickly in the development
	//     cycle. use this liberally and throughout your code. in a production
	//     build you can easily stub this out, or you could even use post-processing
	//     of your javascript files to erase all the calls to assert\s*(.*?);
	//  expression: 
	//     String: a STRING that should be eval'd. A string because if you stub this
	//       out, then the expression won't need to be eval'd. also, it gives you more
	//       information about your assertion. your expression is executed
	//       via the "assert" call, in your context, so you have access to all your current
	//       locally available variables
	//  message: optional string about your assertion
	//  throwException: TRUE by default, if false, will simply create a console.warn() condition
 
	message =(typeof(message) === "undefined") ? "" : message; //i will need to escape the quotes
	throwException = (typeof(throwException) === "undefined") ? true : throwException;
 
	var assertFunction = "(dojo.hitch(this, function(expression,  message, throwException){" + 
	   "var	result = eval(expression);" +
 
	"if (!result) {" + 
		"if (throwException !== false) {" + 
			"throw new medryx.exceptions.AssertionFailedException(expression, message);" +
		"} else {" +
			"console.warn('Assertion Failed: ' + message + ' for ' + expression);" +
		"}" +
	"}" +
 
	"return result;" +
	"}, \"" + expression.replace(/"/g, "\\\"") + "\", \"" + message.replace(/"/g, "\\\"") + "\", " + throwException + "))();";
 
 
	return assertFunction;
}

So, seeing that this code is not supposed to get executed in a build environment, you obviously wouldn’t want to perform any “action” in an assertion. In Java, you actually aren’t supposed to use assertions for checking arguments of public methods either, but I think Javascript is a little different, and it would be okay to use them in this manner.

June 20, 2008

An Object-Relation-Mapper (ORM) for Javascript? Well, kind of…

Filed under: MedryxORM, dojo, javascript — Tags: , , , , , , , , — maulin @ 7:13 am

A while back, I sent a post to the Dojo discussion forum regarding lazy loading and relationships between entities. The very simple response by Alex:

stores don’t “do” references…

Aha! That was the part I hadn’t figured out. Ever try to open a phillips-head screw with a flat-head screwdriver? It felt a bit like that. Over time, I came up with many solutions, but never a comprehensive one that I could use in multiple projects. I came up with several design patterns, and perhaps some re-usable code, but it all still felt like a hack.

My path towards a more comprehensive solution to managing related entities came from the server world with a similar, but more lightweight relational model I started to explore with this post I made a little while back. Well, many all-nighters later, I think I have a 0.1 alpha version of a cool system that makes a relational, lazy enabled, dirty-checking javascript object model a piece of cake. And there is plenty of sugar to make it fun to use. Oh, and its dojo.data compatible.

(As an aside, I was excited when I first read about the JsonRestStore.. Indeed, it probably does a lot of this same work. So it may be an issue of style. I also don’t know a whole lot about JSON referencing, and my referencing model (to be documented) seemed a lot easier to translate on the server side. But as I said, I need to learn a lot more about it. I hope to start here.)

Having done quite a bit of work with Java in the past, I am big believer in Object-Relational-Mappers on the server side. I have used Hibernate most extensively, and have even dabbled with PHP-based ORM’s like Propel. I like the idea of creating a model, and letting the system figure out how to persist it.

In the client-side world of Javascript, obviously a full-fledged ORM would be overkill. But I think an analogy of an ORM on the server-side — which maps relational-database information to domain-objects — can be made. The only difference is that you are mapping your server communication to your domain objects. I’m not sure what to call this yet, but I am open to suggestions. For now, I’ll borrow “ORM”, though technically that is not what this is. Perhaps client-server object mapper? (CSOM?)

What is MedryxORM?

MedryxORM is an extension of the Dojo toolkit that allows for simple communication between your client-server services and your javascript domain model. It consists of an EntityManager that handles all the mapping of your simple javascript-objects to your javascript-services and can be used to query and persist your domain objects. Similar to Hibernate, this system aims to be as unobtrusive to your usual coding as possible, but provides access to several ways in which you can optimize the performance of your application once you have it working, making refactoring of your application for performance a much simpler process. There are several “shortcuts”, that make using the system easier, but may be a bit more intrusive on your usual coding practices, so they are optional.

Enough jargon. Lets do some coding, and then I’ll reveal how it all works. Since I am a physician, my domains almost always deal with medical records, so I’ll use that as an example. Lets say that we want to build an application that shows a table of patients. You can then drill-down on patient details like insurance information, clinical information, etc. You can add simple notes to the patient, and perhaps assign a responsible physician/team of physicians. You can then page the appropriate responsible physician.

So lets first describe out domain objects in javascript. Here is a model.Patient:

dojo.declare("model.Patient", null, {
  //id
  id:null,
 
  //simple properties
  fullName:null,
  initials:null,
  medicalRecordNumber:null,
  birthdate:null,
  gender:null,
 
  //associations
  visit:null
 
  toString:function() {
    return this.fullName || this.getFullName(); //I'll explain this later.    
  },
 
  getAge:function() {
  //snip... uses birthdate to calculate an age
  }
});

And here is a model.Visit:

dojo.declare("model.Visit", null, {
  //id
  id:null,
 
    //simple properties
  accountNumber:null,
  admissionDate:null,
  admissionTime:null,
  dischargeDate:null,
    dischargeTime:null,
  admittingDiagnosis:null,
    bed:null,
  room:null,
  ward:null,
 
  coverageNotes:null,
 
  //associations
  team:null,
 
  //collection of insurances
  insurances:null,
 
  getLengthOfStay:function() {
    //snip -- calculates number of days in the hospital based on the admission date.
  }
});

Well, you get the idea. Nothing very exciting here. But now let’s “register” these class with the EntityManager:

 
entityManager.registerClass({
  entityClassName:"model.Patient",
  alias:"Patient"
});
 
entityManager.registerClass({
  entityClassName:"model.Visit",
  alias:"Visit"
});

I’ll get into mapping and configuring the EntityManager in just a minute. But first lets jump ahead and see how you can use this:

 
var patient = new Patient(10);
alert( patient.getFullName() + " is " + 
      patient.getAge() + " years old!" );

Now, as long as their is a patient whose id = 10 on the server, then this will all *just work*. There is NO other necessary calls to xhr, or rpc, or REST, or anything else (Though those things happen behind the scenes, depending on how you configure your EntityManager.)

This might be even cooler:

 
var patient = new Patient(10);
 
alert ( "Uh-oh, " + patient.getFullName() + " has been here for " + 
    patient.getVisit().getLengthOfStay() + " days!" );

Again, there is no further coding required. And because you map your server data to your javascript objects, you can actually use methods declared on those objects as you normally would. This example would result in two calls to the server — one to get the patient, and the next to get the visit (though that would not actuall occur until the getLengthOfStay() method called getAdmissionDate()). But that is a default behavior. You could give the server some “hints” about how you will be using the Patient, and let it send you all the information you need on the first call. By default, the code above would behave use synchronous data access methods, but its a very simple tweak to convert this exact same code to friendly asynchronous calls. But all in due time…

Now, admittedly, this is a contrived use. How often will you be instantiating an object with an identifier and expecting it to connect to some backend? But a more realistic use-case would be to use the fetch/query methods of the EntityManager (using fetch if you want to use dojo.data style access, or query if you want to use my Deferred-style access). Once you make the query, your results are all instances of your object model, with all of the appropriate relationships initialized in all the appropriate ways.

var deferred = entityManager.query({query:{entityClass:"Patient", fullName:"Smi*"});
deferred.addCallback(function(patients) {
  dojo.forEach(patients, function(patient) {
 
    document.write ( patient.getFullName() + " is " 
        + patient.getAge() + " years old!" );
 
  });
})

And in terms of persistence, you can choose from a number of different options. Some people prefer the DAO design pattern, others prefer ActiveRecord. I think this (sort of) gives you both. Or you can choose to completely forget about persistence, and let it all happen automagically (flushing on unload).

 
var patient = new Patient(10);
patient.setMedicalRecordNumber("123456");
patient.save();

or…

 
var patient = new Patient(10);
patient.setMedicalRecordNumber("123456");
 
entityManager.saveEntity(patient);

or…

var patient = new Patient(10);
patient.setMedicalRecordNumber("123456");
 
entityManager.save(); //flush all dirty records to the server

You are guaranteed to have only a single instance representing each persistent entity. So, this works fine (not that it has much purpose):

var patient = new Patient(10);
 
var visit = patient.getVisit();
 
var deferred = entityManager.query({query:{entityClass:"Visit", patient:10}});
deferred.addCallback(function(results) {
    assertTrue ( visit === results[0] ) ;
});
 
//or...
 
var visitId = visit.getId();
var newVisit = new Visit(visitId);
 
assertTrue ( visit === newVisit );

I’ll get into the details of optimization, caching, and other goodies later. Now that you’ve seen a little of the magic, lets dive into how its all configured.

At the core of the EntityManager is the class metadata — or “mapping”, inspired by Hibernate. You can specify your mapping in two ways — as a JSON object or “natively”. Your choice metadata is essentially stylistic, and is equivalent to the Java argument of Annotations based configuration versus XML based configuration.

To start with, lets define some terms, to make writing this a bit easier:

  • Managed Class: any class that is registered in the EntityManager
  • Managed Entity: an instance of a managed class
  • Managed Property: any property on your Managed Class that is managed by the EntityManager (Having unmanged properties in your ManagedClass is certainly allowed, but keep in mind that the EntityManager will totally ignore unmanaged properties.)

There are several types of ManagedProperty’s already built, and you are free to create your own to implement novel behavior.

  • medryx.orm.Identifier: in this implementation, only numeric identifiers are allowed, and there must be one and only one Identifier per Managed Class
  • medryx.orm.Property: used for any property of a Managed Class that is not an association — usually scalar values like numbers, strings, Dates, etc.
  • medryx.orm.Association: any association to another Managed Entity. Note that this must be an association to a Managed class, otherwise you use Property above
  • medryx.orm.Collection: a collection of Managed Entities. Again, this is inteded to be used for associations to elements of a Managed class only.

The details of each of the ManagedProperty types will be more fully discussed a bit later. Let look at a native mapping for a Patient. You could either create a subclass of patient, or just change your Patient class itself, which is the approach I will show here for simplicity.

dojo.declare("model.Patient", null, {
  //id
  id:new medryx.orm.Identifier(),
 
  //simple properties
  fullName:new medryx.orm.Property(),
  initials:new medryx.orm.Property(),
  medicalRecordNumber:new medryx.orm.Property({readOnly:false}), //this is a mutable field (not in real life, but for this example it is)
  birthdate:new medryx.orm.Property({converter:medryx.orm.converters.DateConverter}),
  gender:new medryx.orm.Property(),
 
  //associations
  visit:new medryx.orm.Association(