Yoan Blanc’s weblog

Another lost swiss guy

April 2008

Introduction to jQuery’s custom event

Yoan BlancWed 30 April 2008, , ,

Trying doing a kind of RIA with a lot XHR (so called Ajax) messaging (very similar to Bayeux), I recently dived into what makes me enjoying working with jQuery. It’s two methods: bind (to bind an event like click, mouseover…) and trigger (to fire that event). They aims to solve the problem identical to the one of the GoF’s pattern called: Observer pattern. YUI has got a more pure implementation called CustomEvent that powers the AjaxEvent blogged by (awesome) Tim a time ago and Dojo’s one is very similar to jQuery’s.

Let’s begin, I bet you’ve already met .click() or .blur(), they are only shorcuts to .bind("click", …) and .bind("blur", …). First example with a custom event called hello":

$({msg:"Hello World!"}).
 bind("hello", function(){
  alert(this.msg);
 }).
 trigger("hello");

That will show you an alert saying “Hello World!”. $(...) creates a jQuery object with {msg:…}, bind binds to the event called “hello” and will call alert using the key “msg” from the origin object.

$({msg:"Hello"}).
 bind("hello", function(event, name){
  alert(this.msg+" "+name);
 }).
 trigger("hello", ["World!"]);

More tricky now, the trigger will pass arguments to the binded function. The result is still the same: “Hello World!”. So you can pass arguments when an event is triggered.

$({msg:"Hello"}).
 bind("hello", "World!", function(event){
  alert(this.msg+" "+event.data);
 }).
 trigger("hello");
		

Or when a function is binded, which is less useful but still. The result is always the same: “Hello World!”

What does this permits you? Tons of brilliant things. Like the AjaxEvent, you can dispatch Ajax results to different modules. You can decouple easily actions that have local actions. Imagine a world where all your console.logs are outside your main module and inside a logger module and that it doesn’t break when you fires a browser without Firebug installed? Piece of cake:

$("*").bind("hello", function(event) {
 console.log(
  event.type+": "+
  Array.prototype.slice.call(arguments, 1).join(", "));
});

This binding will be fired on any “hello” event and log “hello: arg1, arg2, arg3, ...”. event.type contains the name of the event for the lazy. Tom built a JS script that does that for YUI Custom Event. It can run directly inside Firebug as well via a proper the extension.

There is a list of all binded events inside jQuery.event.global, let’s use that to build a primitive logger.

for(var eventName in $.event.global) {
 $("*").bind(eventName, function(event) {
  console.log(
   event.type+": "+
   event.target.nodeName+") "+
   Array.prototype.slice.call(arguments, 1).join(", "));
  });
}

Okay, now I’ve made a little Sudoku, which is a complete rip-off of the famous GNOME Sudoku (which rocks thus) that almost create a module with only bind/trigger and two jQuery.prototype (aka jQuery.fn) hacks, the logger and a show-me-the source for you lazy. No module pattern (of any kind), no OOP (except jQuery’s extra methods like conflicts and replaceClass). Only binding events on the sudoku table(s) — yes, you can have multiple tables in the same page — and triggering events within those bindings.

If all the information go through an event (or different events) it’s very easy to had a listener that can:

  • keep track of the moves (using XHR i.e.),
  • offer an undo/redo interface
  • or even color the cells over the time so you can visually keep track of what is recent and what isn’t.

And all that without even modifying the existing code.

Étant sur un projet qui peut s’apparenter à une application riche avec énormément d’appel XHR (dit Ajax), je suis tombé nez à nez avec deux des plus belles fonctionnalités de jQuery (au delà de extend), j’ai nommé bind et trigger. Elle permettent de faire ce qu’on peut appelé Pub/Sub ou le design pattern du Gang of Four : Observer, un principe bien connu et plutôt évident. YUI offre le CustomEvent et Dojo un mécanisme similaire à jQuery, au cas où vous les connaitriez déjà c’est à ce moment-là qu’il faut hocher de la tête en approbation.

Pour ne pas changer des bonnes habitudes, les exemples sont dans la partie anglophone, ils doivent fonctionner via la console firebug pour autant que vous soyez sur un site qui utilise jQuery.

Tout ceci pour arriver au clou du spectacle, cherchant un outil qui puisse faire un usage fondé de cette gymnastique évènementielle. Un jeu pardis! Donc, vous pouvez trouver ici un Sudoku reposant essentiellement sur une communication évènementielle : pas de module pattern, ni de classes/objets (et il est bien évidemment possible d’avoir plusieurs sudoku dans la même page, sinon ça n’est pas drôle).

Les avantages de cette approche là, au delà du fait qu’il y a un découplage fort entre les différents éléments sont : plus de debug dans le code du cœur puisqu’il suffit d’écouter les évènements (voir le logger), ajout de fonctionnalité par écoute d’évènements existant (par exemple: sauvegarde de la partie sur le serveur en direct, coloration des cellules jouées en fonction du temps permettant de visuellement avoir une mémoire des coups récents face au autres, etc.). Et avec ça, faire du refactoring est très simple étant donné le couplage très faible de tout.

Amusez-vous bien!

AppEngine and BigTable minus Google?

Yoan BlancWed 09 April 2008, , , , ,

Everyone is more or less enthusiastic by the Google’s AppEngine initiative. But as you can imagine or know, it’s not that difficult to build similar web service using open source software.

AppEngine is very similar to web.py, and Aaron shows it building an application using web.py instead of Google’s webapp. I find webapp more javaish (response.out.write) than pythonic (return or print).

