Gmane
Favicon
From: Kris Zyp <kzyp <at> dojotoolkit.org>
Subject: Naturalizing Dojo Data
Newsgroups: gmane.comp.web.dojo.devel
Date: 2008-04-09 18:57:42 GMT (33 weeks, 6 days, 12 hours and 10 minutes ago)
The dojox.lang.observable (and dojox.data.JSData) modules were written for 
the purpose of providing more natural and aesthetic APIs and data access. 
There may be a number of possible uses, however, I wanted to make some 
specific notes about naturalizing dojo data access. I think our primary 
intent is that we can access and modify properties/attributes using regular 
javascript syntax rather than without having to call store.{g,s}etValue. 
Rather than writing:
var name = store.getValue(item,"name");
store.setValue(item,"foo","bar");
we write:
var name = item.name;
name.foo = "bar";

Unfortunately, with the vbscript/lettable hack as used in observable, we 
can't capture:
item.subItem = {name:"my object"}; // objects won't work in vb's setters

However, even with native getters and setters, we can't capture all actions 
like:
delete item.old; // delete removes the getter/setter without calling 
anything
item.newProp = "value"; // property didn't exist before

Prior to working at SitePen, I developed a whole client side framework (part 
of the Persevere project) that really could detect all JavaScript 
modifications and perform the appropriate lookups or persistence. However, 
this framework required JavaScript compilation in order to work (basically 
it compiled all property accesses and modifications to internal calls). 
However, I don't think anyone wants the cost of compilation in order to 
improve dojo data API. ES4 may also provide the means for capturing almost 
all modifications, by using catch-all getters/setters and I think there is a 
delete meta function that may be interceptable, as well. But ES4 is a ways 
off still.

Unfortunately, today this means we must develop a set of rules for 
developers if they want to use normal property access with dojo data items 
as provided by the dojox.data.JSData (observable) data store converter:
1. You may use JavaScript's property syntax for modifying or accessing an 
existing property.
2. If you want to delete a property, you must use unsetAttribute, rather 
than JS's delete operator.
3. If you want to add a new property, you must use store.setValue.

(Because of vbscript limitations, these rules also must be observed):
4. If you want to access a property that has (or may have) an object value, 
you must use store.getValue
5. If you want to modify a property that has had an object value or will 
have an object value, you must use store.setValue

So using an item from a store with the observable/JSData module would look 
like:
name = item.name; // rule 1
subItem = store.getValue(item,"subItem"); // get an object rule 4
subName = subItem.name; // rule 1
item.name = "new name"; // rule 1
store.unsetAttribute(item,"oldProp"); // rule 2
store.setValue(item,"newProp","value"); // rule 3
store.save();

It is worth noting that accessing and modifying existing properties with 
primitive values is probably the predominant use case. The observable module 
does provide the means for covering this use case. Also, once native support 
is available for getters and setters in IE, developers would only need to 
abide by the first three rules.

I have been developing a JsonRestStore data store that accesses and saves 
data through RESTian endpoints (Amazon S3, CouchDB, and Persevere so far). 
This has been built intentionally with the idea of allowing developers to do 
direct property access with a minimal set of rules. There are few techniques 
I utilized to make items more directly usable:
1. All references are resolved on intake, rather than on access
2. Any lazy references (references to data that hasn't been loaded yet from 
the RESTian endpoint), are represented as dojo.Deferred objects
3. Public API for "_setDirty" (right now, I call it "changing").

Therefore developers can use the following rules for directly accessing item 
properties:
1. Prior to any modifications to an item, you must call store.changing(item)
2. Any property may be directly accessed, however if it is a lazy property, 
you can have it automatically resolved by calling getValue, or if you access 
it directly, you will get a Deferred object. You can add a callback, and 
receive the resolved value.

The last example would look like:
name = item.name;
subItem = item.subItem;
subName = store.getValue(subItem,"name"); // let's assume this is a lazy 
property
store.changing(item);
item.name = "new name";
delete item.oldProp; // we can use the JS delete operator
item.newProp = value; // we can set new properties as well
store.save();

In terms of ease of use and simplicity, I think this approach may actually 
be relatively palatable. This approach also doesn't incur the big 
performance penalty of getters/setters (as documented in the previous 
emails). Additionaly, when native getters/setters arrive, they can 
selectively be applied to lazy properties (for minimal performance hit), and 
developers would only need to observer rule #1 in order to directly access 
and modify properties. Of course this is viable due to the way JsonRestStore 
was developed, it can't be retroactively applied to other data stores 
(unless someone does some recoding).

So briefly, the observable/JSdata module can take existing data stores and 
make properties accessible with JavaScript for the majority case, existing 
primitive values. However, it is slow and has a somewhat more complicated 
set of rules for different forms of access. Alternately, data stores can be 
developed that have a simpler set of rules and provide faster data access.

Thanks,
Kris