Sunday, November 25, 2012

REST-like Delete in a Dart Server

‹prev | My Chain | next›

I have made good progress in swapping a node.js for a Dart backend. But yesterday, when I tried some associated front-end code in the latest Dartium, I found a number of new things broken. Unfortunately, this seems fairly typical of the language nowadays. To be fair, this is a very new language nowhere near a 1.0 release. Still, it makes life hard when maintaining libraries. It makes life even harder when trying to maintain a book. But we must persevere.

So first, I work through both the code in both my Dart Comics sample app and my Hipster MVC library making changes. The bulk of the changes are preparing for the new import/library syntax. So the following:
#import('Collections.Comics.dart', prefix: 'Collections');
#import('Views.Comics.dart', prefix: 'Views');
#import('Views.AddComic.dart', prefix: 'Views');

#import('dart:html');
#import('dart:json');

#import('package:hipster_mvc/hipster_sync.dart');

main() {
  // ...
}
Is now written as:
import 'Collections.Comics.dart' as Collections;
import 'Views.Comics.dart' as Views;
import 'Views.AddComic.dart' as Views;

import 'dart:html';
import 'dart:json';

import 'package:hipster_mvc/hipster_sync.dart';

main() {
  // ...
}
Unfortunately the changes do not end there. It seems that retrieving browser dimensions has changed yet again. Instead of a Future, I now need to request a layout frame:
    window.requestLayoutFrame(() {
      var doc = window.document.documentElement;

      // ...
      bg.style.width = "${doc.offsetWidth}px";
      bg.style.height = "${doc.clientHeight}px";
      // ...
    });
I have changed that code more than once so that I can keep up with the language. Speaking of changes that I have made more than once, it seems that useCapture is again a regular optional parameter instead of a named optional parameter:
class ModelEventList implements EventListenerList {
  // ...
  add(fn, [bool useCapture]) {
    listeners.add(fn);
  }
  // ...
}
With that out of the way, I again have my front-end code all in order. Best of all I can even create new records in the datastore using a combination of the Hipster MVC client library and the dart-dirty backend datastore.

All that remains is a DELETE responder in my HTTP server and I will have complete functionality in place. As with all Dart HTTP responders, I first need a function that determines if the responder will handle the request. In this case, the HTTP method needs to be DELETE and the route must match /comics/<number>:
import 'dart:io';
import 'dart:json';

import 'package:dart_dirty/dirty.dart';

main() {
  Dirty db = new Dirty('dart_comics.db');

  HttpServer app = new HttpServer();
  // ...
  app.addRequestHandler(
    (req) => req.method == 'DELETE' &&
             new RegExp(r"^/comics/\d").hasMatch(req.path),
    (req, res) { /* ... * / }
  );

  app.listen('127.0.0.1', 8000);
}
The r before the RegExp string appears to be Dart's new way of signalling a non-interpolating string (borrowed from Python, I think). Since I do not have a dollar sign in this regular expression, I do not strictly need it, but it seems like a good habit to start here.

As for the response, I need to extract the ID from the route (e.g. /comics/42) and delete the record with that ID from dart-dirty:
  app.addRequestHandler(
    (req) => req.method == 'DELETE' &&
             new RegExp(r"^/comics/\d").hasMatch(req.path),
    (req, res) {
      var r = new RegExp(r"^/comics/(\d+)");
      var id = r.firstMatch(req.path)[1];

      db.remove(int.parse(id));

      res.outputStream.writeString('{}');
      res.outputStream.close();
    }
  );
I then send back an empty record to signal that it has been successfully deleted.

With that, I can add a new record from my Dart client-side application:


Then I can click the delete link:


And the record is deleted from the UI and from the persistent datastore:


Yay! So that should do it. I have replaced the node.js backend and datastore with a pure Dart equivalent. There is still a bit of cleanup that I would like to perform, but I am pretty excited about my progress.


Day #580

No comments:

Post a Comment