// from: using webapp
def get(self):
 self.response.headers['Content-Type'] = 'text/plain'
 self.response.out.write(’Hello, webapp World!’)

And BigTable (on Wikipedia), a column-oriented database using the popular map/reduce to work in parallel and in a distributed way. CouchDB is a schema-less database also using a map/reduce algorithm to perform requests. CouchDB is different from BigTable; and Amazon’s SimpleDB appears to be a web service alternative to CouchDB (both are built with Erlang). With CouchDB you have the very hype map/reduce and revisions; and you can forget about all the SQL you already know (or GQL you’ll have to learn) in favor of JavaScript, nifty isn’t it?

So, a little introduction the CouchDB interface using Python. You may need to install it before (don’t miss the very helpful README file) and the Christopher Lenz’s Python interface.

from couchdb import client
server = client.Server("http://localhost:5984/")
db = server.create("test")
# or db["test"]
# if the database already exists.

So, we have a database. Le’ts put some data in it.

db.create({
 "type": "Note",
 "content": "Hello World!"
})

db.create({
 "type": "Note",
 "content": "FizzBuzz"
})

And query it:

notes = db.query(\
 """function(doc){
  map(null, doc)
 }""")

for note in notes:
 print note.value["content"]

the map operation build the {key: value} object you get. Any sorting operations are done on the key, for example: add the timestamp of when you created a note and use it as a key to have a date-sorted view. Views are an important concept of CouchDB. You have (afaik) to create them in order to perform sorting, ordering, slicing, … operations and they are optimized. Let’s create a view called notes returning all document sorted by content:

db["_design/notes"] = {
 "views": {
  "all": """function(doc) {
   if(doc.type === "Note")
    map(doc.content, doc);
   }"""
 }
}

Now use it, selecting only one note (using count):

notes = db.view(\
 "_view/notes/all", \
 count=1)

for note in notes:
 print note.value["content"]

Find here (and the template) the CouchDB version of Aaron’s BigTable. The message is, you don’t need Google to create simple, straightforward web application. Of course, hosting your web.py/CouchDB application won’t be that cheap or easy but you won’t have to bother knowing how you’ll move your (users’) data out of AppEngine and not lose your users then.

Some people are already talking about creating their own AppEngine using CouchDB (via David). Are AppEngine and GQL worth reusing them? I don’t think AppEngine is either bad or evil, just that if you find the way applications are built cool, you can get that with open source bricks.

Is AppEngine a Facebook applications framework without Facebook? Outside of a social network (except if you consider that GMail is a social network) and with free hosting and bandwidth. Nothing is free. Facebook applications can live outside Facebook as some of them are like widgets/gadgets but can AppEngine applications live outside Google, if they want to?

AppEngine, le nouveau produit Google, est arrivé à grand bruit, rendant les gens heureux ou sceptiques. J’aimerais (dé)montrer qu’il est simple de trouver des alternatives ayant des éléments similaires pour webapp et BigTable.

webapp, le framework Python, est fortement similaire à web.py, avec même l’inconvénient qu’étant du Python il y a le goût de Java/C# dans sa verbosité. Il lui manque peut-être un coup de polish faisant abstraction des : « wscgi », « request », « response », ... dont on s’en moque bien au fond. Adrian débattait sur ce type d’éléments qui se retrouve au cœur de Django. Pour info, AppEngine semble utiliser le même système de templates que Django (dans la syntaxe du moins).

BigTable, le système de gestion de bases de donnée made in Google, orienté colonne (et non pas ligne comme le sont généralement les SGBDR). Il est spécialement connu pour ses aspects distribués et le fait que Google se sert intensivement du map/reduce pour les opérations lourdes. CouchDB respose également sur du map/reduce mais à l’instar de beaucoup de SGBD (dont BigTable) est sans schéma, il fonctionne avec des documents qu’il store et restore. Amazon et son SimpleDB s’en approchent fortement, l’un et l’autre sont construit avec le langage de programmation Erlang (spécialement orienté systèmes distribués).

La partie anglophone de cet entrée contient une rapide introduction aux principes de base de CouchDB : la base de donnée, le document et la vue. Et trouvez une application faisant la même chose que celle d’Aaron Schwartz utilisant web.py sur AppEngine, mais se servant de CouchDB en backend plutôt que BigTable.

Le bémol à ça, et le grand avantage de AppEngine, est l’hébergement et le déploiement qui sont géré par Google de manière très élégante et j’imagine efficace. Mais que se passera-t-il quand votre application aura une forte demande, ou que vous désirerez en récupérer les données de vos utilisateurs ? On peut faire un rapprochement avec les applications Facebook qui vivent au sein d’une entité tierse à la différence que seules les données utilisateurs sont chez Facebook tout le reste vous incombant. Rien n’est gratuit, ni un hébergement, ni une bande passante à chacun de trouver le meilleur compromis à ses besoins, peurs et envies. AppEngine n’en reste pas moins un beau coup de pouce à Python et un outil intéressant.

About

meYoan Blanc is a web developer that lives in Lausanne (rue Couchirard 15, 1004 Switzerland) worked for Yahoo! and comes from La Chaux-de-Fonds. This web site is for this weblog, a playground for random stuffs and can help keepingmeconnected with some friends out there.

Get my vCard or contact me by phone (+41 21 625 82 09) or email ().

Misc

RSS, list.blogug.ch

This site reflects only my opinion and is not affiliated with anyone else.

copyright 2006-2008 — doSimple.ch