Update 10.12.08: fixed horrifically blatant bug in sortBy

MetaJet

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:

idobj-Rixl0Dazi
_storedS{ id: obj-Rixl0Dazi, date: Wed Jan 07 2009 05:06:09 GMT-0800 (PST), type: message }
dateWed Jan 07 2009 05:06:09 GMT-0800 (PST)
_typemessage

I'm a message born at Wed Jan 07 2009 05:06:09 GMT-0800 (PST)!!!

I say: nothing

idobj-Rixl0Dazi
_storedS{ id: obj-Rixl0Dazi, note: Wilfred Whalmsley, date: Wed Jan 07 2009 05:06:09 GMT-0800 (PST), type: message }
noteWilfred Whalmsley
dateWed Jan 07 2009 05:06:09 GMT-0800 (PST)
_typemessage

I'm a message born at Wed Jan 07 2009 05:06:09 GMT-0800 (PST)!!!

I say: Wilfred Whalmsley

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:

idobj-Rixl0DioN
_storedS{ id: obj-Rixl0DioN, note: criminality, type: message }
notecriminality
_typemessage

idobj-Rixl0Dazi
_storedS{ id: obj-Rixl0Dazi, note: Wilfred Whalmsley, date: Wed Jan 07 2009 05:06:09 GMT-0800 (PST), type: message }
noteWilfred Whalmsley
_typemessage
dateWed Jan 07 2009 05:06:09 GMT-0800 (PST)

indexnotedate_stored_typeid
0Wilfred WhalmsleyWed 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 }messageobj-Rixl0Dazi
1criminalityS{ id: obj-Rixl0DioN, note: criminality, type: message }messageobj-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:

indexnotedate_stored_typeid
0Wilfred WhalmsleyWed 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 }messageobj-Rixl0Dazi
1wombatsWed 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 }messageobj-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.)

Associations

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:

Hey, it's Wed Jan 07 2009 05:06:09 GMT-0800 (PST) and I have 2 notes:

arglebargle

flimflam

Hey, it's Wed Jan 07 2009 05:06:09 GMT-0800 (PST) and I have 3 notes:

worldliness

grumpus

henrietta

idobj-Rixl0ECbj
_storedS{ id: obj-Rixl0ECbj, date: Wed Jan 07 2009 05:06:09 GMT-0800 (PST), type: message }
dateWed Jan 07 2009 05:06:09 GMT-0800 (PST)
_typemessage

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

Powered by AppJet
source
rendered in 0.736s