Medryx Observations

June 27, 2008

My PC is dead — may it rest in peace…

Filed under: gwt — Tags: , , , , — maulin @ 9:31 pm

So my PC is dead. Well, not really dead, but I am sick of it. I am sick of Windows. I am sick of reformatting every year to make it perform. Its time for a new box. And I just ordered a dream machine. So, in the meantime, I figured I would waste some time.

So I decided to play a bit with the Google Web Toolkit. There were really a few motivations here.

  • Several recent posts on the dojo-interest mailing list about Dojo/GWT integration.
  • I have been somewhat dissatisfied with my options for a Javascript IDE. I use Aptana on Eclipse. It is very comfortable, because I have been using Eclipse for Java forever. But it really is a glorified syntax-coloring text editor when used with Dojo. Komodo is very impressive, but it has one tragic flaw — it only exists in a single window. I have 3 monitors, and it makes me cry when I have to stretch a single window across more than one just to make it feel right.
  • GWT could leverage my already existing Java knowledge

So, while I wait for the dream machine to arrive, its time to dabble… Wish me luck!

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({associationClass:"model.Visit"});
 
  toString:function() {
    return this.fullName || this.getFullName(); //I'll explain this later.    
  },
 
  getAge:function() {
  //snip... uses birthdate to calculate an age
  }
});

and Visit is altered as follows:

dojo.declare("model.Visit", null, {
  //id
  id:null,
 
    //simple properties
  accountNumber:new medryx.orm.Property(),
  admissionDate:new medryx.orm.Property(),
  admissionTime:new medryx.orm.Property(),
  dischargeDate:new medryx.orm.Property(),
    dischargeTime:new medryx.orm.Property(),
  admittingDiagnosis:new medryx.orm.Property(),
    bed:new medryx.orm.Property(),
  room:new medryx.orm.Property(),
  ward:new medryx.orm.Property(),
 
  coverageNotes:new medryx.orm.Property({readOnly:false}),
 
  //associations
  team:new medryx.orm.Association({associationClass:model.Team}),
 
  //collection of insurances
  insurances:new medryx.orm.Collection({associationClass:model.VisitInsurance}),
 
  getLengthOfStay:function() {
    //snip -- calculates number of days in the hospital based on the admission date.
  },
 
  getRoomNumber:function() {
    return this.getWard() + "-" + this.getRoom() + "-" + this.getBed(); //always use getters to allow lazy initialization if needed
  }
});

Okay, so you are almost done with your configuration. (Obviously I’ve left model.Team and model.VisitInsurance out for brevity, but they are just as simple to “annotate”).

The next step is to plug in your PersistenceService. MedryxORM defines a PersistenceService interface that looks like:

dojo.declare("medryx.orm.PersistenceService", medryx.AbstractBase, {
 
    /**
     * the deferred calls back with an object:
     * {
     *    total: total number of records available for this query, or -1 if unknown
     *    items:array of items
     * }
     * returns a deferred (always)
     * 
     * @param {Function | String} entityClass
     * @param {Object} kwArgs (see ReadApi... still need to document queryOptions)
     * @param {Boolean} sync
     */
    $fetch:null,
 
    /**
     * returns a deferred (always) that callsback with 
     * the id if successful
     * 
     * if no id is null or < 0, then this is a put, otherwise it is
     * a post
     * 
     * @param {Function | String} entityClass, 
     * @param {Integer} id, 
     * @param {Object} propertiesToPersist
     */
    $save:null,
 
    /**
         * similar to WriteApi deleteItem
         * 
         * returns a deferred (always)
         * 
         * @param {Function | String} entityClass, 
     * @param {Integer} id
         */
        $deleteItem:null,
 
    /**
     * given an entiyClass, id, and properties, full load an instance 
     * that guarantees that each property in properties is initialized
     * (or if no properties are specified, then some contract
     * between client and server about "default" loading has
     * been fulfilled)
 
     * @param {Function | String} entityClass
     * @param {Integer} id
     * @param {Array of Strings} properties -- this is a list of properties that the 
     * server GUARANTEES will be present in the returned entity. The server *MAY* and 
     * frequently *WILL* return more than just these properties, and the EntityManager 
     * will handle that case appropriately and consume all the information the server sends
     * each item in the array may be either a property name, a dot-notated association property
     * or a *
     * for example: ["*", "visit.accountNumber", "visit.team.*"] would load all the properties
     * of the entityClass with id, the visit association with the account number loaded, and 
     * the team of the visit with ALL of its properties loaded. 
     * @param {Boolean} sync
     */
    $load:null
});

