Sunday, August 31, 2008

Sticky Notes example rewritten

The initial version of the sticky notes example was pretty much an exact copy of the non-Joose version, which sucked, because the code was really hard to understand and didn't work too well outside of the Safari Browser.
I rewrote it using jQuery and on the way implemented a generic databinding system to attach Joose objects to jQuery objects and vice-versa. The new code is way shorter and should pretty much speak for itself (understanding what really happens is a totally different matter).
If you are interested in the jQuery databinding, take a look at the code, but beware that it dwelves deep into the meta magic of Joose and isn't ment for the lighthearted or people who like extensive documentation :)

Friday, August 29, 2008

New OR-Mapper Example: Sticky Notes

I created a new example that uses the new OR-Mapper for the HTML5 and Google Gears database.
It is based on the an example from the WebKit guys and it is currently ported one-to-one. Thus it works best in WebKit browsers like Safari and only mostly works in Firefox 3. I didn't test any other browsers yet.
If it works you should be able to place notes on the screen which will persist across page reloads.

Wednesday, August 27, 2008

More work on the OR-Mapper

just some updates that make the tool actually useful:
  • There is now a remove method in every entity to delete it from the database
  • Transaction support for Gears. Transactions are queued, so you can safely nest them in callbacks but they wont be executed until the current transaction is done (same behavior as with HTML5 databases)
  • A little meta class magic to make Entity definitions even nicer:
This is how entity definitions look now:
           Class("Person", {
isa: ORM.Entity,

tableName: "person",

hasOne: {
mother: {
isa: function () { return m.Person }
}
},

hasMany: {
cars: {
isa: function () { return m.Car },
foreignKey: "owner"
}
}
});

If you are interested in how these DSL like class definitions are
achieved using the Joose framework, this commit might be interesting:

Monday, August 25, 2008

OR-Mapper for the HTML5 database and the Google Gears database

Last week a guy on the Joose mailing list asked whether the example object relational mapper included with Joose could be used to build iPhone web application using the webkit implementation of the HTML5 database API. The immediate answer was that it wasn't possible because the OR mapper was designed to work with the Google Gears database which is currently not compantible with HTML5.

So I did two thinks
  • I implemented a wrapper for the Gears API that makes it look like the HTML5 database API (There was already one linked from Ajaxian but all the download links were dead)
  • I ported the existing OR mapper to use the new API
This means that the OR mapper can be used with the Gears db and the HTML5 db without any changes.

The HTML5 database API in inherently asynchronous. The reason being that databases requests should not block script execution and thus decrease the responsiveness of the GUI.
I, personally, don't like the API because thinks start to look a lot like degenerated LISP once you get down to the 16th callback level, but, I guess, performance was more important than beauty for the API designers.

The primary change with respect to the synchronous Gears version is that now all operations that talk to the database like Entity.save() or Entity.newFromId() take an extra parameter that is a callback function which gets executed as soon as the db operation is completed.

Here are some examples from the test suite of the OR-Mapper.

Declaring an entity looks like this (A Person that has a mother and many cars):
    Class("Person", {
isa: ORM.Entity,

classMethods: {
tableName: function () {
return "person"
}
},

has: {
mother: {
metaclass: ORM.HasOne,
isa: function () { return m.Person }
},

cars: {
metaclass: ORM.HasMany,
isa: function () { return m.Car },
foreignKey: "owner"
}
}
});
And using the classes looks like this (I warned you about the LISP effect):

/*
ok, isEq, canOk, etc. are functions from the unit testing framework that I use
*/

ORM.transaction(function (tx) {

var mother = new MyEntities.Person();

mother.setName("elke");
mother.setCity("Elmshorn");
ok(mother.isNewEntity, "... mother is a new entity object")

function testPerson (person) {
canOk(person, "getRowid")
canOk(person, "getName", "... there is a name method")
isEq(person.getName(), "malte", "... name is correct")
isEq(person.getCity(), "Hamburg", "... city is correct")
person.getMother(function (mother) {
ok(mother, "... can fetch mother")
isEq(mother.getName(), "elke", "... mother name is correct")
isEq(mother.getCity(), "Elmshorn", "... mother city is correct")
})


ok(person.getCars, "... there is a cars method")
person.getCars(function (cars) {
for(var i = 0; i < 10; i++) {
canOk(cars[i], "getBrand")
isEq(cars[i].getBrand(), "bmw", "... brand is correct");
isEq(cars[i].getModel(), "3."+i, "... model is correct");
canOk(cars[i], "getOwner")
cars[i].getOwner(function (owner) {isEq(owner.getName(), "malte", "... owner name is correct")})
}
var count = cars.length
ok(count == 10, "... there are only 10 cars: "+count)
});
}

mother.save(function () {
ok(!mother.isNewEntity, "... mother is now saved")

var person = new MyEntities.Person();

person.setName("malte");
person.setCity("Hamburg");
person.setMother(mother)
person.save(function () {

var i = 0;
function makeCar() {
var car = new MyEntities.Car();
car.setModel("3."+i);
car.setBrand("bmw");
car.setOwner(person);
car.save(function () {
if(++i < 10) {
makeCar()
} else {
testPerson(person)
MyEntities.Person.newFromId(person.getRowid(), function (aPerson) {
testPerson(aPerson)
endTests()
})

}
})
}

makeCar()
})})})

