Update 10.12.08: fixed horrifically blatant bug in sortBy
MetaJet is a small framework that integrates AppJet's persistent storage with JavaScript's conventional object-oriented coding methods. You could think of it as an ORM for AppJet's Storage library. With bare StorableObjects, you can't attach functionality; with MetaJet-generated classes, you can build a class in the normal way and use the storage-management API that comes built into it.
Here's an example (not including the import('lib-meta') bit):
var Message = Meta.newClass('message');
// then build onto your class as normal
Message.prototype.announce = function()
{
print(this);
print(H2("I'm a message born at " + this.date + "!!!"));
print(H3("I say: " + (this.note == null ? 'nothing' : this.note)));
}
var missive = new Message({date: new Date()});
missive.announce();
missive.note = 'Wilfred Whalmsley';
// setting props can be done right on the obj,
// but you'll need to call save() after to store your changes
// unless you use update_attribute('property', value)
missive.save();
missive.announce();
which will get you:
| id | obj-Rixl0Dazi |
|---|---|
| _stored | S{ id: obj-Rixl0Dazi, date: Wed Jan 07 2009 05:06:09 GMT-0800 (PST), type: message } |
| date | Wed Jan 07 2009 05:06:09 GMT-0800 (PST) |
| _type | message |
| id | obj-Rixl0Dazi |
|---|---|
| _stored | S{ id: obj-Rixl0Dazi, note: Wilfred Whalmsley, date: Wed Jan 07 2009 05:06:09 GMT-0800 (PST), type: message } |
| note | Wilfred Whalmsley |
| date | Wed Jan 07 2009 05:06:09 GMT-0800 (PST) |
| _type | message |
Define a beforeSave() or afterSave() method on your Meta-generated class and it'll get called at the appropriate time. If beforeSave() returns false, the save doesn't go through. (You might put validation code there.)
You can destroy() objects to remove them from storage. Your object itself will keep hanging out with the values it had, but it will wear a destroyed property and will no longer save.
You can find stuff:
var crimnote = Message.find.orCreate({note: 'criminality'});
var wilfrednote = Message.find.first({note: 'Wilfred Whalmsley'});
var allmsgs = Message.find.all();
new Meta.Group(crimnote, wilfrednote, allmsgs).each(function(thing){printp(thing);});
...results in:
| id | obj-Rixl0DioN |
|---|---|
| _stored | S{ id: obj-Rixl0DioN, note: criminality, type: message } |
| note | criminality |
| _type | message |
| id | obj-Rixl0Dazi |
|---|---|
| _stored | S{ id: obj-Rixl0Dazi, note: Wilfred Whalmsley, date: Wed Jan 07 2009 05:06:09 GMT-0800 (PST), type: message } |
| note | Wilfred Whalmsley |
| _type | message |
| date | Wed Jan 07 2009 05:06:09 GMT-0800 (PST) |
| index | note | date | _stored | _type | id |
|---|---|---|---|---|---|
| 0 | Wilfred Whalmsley | Wed Jan 07 2009 05:06:09 GMT-0800 (PST) | S{ id: obj-Rixl0Dazi, note: Wilfred Whalmsley, date: Wed Jan 07 2009 05:06:09 GMT-0800 (PST), type: message } | message | obj-Rixl0Dazi |
| 1 | criminality | S{ id: obj-Rixl0DioN, note: criminality, type: message } | message | obj-Rixl0DioN |
Those object literals you pass to the finders work the same way as the ones you pass to StorableCollection.filter(). You can also find single objects by ID:
var wilfredagain = Message.find.first(wilfrednote.id);
find.all and find.first can take not just the usual sort-conditions function, but a find-conditions func to replace the usual object literal:
var othernote = new Message({note: 'wombats', date: new Date()});
printp(Message.find.all(
function(storedobj){ return storedobj.note.match(/^[w|W]/); },
function(a,b) { return b.date - a.date; }
));
yielding:
| index | note | date | _stored | _type | id |
|---|---|---|---|---|---|
| 0 | Wilfred Whalmsley | Wed Jan 07 2009 05:06:09 GMT-0800 (PST) | S{ id: obj-Rixl0Dazi, note: Wilfred Whalmsley, date: Wed Jan 07 2009 05:06:09 GMT-0800 (PST), type: message } | message | obj-Rixl0Dazi |
| 1 | wombats | Wed Jan 07 2009 05:06:09 GMT-0800 (PST) | S{ id: obj-Rixl0DxAv, date: Wed Jan 07 2009 05:06:09 GMT-0800 (PST), note: wombats, type: message } | message | obj-Rixl0DxAv |
Note that the implementation of the find-by-function thing is about as naive as it gets, and if you experience slowdowns, this is probably the first thing to be suspicious of.
You will have noted by now the presence of Meta.Group, an awesome extra thing. These are just arrays with a subset of the Array extension methods from the Prototype library: namely, each, collect, filter, inject, max, min, pluck, reject, sortBy, include, first, last and uniq. find.all returns Meta.Groups.
If you get tired of doing prototype funcs one at a time, there's another awesome helper:
Meta.extend(Message.prototype, {
this_method: function(foo) { ... },
that_thing: function(foo, bar) { ... }
});
In fact this works on everything that has object-literal-style properties. It rocks for merging things together to pass to a finder. (It works just like Prototype's Object.extend.)
Simple associations are pretty straightforward with MetaJet. Let's reconfigure our class landscape a bit:
Meta.clear_all(); // blows away all of storage. use with extreme care, obviously
Message = Meta.newClass('message', {
has_many: 'notes'
});
var Note = Meta.newClass('note', {
belongs_to: 'message'
});
var msg1 = new Message({date: new Date()});
var msg2 = new Message({date: new Date()});
var note1 = new Note({text: 'arglebargle', message_id: msg1.id});
var note2 = new Note({text: 'flimflam', message_id: msg1.id});
var note3 = new Note({text: 'henrietta', message_id: msg2.id});
var note4 = new Note({text: 'grumpus', message_id: msg2.id});
var note5 = new Note({text: 'worldliness', message_id: msg2.id});
Message.prototype.announce = function()
{
print(H3("Hey, it's " + this.date + ' and I have ' +
Meta(this,'notes').length + ' notes:'
));
Meta(this,'notes').each(function(note){
print(P(note.text));
});
};
msg1.announce();
msg2.announce();
print(Meta(note5,'message'));
Let's see how that flies:
arglebargle
flimflam
worldliness
grumpus
henrietta
| id | obj-Rixl0ECbj |
|---|---|
| _stored | S{ id: obj-Rixl0ECbj, date: Wed Jan 07 2009 05:06:09 GMT-0800 (PST), type: message } |
| date | Wed Jan 07 2009 05:06:09 GMT-0800 (PST) |
| _type | message |
I sincerely apologize for making you type Meta(object,'property') instead of object.property. JavaScript is awesome, but not that awesome. Or I'm not that awesome. Or both. (I am considering writing some setters so you can at least say note2.message = msg2 and the like.)
What you see above is the syntax for simple associations; there's a little more in the code but it's not reliable yet. Basically, we're looking to support polymorphic associations, key names other than the class name, and join models in the near term.
This is definitely alpha code. I apologie for the lack of properly appjetty docs; the internals of MetaJet are currently structured in kind of a bizarre way that doesn't map very well to jsDoc conventions. So I am working on expanding this document pretty constantly.
Patches are welcome and bugs are inevitable; you can get in touch with me via my web page below, or find me by searching the AppJet forums for 'MetaJet'.
Have fun,
Mike