We also provide an RpcPersistenceService that gets rid of sync handling for you. You could easily create an SMD to your server that meets the requirements of this interface.

You provide a default PersistenceService to the EntityManager. If you do not specify a specific PersistenceService for a given Managed Class, then the default PersistenceService is used. However, it may be easier for you to implement a seperate SMD for each Managed Class, so you can supply a seperate PersistenceService for a particular class (or for every class). My server can easily interpret the “entityClass” and determine what to send back to the client, but if you wanted to make your server more generic, you may choose to ignore the entityClass on the server and let that mapping occur on the client. It would be extremely easily to build a REST PersistenceService (and I will probably do that when the need arises) into which you can adapt a Rest service to fit the PersistenceService API.

Let make this a bit less abstract. As I said, my server is pretty smart, so I it knows the right thing to do with entityClass. So I have a single PersistenceService that I specify when I “startup” my EntityManager (i.e. instantiate it).

ormPersistenceServiceSMD = {
  serviceType:"JSON-RPC",
  serviceUrl:"mySmartServer.php",
  methods: [
    {
      name: "load"
      parameters:[
        {name:"entityClass", type:"string"}
        {name:"id", type:"integer"}
        {name:"properties", type:"array"}
      ]
    },
    //snip... you get the idea
  ]
}

Then I can plug that into my EntityManager with a couple of wrappers:

var rpcPersistenceService = new medryx.orm.RpcPersistenceService(new dojo.rpc.JsonService(ormPersistenceServiceSMD));
var entityManager = new medryx.orm.EntityManager(rpcPersistenceService);
 
entityManager.registerClass({entityClass:"model.Patient", alias:"Patient"});
entityManager.registerClass({entityClass:"model.Visit", alias:"Visit"});
 
//and now everything above works!
 
var patient = new Patient(10);
patient.getVisit().setCoverageNotes("Some new coverage notes");
 
patient.save();

Woo-hoo! I obviously have a LOT of work to do in terms of documentation. But this is a start! Please, tell me what you think!!! I’ve posted the source on Google code. Maybe if I get lucky, some Dojo folks will take an interest, and we could be incubated as a dojox project! One can dare to have dreams, right?!

June 18, 2008

Pseudo-RESTful

Filed under: Uncategorized — maulin @ 10:40 am

I have to admit it: I spent the last week rolling my own data store that handles lazy loading that I thought was going to be the greatest thing since… well anyway. Then I read this , and realized that there was no need to write almost any of what I had written. I hate when that happens.

In any case, the new JsonRestStore does just about everything I wanted to do, only more efficiently. My only problem is that my server, which I have no control over, has blocked “PUT” and “DELETE”. But really, that seems minor. I mean, couldn’t we just include a flag to our REST server telling it what action we are expecting? Yes. We can.

A detailed look into dojox.rpc.Rest reveals that the _change method is where the final call to xhr occurs. Now I know this is unsafe since this is a private method, but its a good quick fix for now. And perhaps I can convince the dojo folks after 1.2 is official that they should support this kind of behavior for “Pseudo-REST” services, right out of the box.

Here is the code.

/**
 * 
 * uses post for all post/put/delete, but adds and attribute called action
 * 
 * @author maulin
 * 
 */
dojo.provide("medryx.stores.ModifiedRest");
dojo.require("dojox.rpc.Rest");
 