If you have either Google Gears installed or are using a web browser that implements the HTML5 database like Safari (and other browsers based on webkit), you can run the testsuite yourself.

For more info, check out the source:
The code is currently extremely alpha and missing features. If people show interest I'll push it to production quality.

Saturday, August 16, 2008

Profiling Joose bootstrap time

Joose is self-hosting which means two things:
  1. Most parts of the Joose framework are written using Joose itself
  2. Those parts of the framework where this is not possible (the most fundamental parts) are later bootstrapped to look like the had been written using Joose
The nice thing about this is that all objects within the Joose object system are introspectable using Joose's own methods. It is in a sense pure.
However, bootstrapping incurs overhead (because you do stuff) and I was wondering how big the overhead is. Well, it turns out it is rather small.

I used firebug for profiling, so all my numbers are from Firefox. My computer is a Macbook Pro with 2.5 GHz Core 2 Duo.

Under Firefox 3 compiling Joose takes 17 milliseconds and under Firefox 2 the compilation takes 23 milliseconds, so we are around 2 hundreds of a seconds. Real speed junkies might be worried about this, but tricks like compiling all JS files into one large file probably make a much larger difference.

My conclusion is that Joose's overhead is pretty much negligible, but obviously you have to judge yourself. If anybody is really worried about the 20 milliseconds (maybe because it turns out to be 6000 milliseconds under IE6 :) there is a possible optimization to reduce the bootstrap time to 0, but I won't do it until it is absolutely neccessary.

Thursday, August 14, 2008

Joose now supports jsonpickle

Joose now supports jsonpickle. (At least if you check out the latest SVN).
This gives all the Python programmers the chance to transfer dumps of their Python objects via http to the browser and then instantiate those objects using JavaScript and do method calls on them.

Here is a code example stolen from the jsonpickle homepage:
>>> import jsonpickle
>>> from jsonpickle.tests.classes import Thing

# Create an object.
>>> obj = Thing('A String')
>>> print obj.name
A String

# Use jsonpickle to transform the object into a JSON string.
>>> pickled = jsonpickle.dumps(obj)
>>> print pickled
{"child": null, "classname__": "Thing", "name": "A String", "classmodule__": "jsonpickle.tests.classes"}


And here is how you would use the pickled Python object with Joose:
Module("jsonpickle.tests.classes", function () {
Class("Thing", {
does: Joose.Storage.jsonpickle,
has: {
child: {
is: "rw"
},
name: {
is: "rw"
}
}
})
})

var thing = JSON.parse('{"child": "Robert", "classname__": "Thing", "name": "A String", "classmodule__": "jsonpickle.tests.classes"}');


Of course you may add many more methods to your Joose classes and use them like real objects.

With a simple JSON.stringify(thing) you can send the data back to Python. (You need to call Joose.Storage.Unpacker.jsonpickle.patchJSON(); to be able to use JSON.stringify directly)

Wednesday, August 13, 2008

Automatic Serialization and Deserialization of JavaScript-Objects to JSON with Joose.Storage

One of the most useful features of Joose is the builtin role Joose.Storage which gives easy JSON serialization and deserialization to all Joose objects. All you have to do to use this role is to add
does: Joose.Storage
to your class definition like this:
Class("Point", {
does: Joose.Storage,
has: {
x: {is: rw},
y: {is: rw},
otherPara: {
is: rw,
init: "stuff",
persistent: false
}
}
})

Now if you use the json2.js library avaible from json.org you can also say:
Joose.Storage.Unpacker.patchJSON();
... and magically when you use the JSON library all Joose object will be serialized when you call JSON.stringify and will be deserialized (become Joose objects of the correct type) again when you call JSON.parse.

This makes round-tripping between JavaScript and JSON very easy. For example this creates a point then serializes it and immediately deserializes the point again.
var point = JSON.parse(JSON.stringify(new Point({x: 10, y: 20})));
alert(point.getX())
With normal JSON the resulting object will not have any methods but with Joose.Storage you get a real object of class Point.

The best thing about Joose.Storage is that it also works for deeply nested objects and it even preserves identity when an objects occurs within the same structure multiple times. In the above example the attribute otherPara of class Point is declared as persistent: false. This means that the attribute will not be serialized. This makes sense for attributes which can't be serialized like references to DOM elements.

Here is the resulting JSON document for a nested structure of points:
JavaScript:
var p0 = new Geometry.Point({x: 10, y: 20})
var p1 = new Geometry.Point({x: 20, y: 30})

var test = {
a: p0,
b: p0,
c: p1
}

Resulting JSON:
{"a":
{"__CLASS__":"Point",
"__ID__":5,
"x":10,
"y":20},
"b":
{"__ID__":5},
"c":
{"__CLASS__":"Point",
"__ID__":6,
"x":20,
"y":30}}

Joose.Storage is ideal for sending object to the server using and then creating them again at a later point. Joose.Storage is also wire compatible with MooseX::Storage which is a Perl module for serialization of objects and there are efforts under way to make it compatible with jsonpickle which is a Python implementation of a similar concept.

Wednesday, August 6, 2008

Custom events in jQuery

Dion made a nice post about the power of custom events in jQuery. Here is his example converted to jQuery.