Wednesday, March 14, 2012

Hipster MVC over Dart Websockets

‹prev | My Chain | next›

OK. What the heck. I got websockets in Dart working last night. Let's see if I can get Hipster MVC working over websockets.

First up, however, I am going to adopt the Backbone.js practice of syncing with 'create', 'update', 'read', and 'delete' instead of the HTTP verb equivalent:
  static Map _methodMap = const {
    'create': 'post',
    'update': 'put',
    'read': 'get'
  };

  // default sync behavior
  static Future _defaultSync(_method, model) {
    String method = _method.toLowerCase(),
           verb   = _methodMap.containsKey(method) ?
                      _methodMap[method] : method;

    // ...
  }
I kinda got away with it when I was just messing about with localStorage, but even then it felt wrong to "post" new records to localStorage.

With that TODO done, I shift to defining my websocketSync method. For now, I do it directly in the main.dart entry point to my MVC app:
#import('HipsterSync.dart');

main() {
  HipsterSync.sync = wsSync;

  var my_comics_collection = new Collections.Comics()
    , comics_view = new Views.Comics( /* ... */ );

  my_comics_collection.fetch();

  new Views.AddComic( /* ... */ );
}

wsSync(method, model) {
  final completer = new Completer();

  print("[wsSync] $method");

  return completer.future;
}
It seems that, as-is, the return of a Future is needed (another TODO). With that in place, I can load the app and see my print statement / tracer bullet in the Dart console:


In my backend, I enable websockets with the same node.js + WebSocket-Node setup from last night. Then I respond to a websocket message with a JSON representation of my sweet comic book collection:
  connection.on('message', function(message) {
    if (message.type === 'utf8') {
      console.log('Received Message: ' + message.utf8Data);

      connection.sendUTF(jsonComics());
    }
  });
That will not work for create/update/delete operations, but I will worry about that later.

Back in the main.dart code, I establish a websocket connection and, once it is open, I initialized my app—including fetching the comic book collection:
#import('HipsterSync.dart');

WebSocket ws;

main() {
  HipsterSync.sync = wsSync;
  ws = new WebSocket("ws://localhost:3000/");

  // After open, initialize the app...
  ws.
    on.
    open.
    add((event) {
      var my_comics_collection = new Collections.Comics()
        , comics_view = new Views.Comics( /* ... */ );

      my_comics_collection.fetch();

      new Views.AddComic( /* ... */ );
    });
}
With that, I am ready to implement my wsSync method to sync data with the backend over websockets. That is actually fairly straight-forward. When the app syncs (the only sync it does on-load is fetching the collection), I send the server a websocket message. When the server responds, I complete the Future (aka callback) with the JSON parsed comic collection:
wsSync(method, model) {
  final completer = new Completer();

  ws.send(method);

  // Handle messages from the server, completing the completer
  ws.
    on.
    message.
    add((event) {
      print("The data in the event is: " + event.data);

      completer.complete(JSON.parse(event.data));
    });

  return completer.future;
}
And just like that, I am HipsterSyncing my MVC collection over websockets:


That was almost too easy.

I still need to get creating / deleting working over websockets, so I may pick that up tomorrow. Then again, I am beginning to feel the strains of the impending Dart for Hipsters deadline so I may move on to other topics.


Day #325

No comments:

Post a Comment