(function() {
 
  //copied verbatim from dojox.rpc.Rest -- it is local in scope inside Rest
 //and called from the _change method, so I need it (for now)
  function index(deferred, service, range, id){
        deferred.addCallback(function(result){
            if(id==""){
                service._rootQueries.push(result);
            }
            if(range){
                // try to record the total number of items from the range header
                range = deferred.ioArgs.xhr.getResponseHeader("Content-Range");
                deferred.fullLength = range && (range=range.match(/\/(.*)/|>)) && parseInt(range[1]);
            }
            return service.cache.intake(result,id);
        });
        return deferred;
    }
 
  //"over-rides" the actual xhr call to use POST and add action attribute to querystring
  dojox.rpc.Rest._change = function(method,service,id,content){
        // this is called to actually do the put, post, and delete
        var request = service._getRequest(id);
        request[method+"Data"] = content;
 
        //ADDED THE FOLLOWING TWO LINES
        request.data += "&action=" + method.toLowerCase();
        request.url += (request.url.indexOf('?') > 0 ? "&" : "?" ) + "action=" + method.toLowerCase();
 
        //CHANGED THIS LINE TO ALWAYS USE POST
        return index(dojo.xhr("POST",request,true),service);
    };
 
})();

June 9, 2008

A Javascript Object Model and Lazy Initialization

Filed under: dojo, javascript, oop — Tags: , , , , — maulin @ 9:06 pm

I’ve been thinking a lot about javascript client-side object models lately. I use dojo for my javascript oop tools, so creating classes, and inheritance is not really the problem. The problem is figuring out how to make as transparent as possible a rich relational object model. Then to figure how to do automatic dirty checking and updating.

The needs of a client side object model seem to be slightly different than those on the server. And obviously the client is more resource constrained. So we really only want to get the information we absolutely need for the client to run. But on the other hand, creating a single set of objects that can be used anywhere in our application, would obviously also be of benefit. So how to solve this?

Its all a question of optimization versus lazy-loading. If we use sparsely populated objects, and then lazy load the remaining properties on demand, then perhaps we will have the solution. We will be able to create a single object model, and use it reliably on every page of the application. Then as a performance optimization later — we can make the “laziness” disappear by populating the object less sparsely, or targeting which parts of the object model are loaded intially, and which are loaded lazy.

But if we want to create a system where we are always interacting with an object in the same way, and letting the performance optimizations occur in the background, then we need to define how exactly we are going to interact with the object.

So lets start walking through an example. Consider a Person object:

dojo.declare("medryx.model.Person", null, {
  firstName:null,
  middleName:null,
  lastName:null,
  roles:null
});

Now first things first. There is no way I can think of that we are going to be able to support direct property access of these properties and have any ability to instrument our entity to take care of performance optimization, dirty checking, and so-forth in the background. And since support for javascript property getters and setters is sparse to say the least, I think right off the bat, we are going to be left with using get and set methods for properties, ala javabeans. Don’t worry though, I am not saying you will need to necessarily write all those getters and setters. I’m just saying to access properties reliably, you will need to use them. Javascript is such a flexible language that we can handle creating these getters and setters automagically.

My standard model for instantiating an object is to pass in an associative array of properties and initialize each of the properties automatically. We could use our constructor that initializes the properties also create the getters and setters without to much additional effort. We could event handle “private” properties that do not need getters and setters. If we put this in a base class for all of our entities, we would have:

dojo.declare("medryx.model.Entity", null, {
constructor:function() {
  if (arguments[0]) { //mixin kwArguments if present, which is only valid argument to constructor of an Entity
    dojo.mixin(this, arguments[0]);  
  }
  //setup getters
  for (var key in this){
    if (key[0] !== "_") { //ignore underscore-prefixed properties
      if (!dojo.isFunction(this[key])) { //ignore functions
        this.setupGetter(key);
        this.setupSetter(key);
      }
    }
  }
},
setupSetter:function(key) {
  var setterName = "set" + medryx.util.StringUtils.capFirst(key);
  if (!dojo.isFunction(this[setterName])) { //don't use an auto-getter if a getter is already defined (allows override)
    //assume this is a property and needs a setter
    this[setterName] = dojo.hitch(this, "set", key);
  }
},
setupGetter:function(key) {
  var getterName = "get" + medryx.util.StringUtils.capFirst(key);
  if (!dojo.isFunction(this[getterName])) { //don't use an auto-getter if a getter is already defined (allows override)
    //assume this is a property and needs a setter
    this[getterName] = dojo.hitch(this, "get", key);
  }
},
get:function(property) {
  return this[property];
},
set:function(property, value) {
  this[property] = value;
}
});

and then we change person to inherit from Entity:

 
dojo.declare("medryx.model.Person", medryx.model.Entity, {
  id:null,
  firstName:null,
  middleName:null,
  lastName:null,
  roles:null
});

A quick walk-through of Entity shows that it iterates through all the properties that exist on the entity that do not start with a “_”. For each of these it creates a getter and a setter. It also mixes in a the properties from the initialization argument. Notice also that if a getter already exists, then the default getter is not used. So if you had some business logic you wanted to put into a getter or setter of your own, you
could simply create your own getProperty() method and it would not be over-ridden.

So our first cut is done. Let’s see how it works:

  Person person = new medryx.model.Person({firstName:"Maulin", roles:["super-user", "dev-user"]);
  div.innerHTML = "Hello " + person.getFirstName() + 
    "!" + " I see you have the following roles: " +
    person.getRoles().join(", ") + ".";

Simple enough. So how has this helped us? Well, it hasn’t. Yet. But now lets say that istead of explicitly instantiating the person with values, that we were loading a person via JSON in an ajax call to the server. Something like:

var deferred = loadPeople();
var people;
deferred.addCallback(function(peopleProperties) {
  people = dojo.map(peopleProperties, function(personProperties) {
    return new medryx.model.Person(personProperties);
  });
});

Cool. With very little work, I now have locally cached “people” store that I could easily build some query, sort, and filter functions for. (I’ll talk about that in a later article).

So now lets get back to the idea of lazy loading. Lazy loading can operate in two paradigms — synchronous and asyncrhronous. The advantage of synchronous is that is a very comfortable programming model. You really don’t change anything other than Entity, and you have lazy loading without changing any application code.

As an example, lets say that our call to loadPeople() above return an array of associative arrays that had firstName, middleName, and lastName for each person. But lets say roles was not sent down from the server. Now when we make a call to getRoles(), we get null, which is obviously not what we want. So somehow, getRoles() needs to know that its dealing with a lazy property.

One way would be to say that if a property === null, then try to load it lazily before returning from the getter. But what if the value is supposed to be null? Okay, then how about if we create “fake” value for any property that has the ability to be lazy loaded? Then the system could check if the property has been initialized or not, and if not, it can perform a lazy load.

So lets change our Person class a bit:

dojo.declare("medryx.model.Person", medryx.model.Entity, {
  id:null,
  firstName:null,
  middleName:null,
  lastName:null,
  roles:medyrx.model.Entity.LAZY
});

now that we have marked roles as LAZY, lets change our Entity to account for it:

setupGetter:function(key) {
  var getterName = "get" + medryx.util.StringUtils.capFirst(key);
  if (!dojo.isFunction(this[getterName])) { //don't use an auto-getter if a getter is already defined (allows override)
    //assume this is a property and needs a getter
    //check if this property is lazy
    var isLazy = (this[key] === medryx.model.Entity.LAZY); 
        //note that if this property was already initialized by the mixin in the constructor, that it would no longer be lazy
     if (!isLazy) {
        this[getterName] = dojo.hitch(this, "get", key); //default behavior
     } else {
       if (!dojo.isFunction(this.lazyLoadProperty)) {
        throw new Error("Entities with lazy properties MUST implement lazyLoadProperties");
       }
       this[getterName] = dojo.hitch(this, "getLazy", key, false);
     }
  }
},
getLazy:function(property) {
  if (this[property] === medryx.model.Entity.LAZY) {
    this.lazyLoadProperty(property); //a SYNCHRONOUS CALL that when it returns will have initialzed the property
  }
  return this[property];
}

So this code will check if a property is lazy. If it is, it will shunt the getter over to getLazy(). getLazy() will check if the property has been initialized, and if it has not, then it will ask the subclass to initialize it. Since this would be a synchronous call, the end result would be that a call the getRoles() would return the roles, just as it did before.

Finally, for completeness, lets look at the person object one last time:

dojo.declare("medryx.model.Person", medryx.model.Entity, {
  id:null,
  firstName:null,
  middleName:null,
  lastName:null,
  roles:medyrx.model.Entity.LAZY,
 
  //private:
  _allProperties:null,
 
  lazyLoadProperty(property) {
    if (!this._allProperties) {
      !this._allProperties = medryx.dao.loadCompletePerson(this.id); //SYNCHRONOUSLY laod all the properties for this person
    }
    this[property] = this._allProperties[property];
  }
});

While this implementation works fairly well for simple unininitialized properties, we now need to consider how we would handle related objects. For example, lets give a person an account object.

dojo.declare("medryx.model.Person", medryx.model.Entity, {
  id:null,
  firstName:null,
  middleName:null,
  lastName:null,
  roles:medyrx.model.Entity.LAZY,
  account:null,
 
  //private:
  _allProperties:null,
 
  lazyLoadProperty(property) {
    if (!this._allProperties) {
      !this._allProperties = medryx.dao.loadCompletePerson(this.id); //SYNCHRONOUSLY laod all the properties for this person
    }
    this[property] = this._allProperties[property];
  }
});

When we are initializing the person, we will only get an accountId from the server. We could add some custom work into the Person constructor to instantiate the team:

dojo.declare("medryx.model.Person", medryx.model.Entity, {
  id:null,
  firstName:null,
  middleName:null,
  lastName:null,
  roles:medyrx.model.Entity.LAZY,
  account:null,
 
  //private:
  _allProperties:null,
 
  //constructor:
  constructor:function(args) {
    if (args && args.accountId) {
      this.account = new medryx.model.Account({id:args.accountId, person:this});
    }
  }
 
  lazyLoadProperty(property) {
    if (!this._allProperties) {
      !this._allProperties = medryx.dao.loadCompletePerson(this.id); //SYNCHRONOUSLY laod all the properties for this person
    }
    this[property] = this._allProperties[property];
  }
});

And if account looks like:

dojo.declare("medryx.model.Account", medryx.model.Entity, {
  id:null,
  person:null,
  lastLogin:medryx.model.Entity.LAZY,
 
  lazyLoadProperty(property) {
    //to be implemented with a SYNCHRONOUS loader
  }
});

Then if I want to get the lastLogin I could write:

div.innerHTML = person.getAccount().getLastLogin();

and everything would *just work*! But the devil is always in the details. If you use this technique, you really need to think about how much data to supply to your objects in advance with enough data so that these lazy initializations are kept to a minimum. Synchronous calls to the server are by definition a bad thing.

Or alternatively, we can change our design to a slightly more complicated one, that handles lazy initialization more gracefully with ansynchronous calls. That will be the topic of my next post.

Building (and Testing) Dojo Widgets — MVC?

Filed under: dojo, javascript, oop, unit testing — Tags: , , , , , — maulin @ 8:04 am

There are plenty of tutorials out there about Dojo widgets, so I won’t write another one. But I would like to document here my philosophy for writing widgets, allowing for separation of concerns and perhaps even an MVC architecture.

In my mind, a widget is a hybrid of view component and model component. It builds a UI, then receives events on that UI that it passes on the model. But this can get messy, especially for testing, debugging, and changes later on down the road. So I propose that we separate the widget into its elemental components, and then only combine them in the “final widget”. Here’s what I mean:

Lets create widget called TeamList, that creates a list of teams. When you click on a team you get a dialog with team details.

/widgets
---/ui
------/tests
---------/all.js
---------/runTests.html
---------/TeamListTest.js
------/_TeamList
---------/TeamList.html
---------/TeamListView.js
---------/TeamListController.js
---------/tundra.css
------TeamList.js
---/themes
------/tundra.css

As you can see, I actually seperate my widgets into several files:

  1. TeamList.html: the standard widget template
  2. TeamListView.js: a class that DOES NOT derive from the usual _Widget or _Templated, but that “pretends to”.

    This class is used to manipulate everything related to the UI, and to create the events that the widget will generate (or translate the low level user events to more useful ones for the application).

    The key is that none of the “high-level” events will trigger ANYTHING to happen in the model. That is the job of the controller.

    This makes this class quite isolated, and fairly easy to test and change, as long as it adheres to its contract of high-level events that it will trigger.

    Here is the code for my TeamListView:

     
    dojo.declare("medryx.widgets.ui._TeamList.TeamListView", medryx.AbstractBase, {
        //*** attributes
        //filter the teams
        filter:null,
     
        //sort the teams. this can be an array of sort objects (see AbstractDao.sort)
        //or can be a comma-seperated list of fields (simpler, but less flexible)
        sort:null,
        headerContent:"",
    	footerContent:"",
     
        //*** internal
        templatePath: dojo.moduleUrl("medryx","widgets/ui/teamList/teamList.html"),
        controller:null,
        teams:null,
     
        //** nodes defined in the template -- only defined here so we remember them
        list:null,
     
        /**
         *  abstract method
         *  MUST return a valid initialized controller
         */
        $buildController:null,
     
        postCreate:function() {
            this.controller = $buildController();
            this.loadTeams({
                filter: this.filter,
                sort: this.sort
            });
        },
       /**
         * delegate to the controller.
         */
        loadTeams:function() {
            var deferred = this.controller.loadTeams(arguments);
            deferred.addCallback(dojo.hitch(this, this.displayTeams, this.list);
        },
       displayTeams:function(list, teams) {
    	dojo.forEach(teams, function(team) {
    		var li = document.createElement("li");
    		li.innerHTML = team.getName();
    		list.appendChild(li);
    		dojo.connect(list, "onclick", dojo.hitch(this, "teamClicked", team));
    	});
       },
       teamClicked:function(team, event) {
       }

    So lets point out some interesting things about this “View”. Notice that it does not do anything about actually loading teams, or handling what happens once a team gets clicked. It just translates the low level click event to a high level teamClicked event that passes the team of interest to the event.

    Next, notice that while we implement postCreate(), we are not inheriting (yet) from _Widget or _Templated. Here’s why. Lets say I want to test this class. If it had already inherited from these classes, it would automatically call the lifecycle events of the class. But I don’t necessarily want that to happen… yet. I want to call my postCreate method manually and run my tests to be sure everything in this class is working right, before I start wiring things up.

    One other small point — look at displayTeams. Notice how I actually pass the list to which I want to attach the items to this method. Obviously I could use “this.list” throughout displayTeams, and then I wouldn’t have to pass the argument. But stylistically, I find it easier to write tests for methods that are “self contained” and have as little reference to “this.*” as possible. If I hadn’t passed the list as a parameter, I would have had to remember in my tests to set this.list to a new list before running my test. And you can easily forget to do that. In other words, I try to make my widgets as “stateless” as possible in the View. I like to let the final widget inject all the state that is necessary.

    Here is the (very simple) controller class:

    dojo.declare("medryx.widgets.ui._TeamList.TeamListController", null, {
        view:null,
        constructor:function(view) {
            this.view = view;
            dojo.connect(view, "teamClicked", this, "pageTeam");
        },
     
     /**
      * return a deferred that will callback for all teams
      * that are to be display. the arguments are filter and sort
      * either as parameters or as kw arguments
      */
        loadTeams:function() {
            return medryx.dao.TeamsDao.getAll(arguments);
        },
     
        pageTeam:function(team) {
            var pageForm = new medryx.widgets.ui.PageForm();
            pageForm.setTeam(team);
            pageForm.show();
        }
    });

    This is where I connect this widget to other layers of the application. So all interactions outside of the widget should occur through this controller. Notice how I connect up the “teamClicked” event to a “pageTeam” function. Another advantage of separating the view from the controller is that allI have to do is a use a different controller, and I can get a totally different widget. Again, this is a fairly easy class to test .

    So where does this all come together? In medryx.widgets.ui.TeamList, of course!

    dojo.declare("medryx.widgets.ui.TeamList", [dijit._Widget, dijit._Templated, medryx.widgets.ui._TeamList.TeamListView], {
    	$buildController:function() {
                 return new medryx.widgets.ui._TeamList.TeamListController	(this);
              }
    });

    All this does is create the actual widget class, mixin the _Widget, _Templated to allow the magic of dojo to take over, and to define the controller that will be used.

Simplified Dojo D.O.H. Testing

Filed under: dojo, javascript, oop, unit testing — Tags: , , , , , , — maulin @ 7:19 am

In my last post, I talked about how to use Dojo’s DOH for unit testing. But the more I wrote about it and used it, the more I realized that there were a couple of things that made it difficult for me to wrap my head around.

First, setUp and tearDown were not similar to their counterparts in other unit-testing frameworks, such as JUnit.

Secondly, the whole model for programming an asynchronous test seemed a bit confusing.

So I have come up with what seems to me to be a ridiculously simple solution — a custom “test fixture”.

As you recall, when registering a set of tests, you pass the name of the set followed by an array of test methods or test fixtures. The methods are simple function calls. The test fixture had the following properties:

  1. name
  2. runTest
  3. timeout
  4. setUp
  5. tearDown

In all the examples I found, this was declared as an associative array, setting each of these properties.

So a typical test looked like:

doh.register("some.test.package", [
function testOne() {
},
{
	name:"test2",
	setUp:function() {
		doSomeSetup();
	},
	tearDown:function() {
		doSomeTearDown();
	},
	runTest:function() {
		doh.assertTrue(doSomethingTrue());
	}
},
{
	name:"test3",
	setUp:function() {
		doSomeSetup();
	},
	tearDown:function() {
		doSomeTearDown();
	},
	runTest:function() {
		doh.assertTrue(doSomethingElseTrue());
	}
}
]);

But what if instead we write a class that implements all the above properties? Then we could subclass that class for a particular set of tests that shared a setUp/tearDown series. So in my first cut, I wrote a simple TestFixture class that looks like this:

dojo.declare("medryx.util.test.TestFixture", null, {
	constructor:function(testName, test) {
		this.name = testName;
		this.runTest = test;
	}
});

Okay, so its not rocket science. But already I feel like it cleaned up my tests substantially. Because now I have:

dojo.declare("some.test.fixture", medryx.util.test.TestFixture, {
	setUp:function() {
		doSomeSetup();
	},
	tearDown:function() {
		doSomeTearDown();
	}
});
 
doh.register("some.test.package", [
function testOne() {
},
new some.test.fixture("test2", function() {
	doh.assertTrue(doSomethingTrue());
}),
new some.test.fixture("test3", function() {
	doh.assertTrue(doSomethingElseTrue());
})
]);

Well that’s a start. But that got me thinking. Could I use this to simplify asynchronous tests?

As I discussed in my previous post, there is essentially a template for doing asynchronous testing:

  1. Create a doh.Deferred
  2. Do your deferred thing
  3. Attach the doh.Deferred.getTestCallback(), instantiated with your own assertions, to you deferred thing’s callback/errback
  4. Return the doh.Deferred

So I wrote a new and improved fixture:

/**
 * Use this test fixture for asynchronous tests.
 * 
 * @param {Object} testName
 * @param {Object} asyncTest: a method that ALWAYS returns a Deferred (dojo or doh)
 * @param {Object} assertionsCallback: a method that you will use to test your assetions once your defered is done
 */
dojo.declare("medryx.util.test.AsyncTestFixture", null, {
    constructor:function(testName, asyncTest, assertionsCallback) {
        this.name = testName;
        this.asyncTest = asyncTest;
		this.assertionsCallback = assertionsCallback;
    },
	runTest:function() {
		var d = new doh.Deferred();
		var actualDeferred = this.asyncTest();
		actualDeferred.addBoth(d.getTestCallback(dojo.hitch(this, "assertionsCallback")));
		return d;
	}
});

So now my asynchronous tests derive from this AsyncTextFixture and are created as:

	new subclass.of.AsyncTestFixture("testName",  function() {
		var def = doSomethingDeferred();
		return def;
	}, function(result) {
		doh.assertEqual("expected", result);
	});

I just think its easier to “remember” how to do aync testing with this fixture. Thoughts?

Older Posts »

Powered by WordPress