Monday, February 13, 2012

Dunno Why I Avoid Strategy So

‹prev | My Chain | next›

I now have my Dart-based Hipster MVC framework able to query and update both local storage and remote data stores (via Ajax). But to do so, I have two different branches of my framework. Backbone.js has the nifty little Backbone.sync method, through which all queries and updates run. I hope to be able to replicate that in Dart.

I suspect that I will eventually succumb to typing various parts of this, but, for now, I will stick closer to Backbone's Javascript than idiomatic Dart. But immediately, I can think of a problem with this approach--it is not possible to redefine methods in Dart. By default, Backbone.sync() performs CRUD over Ajax. To change this behavior, one redefines Backbone.sync (i.e. not overriding it in a sub-class). Complicating matters is that both the model and collection classes need to have access to this overridden sync() method. This seems difficult, at best, if I cannot rewrite a single method.

My first attempt is to define a function that can be source'd into both the model and collection. For example, it would be nice if I could simply define a sync method:
// sync.dart
sync() {
  print("[sync]");
}
And then source() it in my collection sub-class:
#library('Collection class to describe my comic book collection');

#import('HipsterCollection.dart');
#import('Models.ComicBook.dart');

#source('sync.dart');

class Comics extends HipsterCollection {
  get url() => '/comics';
  modelMaker(attrs) => new ComicBook(attrs);
}
Back in my base class, I then try to replace my local storage version of fetch() with an attempt to invoke this sync() function:
class HipsterCollection implements Collection<HipsterModel> {
  // ...
  fetch() {
    try {
      sync();
    }
    catch (NoSuchMethodException e) {
      print("*** time to define a default sync");
    }
  }
  // ...
}
Unfortunately, this will not work—the NoSuchMethodException clause is always executed. It actually works as expected if I source() the sync.dart code directly into the HipsterCollection base class. This is because the source'd code is available to the enclosing library, but nowhere else (not even the base class of the enclosing library).

I need some way for the concrete class to communicate the strategy for syncing back in a way that the base class can manipulate it. So I am stuck defining a "syncer" (that really does not look right) class:
#library('Sync layer for the Hipster MVC');

class HipsterSync {
  sync() {
    print("[sync]");
  }
}
I can then specify this in my Comics concrete collection class:
class Comics extends HipsterCollection {
  get url() => '/comics';
  modelMaker(attrs) => new ComicBook(attrs);
  get syncer() => new HipsterSync();
}
And use it in the base class:
  fetch() {
    if (syncer != null) {
      syncer.sync();
    }
    else {
      // be ajax- like
    }
  }
That seems to work just fine. I do not see any other options besides supplying a syncing object. It is not as nice as Backbone.sync, with which I only need to define the sync strategy once. In my Dart MVC, I will have to supply the syncing strategy for each model and class in the application. That is less than ideal, but hopefully I can find ways to mitigate the pain.

Starting tomorrow.


Day #295

Sunday, February 12, 2012

Getting Started with Dart Local Storage

‹prev | My Chain | next›

I am in a happy place with my Dart-based "Hipster" MVC framework. To be sure, there are a few things that I miss from Javascript, but I can do without. At least for now. So today, I would like to explore saving and loading records from a client-side data store rather than over Ajax.

As can be seen from the Dart Window reference, Dart supports both sessionStorage and localStorage from the DOM Storage specification. The persistence of localStorage makes it a little more fun so that is where I will start.

I am not going to try to support Backbone.js's sync just yet. For now I will content myself with replacing the Ajax calls directly in code (that's why God created source code control).

First up, I add an instance variable to hold the data in the local store and I populate it with data from the localStorage of my app:
class HipsterCollection implements Collection<HipsterModel> {
  // ...
  List<HipsterModel> models;
  Map<String,Map> data;
  // ...
  fetch() {
    var json =  window.localStorage.getItem(url);
    data = (json == null) ? {} : JSON.parse(json);
    _handleStoreInit();
  }
}
I am using the url attribute as the name of my local stores "table" to minimize the number of initial changes from the calling context and concrete sub-classes. In the case of my comic book collection application, the url is "/comics":
class Comics extends HipsterCollection {
  get url() => '/comics';
  modelMaker(attrs) => new ComicBook(attrs);
}
The first time that I fetch() the local store, I expect that it will be null and thus set the local data with an empty Map.

In the future, I expect to parse JSON from the local store. So, next up is a post-data lookup method. For the Ajax version, I had parsed the JSON and created model objects from the data:
  _handleOnLoad(event) {
    var request = event.target
      , list = JSON.parse(request.responseText);

    list.forEach((attrs) {
      models.add(modelMaker(attrs));
    });

    on.load.dispatch(new CollectionEvent('load', this));
  }
In response to the local data store being ready, I need to do the same:
  _handleStoreInit() {
    data.forEach((k,attrs) {
      models.add(modelMaker(attrs));
    });

    on.load.dispatch(new CollectionEvent('load', this));
  }
After reloading the application in Dartium, I find that nothing is broken, but that could very well be thanks to the empty data initializer. The next step is to swap out the save() method on the model class. The Ajax version had been:
  save([callback]) {
    var req = new XMLHttpRequest()
      , json = JSON.stringify(attributes);

    req.on.load.add((event) {
      attributes = JSON.parse(req.responseText);
      on.save.dispatch(event);
      if (callback != null) callback(event);
    });

    req.open('post', '/comics', true);
    req.setRequestHeader('Content-type', 'application/json');
    req.send(json);
  }
I need to retain the actual store (req.send(json)) and must remember to dispatch the save event as well as invoke any supplied callbacks. Additionally, I now have to assign an ID attribute to the records if they do not already have one assigned (I can no longer rely on the backend data store to assign IDs). What I end up with is:
  save([callback]) {
    var id, event;

    if (attributes['id'] == null) {
      attributes['id'] = hash();
    }

    id = attributes['id'];
    collection.data[id] = attributes;
    window.localStorage.setItem(collection.url, JSON.stringify(collection.data));

    event = new Event("Save");
    on.save.dispatch(event);
    if (callback != null) callback(event);
  }
The hash() method used to assign an ID is just the value of Date.now() (milliseconds since the epoch). I make use of the fact that collection is an attribute of model to gain access to the collection's data for updating. Similarly, I grab the url from the collection to retrieve the local "table" to be updated. Since local storage is synchronous, I can then perform event dispatching and callback handling immediately following the save.

And, amazingly, it actually works. I can create two dummy records and, checking local storage in the Javascript console, I see that the records are created:


Since I only have create and delete working in the Ajax version, all that remains for me is to implement the model delete, which looks very similar to the create:
  delete([callback]) {
    collection.data.remove(attributes['id']);
    window.localStorage.setItem(collection.url, JSON.stringify(collection.data));

    var event = new Event("Delete");
    on.delete.dispatch(event);
    if (callback != null) callback(event);
  }
Using the UI, I remove the test records from the data store, then write to local storage some real comic books:


Nice. That worked surprisingly well and was quite easy to implement. I think that is due more to following the Backbone way of things that to Dart. Still, it nice to see that Dart is at least not getting in the way. Up tomorrow, I might take a shot at implementing a Backbone.sync method. That should prove... different in Dart.


Day #294

Saturday, February 11, 2012

Inheritance Grab Bag in Dart

‹prev | My Chain | next›

I am a bit annoyed that there does not seem to be a way to pass a class name to a function in Dart. Even so, it is not a huge hardship. Instead of telling my comic book collection's super class the model class to use when creating records, I have to supply a function that creates the models:
class Comics extends HipsterCollection {
  Comics() {
    url = '/comics';
    model = (attrs) => new ComicBook(attrs);

    on = new CollectionEvents();
    models = [];
  }
}
Like I said, not a big. I would prefer to be able to treat the class name as an object that can be passed:
model = ComicBook;
Which the super class could use to construct object as:
class HipsterCollection {
  // ...
  modelMake(attrs) {
    new model(attrs);
  } 
  // ...
}
But neither passing a reference to a class works nor does trying to treat a variable as a class. At least not in Dart.

No matter, perhaps I can do other things to clean up my sub-class definition. First of all, the definition of the on and models properties surely does not need to be done in every sub-class of HipsterCollection. The first thing to try is defining a constructor in my super class:
class HipsterCollection<M extends HipsterModel> {
  var url, on, model;
  List<M> models;

  HipsterCollection() {
    print("[HipsterCollection]");
    on = new CollectionEvents();
    models = <M>[];
  }
  // ...
}
(I am retaining the generics type information from yesterday, if for no other reason than documentation).

And that works. Not only does my collection still populate, but my super-class-only print("[HipsterCollection]"); statement displays in the console output:


I was unsure that this would work. I more-or-less expected that, since my Comics sub-class defined its own constructor, that the super class constructor would not be evaluated. I do not see anything in the constructor section of the language spec about the subject, so I try out a simple case like:
class A {
  A() { print("A"); }
}

class B extends A {
  B() { print("B"); }
}

class C extends B {
  C() { print("C"); }
  C.special(c) { print(c); }
}

main() {
  var c = new C();
  print("==");
  var c1 = new C.special("whee!");
}
(try.dartlang.org)

In both cases, the super-class contructors in C's ancestor chain are invoked, so this seems at least consistent behavior:
A
B
C
==
A
B
whee!
With the on and models property definitions safely back in the HipsterCollection base class constructor, my Comics collection is looking down right sparse:
class Comics extends HipsterCollection {
  Comics() {
    url = '/comics';
    model = (attrs) => new ComicBook(attrs);
  }
}
Hrm... since I cannot assign a class to the model attribute like I can in Backbone.js, there is no need to define that model function constructor as a property or to do so inside the constructor. It makes more sense to define that as a static class method:
class Comics extends HipsterCollection {
  Comics() {
    url = '/comics';
  }

  static modelMaker(attrs) {
    return new ComicBook(attrs);
  }
}
Only that does not seem to work. Back in the base class, I try:
class HipsterCollection<M extends HipsterModel> {
  // ...
  static modelMaker(attrs) {
    print("[super.modelMaker]");
    return null;
  }
  // ...
  _handleOnLoad(event) {
    var request = event.target
      , list = JSON.parse(request.responseText);

    list.forEach((attrs) {
      models.add(modelMaker(attrs));
    });

    on.load.dispatch(new CollectionEvent('load', this));
  }
}
But the only messages that I see are from the static method in the super class. Reading through the spec, I see this is intentional:
Inheritance of static methods has little utility in Dart. Static methods cannot be overridden... Experience shows that developers are confused by the idea of inherited methods that are not instance methods.
Sigh. It sure is hard for a fella to communicate a constructor class from sub-class to super-class in Dart. In the end, I settle for an instance method:
class Comics extends HipsterCollection {
  Comics() {
    url = '/comics';
  }

  modelMaker(attrs) => new ComicBook(attrs);
}
Back in the HipsterCollection base class, I then declare this method as abstract:
class HipsterCollection<M extends HipsterModel> {
  // ...
  abstract M modelMaker(attrs);
  // ...
  _handleOnLoad(event) {
    var request = event.target
      , list = JSON.parse(request.responseText);

    list.forEach((attrs) {
      models.add(modelMaker(attrs));
    });

    on.load.dispatch(new CollectionEvent('load', this));
  }
}
I must admit it nice to have compile-time, or at least formal, mechanism to catch any method-not-defined-in-sub-class mistakes that I might make.

One last thing that could make the concrete class definition even smaller would be to define the url property in my sub-class definition rather than in the constructor:
class HipsterCollection<M extends HipsterModel> {
  var url, on;
  // ...
}

class Comics extends HipsterCollection {
  var url = '/comics';
  modelMaker(attrs) => new ComicBook(attrs);
}
Unfortunately, that does not work as I receive the following error:
Internal error: 'http://localhost:3000/scripts/Collections.Comics.dart': Error: line 7 pos 7: field 'url' of class 'Comics' conflicts with instance member 'url' of super class 'HipsterCollection'.

  var url = '/comics';
      ^
Hunh? Why is that a "field"? I really intended it to be the instance member. Trying this out in the most simple case seems to actually work:
class A {
  var foo;
}

class B extends A {
  var foo = 'bar';
}

main() {
  var b = new B();
  print(b.foo);
}
(try.dartlang.org)

That works just fine, printing out the value of "foo" from the sub-class. Reading through the spec proves of little help. It seems to use field and instance variable more-or-less interchangeably. It does distinguish in places between field and getter, but there is a difference between instance variable and getter as well. There is no discussion in the spec on redefining an instance variable in a sub-class, though the super-class's instance variables are definitely available to the sub-class:
The instance variables of a class C are those instance variables declared by C and the instance variables inherited by C from its superclass,
Since Dartium and try.dartlang.org are unable to agree, and since redefining does not work in Dartium, I am forced to stick with assigning the instance variable in the constructor. Unless...

I do not expect this work, but what if I define a getter in the sub-class?
class Comics extends HipsterCollection {
  get url() => '/comics';
  modelMaker(attrs) => new ComicBook(attrs);
}
I am somewhat surprised, but that does work. It even works in the simple try.dartlang.org case.

I suppose that I can convince myself that this ought to work. In Dart, instance variables have implicit getters/setters. Therefore, I ought to be able to override those getters and setters. I thought I came across something in the spec that prohibited this, but it was probably just something like: "It is a compile-time error if a class has both a getter and a method with the same name". That does not apply in this case because I am overriding the implicit getter with an explicit getter.

This is a nice, compact format, but the problem, of course is that my url= setter would update the value of the url instance variable but have no effect on the url getter which is now hard-coded. In this case, this is not a big deal because the URL in a client-side MVC framework rarely needs to change. So, back in the base class, I again have an abstract method, this time a getter:
class HipsterCollection<M extends HipsterModel> {
  var on;
  List<M> models;

  abstract M modelMaker(attrs);
  abstract String get url();
  // ...
}

class Comics extends HipsterCollection {
  get url() => '/comics';
  modelMaker(attrs) => new ComicBook(attrs);
}
That will work just fine.

In the case where I need an implicit getter and setter, I will have to stick with assigning the instance variable in the constructor. Or hope that the noted behavior is a bug that will eventually be addressed.

That was quite the tour of the world on inheritance in Dart. At this point, I think I have a good handle on it. Well, maybe not that good, but hopefully enough to justify moving on to another topic tomorrow.


Day #293

Friday, February 10, 2012

Generic Constructors in Dart

‹prev | My Chain | next›

I have my Dart-based MVC framework in a good place. Well, at least a more or less working place. Now is a good time to do a bit of refactoring starting with how I build models in my collections.

Currently, to tell the collection how to create models, I pass it an anonymous function:
class Comics extends HipsterCollection {
  Comics() {
    url = '/comics';
    model = (attrs) => new ComicBook(attrs);

    on = new CollectionEvents();
    models = [];
  }
}
I was not terribly happy about that solution, but it worked.

Andreas Köberle suggested trying to use generics, about which I know virtually nothing. From the Wikipedia article, they are "algorithms are written in terms of to-be-specified-later types". Ah, that explains it. In Javascript and Ruby, I am used to passing references to classes all the time—something of an informal generics programming. In Dart, it seems, that things need to be a bit more formal.

Back in my HipsterCollection base-class, I can define a generic with the type parameter inside angle brackets:
class HipsterCollection<M> {
 // ...
}
Simply reading that, I can look at it as saying that HipsterCollections are built from things of type "M". If I create my HipsterCollection with an explicit type of ComicBook, then perhaps I can create models with the following:
new M(attrs)
That would be nice.

So I replace my model maker function with a new Generic:
class HipsterCollection {
  // ...
  _handleOnLoad(event) {
    var request = event.target
      , list = JSON.parse(request.responseText);

    list.forEach((attrs) {
      // models.add(model(attrs));
      models.add(new M(attrs);)
    });

    on.load.dispatch(new CollectionEvent('load', this));
  // ...
}
Unfortunately, that blows up:
Internal error: 'http://localhost:3000/scripts/HipsterCollection.dart': Error: line 58 pos 18: type parameter 'M' cannot be instantiated
      models.add(new M(attrs);)
                 ^
Looking through the spec, it seems that I am running afoul of: "A type parameter cannot be used to name a constructor in an instance creation expression."

Dang. It seems as though generics are quite a useful as I had hoped. If I run in checked mode, I can do things like extending the generic to enforce that the type is a model:
class HipsterCollection<M extends HipsterModel> {
  // ...
}
But, coming from the wild west of Ruby and Javascript land, this feel like unnecessary ceremony. Ah well, here's hoping that someday the Dart-lords remove that restriction. For now, I will stick with my model-maker function.


Day #292

Thursday, February 9, 2012

Long Distance Events in Dart

‹prev | My Chain | next›

I made it through a chunk of refactoring in my Dart based minimal MVC framework yesterday. I am still not quite done, however. My ultimate goal is to reach a point at which adding to a collection will fire an event for which a collection view can listen and react.

As of last night, I have a view that creates a new model on the collection using the create() method in the HipsterCollection base class:
class HipsterCollection {
  // ...
  create(attrs) {
    var new_model = model(attrs);
    new_model.save(callback:(event) {
      this.add(new_model);
    });
  }

  add(model) {
    models.add(model);
    on.add.
      dispatch(new CollectionEvent('add', this, model:model));
  }
  // ...
}
After a new model is saved, it is added to the collection. The add() method also dispatches an add event, for which the collection view can listen:
#library('Collection View for My Comic Book Collection');

#import('HipsterView.dart');

class Comics extends HipsterView {
  Comics([collection, model, el]):
    super(collection:collection, model:model, el:el);

  post_initialize() {
    _subscribeEvents();
    // ... 
  }

  _subscribeEvents() {
    if (collection == null) return;

    collection.on.load.add((event) { render(); });
    collection.on.add.add((event) { render(); });
  }
  // ...
}
The add.add thing is a little weird (add an event listener for the add event). Since I am defining these events myself, I will have to come up with a better name. But, for now, it works. When I add couple of test comic books to my collection, they are immediately added to the UI:


Yay!

That was sort of anti-climatic. I have struggled much getting to this point. I did not expect it to just work. Ah well.

There is a minor problem. I cannot delete the newly added comic books. This turns out to be due to a lack of ID, which I add to the enclosing <li> tag:


If I reload the page, the new items have an ID and I can delete them. So something must be going wrong in the model creation. Looking at the save() method in my HipsterModel, I think I see what. I am not adding the response from the server to the internal attributes:
class HipsterModel {
  // ...
  save([callback]) {
    var req = new XMLHttpRequest()
      , json = JSON.stringify(attributes);

    req.on.load.add((event) {
      print("[save] ${req.responseText}");
      on.save.dispatch(event);
      if (callback != null) callback(event);
    });

    print("[save] $json");
    req.open('post', '/comics', true);
    req.setRequestHeader('Content-type', 'application/json');
    req.send(json);
  }
  // ...
}
I print the response to the console, but I am not doing anything with it. And, indeed, the response does contain the ID from the server:


It is easy enough to parse that JSON response so that the model's attributes can be updated accordingly:
class HipsterModel {
  // ...
  save([callback]) {
    var req = new XMLHttpRequest()
      , json = JSON.stringify(attributes);

    req.on.load.add((event) {
      attributes = JSON.parse(req.responseText);
      on.save.dispatch(event);
      if (callback != null) callback(event);
    });

    req.open('post', '/comics', true);
    req.setRequestHeader('Content-type', 'application/json');
    req.send(json);
  }
  // ...
}
With that, I can add comics to my collection, have them show up on the page, and delete them. And all of this is reflected in the backend datastore:


That is a fine stopping point for tonight. There is still a ton to do before I am even close to replicating something like Backbone.js in Dart (and I have no intention of doing that), but I feel as though I am off to a pretty solid start. I am especially pleased with how easy it was to achieve the same separation of concerns through events that Backbone boasts.

Up tomorrow: I make my minimal framework a little more Dart-like starting with exploring Generics.


Day #291

Wednesday, February 8, 2012

Stupid Sub-Class Tricks in Dart

‹prev | My Chain | next›

I have model, view and collection base classes factored out of my Dart web application. I have a minor issue that I would like to see if I can resolve tonight. When I add a model to my collection, I would like to have it show up in the UI. Currently I have the add-from-form bit working, but I have to refresh the page ot actually see the new record.

In order for this to work, I will need a create() method on the collection class. The create() method will create a new record and add it to the collection. Adding it to the collection will, in turn, generate an "add" event for which the collection view can listen and do its thing. I have some of the parts necessary for this, but not all.

First up, the create() method:
class HipsterCollection {
  // ...
  create(attrs) {
    var new_model = model(attrs);
    new_model.save(callback:(event) {
      this.add(new_model);
    });
  }
  // ...
}
Nothing fancy there. I create a new model with the model function constructor, then save it. My model class already supports an optional callback that is used here to add the newly created model to the collection.

As for the add() method, I add the model to the models property (which is a simple array) and then dispatch an 'add' event:
class HipsterCollection {
  // ...
  add(model) {
    models.add(model);
    on.add.
      dispatch(new CollectionEvent('add', this, model:model));
  }
  // ...
}
With that, I need to modify the view class to allow a collection to be injected into it. I start in the view base class:
class HipsterView {
  var model, el, collection;

  HipsterView([this.model, this.el, this.collection]) {
    if (this.el is String) this.el = document.query(this.el);
    this._initialize();
  }
  // ...
}
Here I am making use of Dart's very nice "generative constructors" with optional parameters. The constructor HipsterView([this.model, this.el, this.collection]) does just what one would expect: assign this.model if a model parameter is supplied, assign this.el if el is supplied, etc. That is, I could assign the model and collection properties of a HipsterView object with the following:
new HipsterView(model: model_object, collection: collection_object);
That is definitely nice. Except I do not want to create a HipsterView, I want to create a object that sub-classes a HipsterView like the AddComic view:
#import('HipsterView.dart');

class AddComic extends HipsterView {
  // ...
}
Back in my main() entry point, I do this like this:
#import('Collections.Comics.dart', prefix: 'Collections');
#import('Views.Comics.dart', prefix: 'ViewsFIXME01');
#import('Views.AddComic.dart', prefix: 'ViewsFIXME02');

main() {

  var my_comics_collection = new Collections.Comics()
    , comics_view = new ViewsFIXME01.Comics(
        el:'#comics-list',
        collection: my_comics_collection
      );

  my_comics_collection.fetch();

  new ViewsFIXME02.AddComic(
    el:'#add-comic',
    collection: my_comics_collection
  );
}
(the ViewsFIXME01 and ViewsFIXME02 prefixes are a reminder to fix these once a Dart bug has been fixed).

Unfortunately, this does not work. When I load the page, I get the following error:
Internal error: 'http://localhost:3000/scripts/main.dart': Error: line 15 pos 3: invalid arguments passed to constructor 'AddComic' for class 'AddComic'
  new ViewsFIXME02.AddComic(
  ^
Ugh. It seems that I cannot rely on the constructor definition in my base class. Instead, I have to reproduce the same constructor signature in my sub-class:
class AddComic extends HipsterView {
  AddComic([collection, model, el]): super(collection:collection, model:model, el:el);
  // ...
}
Ew. I am not a fan of that. It seems that, if I do not define a constructor in my sub-class, all that Dart does for me is forward an empty sub-class constructor to super() (without arguments). If I want anything more sophisticated, I have to manually transcribe the parameters list like this.

Unfortunately, my trouble does not end there. In my sub-class, I need to perform some post-instantiation initialization:
class AddComic extends HipsterView {
  AddComic([collection, model, el]): super(collection:collection, model:model, el:el);

  void _initialize() {
    print("sub initialize");
    el.on.click.add(_toggle_form);
  }
  // ...
}
In my base class, I had defined my constructor to invoke the _initialize() method:
class HipsterView {
  var model, el, collection;

  HipsterView([this.model, this.el, this.collection]) {
    if (this.el is String) this.el = document.query(this.el);
    print(this.el);
    this._initialize();
  }

  void _initialize() { print("super initialize"); }
  // ...
}
But when I load the page, I am not seeing my "sub initialize" message—I only see "super initialize". I can get this working by declaring _initialize() as an abstract method and removing the function body, but then I have to define _initialize() in all sub-classes. I want this to be an optional thing.

It seems crazy that I cannot access the _initialize() method from the super class. And, after much fiddling, I find that I can access the sub-class's method, but only if it is public. That is, if I remove the underscore from the method name:
class HipsterView {
  var model, el, collection;

  HipsterView([this.model, this.el, this.collection]) {
    if (this.el is String) this.el = document.query(this.el);
    print(this.el);
    this.post_initialize();
  }

  void post_initialize() { print("super initialize"); }
  // ....
}
If I then define post_initialize() in the sub-class:
class AddComic extends HipsterView {
  AddComic([collection, model, el]): super(collection:collection, model:model, el:el);

  void post_initialize() {
    print("sub initialize");
    el.on.click.add(_toggle_form);
  }
  // ...
}
Then I finally see the "sub initialize" message. More importantly, on-click handler is again working.

I think that is enough for one night. I hope to complete the dynamic UI add tomorrow. It was a bit rough today, but I got myself pretty close. I am none too sure about Dart's requirement to explicitly support the super class's parameters. I am definitely not a fan of not being able to access an over-ridden private method from the super class (this seems like a bug). I am a fan of the optional generative constructor format. And, since I made progress in my MVC framework, I will count tonight as a win.


Day #290

Tuesday, February 7, 2012

Passing Classes by Reference in Dart

‹prev | My Chain | next›

Having addressed a minor Dart-to-Javascript mystery, I am ready to dive back into my rudimentary MVC framework. Up tonight: collections.

I can factor just about everything that I previously had in my ComicsCollection class out into a base class. This leaves behind something like:
#library('Collection class to describe my comic book collection');

#import('HipsterCollection.dart');
#import('Models.ComicBook.dart');

class Comics extends HipsterCollection {
  Comics() {
    url = '/comics';
    model = ComicBook;

    on = new CollectionEvents();
    models = [];
  }
}
The on and models object properties ought to get pushed into the base class as well. I will worry about that another time. For now I would like to make sure that the url and model properties are working.

The url property is pretty straight-forward. I am only using it to fetch() a collection of models. There is nothing too fancy there:
#library('Base class for Collections');

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

class HipsterCollection {
  var url, model, models, on;

  //...

  fetch() {
    var req = new XMLHttpRequest();

    req.on.load.add(_handleOnLoad);
    req.open('get', url, true);
    req.send();
  }
}
What is fancy (well, relatively speaking) is the instantiation of models for the collection. Since I am setting the model = ComicBook in my collection class, I would like to create the models something like this:
class HipsterCollection {
  var url, model, models, on;
  // ....

  _handleOnLoad(event) {
    var request = event.target
      , list = JSON.parse(request.responseText);

    list.forEach((attrs) {
      models.add(new model(attrs));
    });

    on.load.dispatch(new Event('load'));
  }
}
The only problem is that Dart does not care for the model = ComicBook assignment:
Internal error: 'http://localhost:3000/scripts/Collections.Comics.dart': Error: line 9 pos 13: Unresolved identifier 'Library:'http://localhost:3000/scripts/Models.ComicBook.dart' Class: ComicBook'
    model = ComicBook;
            ^
I spend a bit of time rooting around for ways to create classes from strings and the like in Dart, but come up empty. I also try static class methods:
class HipsterModel {
  static constructor() {
    // construct things
  }
  //...
}
But, when I try to assign that to my model attribute:
class Comics extends HipsterCollection {
  Comics() {
    url = '/comics';
    model = ComicBook.constructor;
    //...
  }
}
I am greeted with another error:
Internal error: 'http://localhost:3000/scripts/Collections.Comics.dart': Error: line 9 pos 23: unknown static field 'constructor'
    model = ComicBook.constructor;
                      ^
Eventually, I settle on a constructor function:
class Comics extends HipsterCollection {
  Comics() {
    url = '/comics';
    model = (attrs) => new ComicBook(attrs);
    // ...
  }
}
I may not be able to assign class names or static methods, but I can assign anonymous functions. In the HipsterCollection base class, I can then create new models like this:
class HipsterCollection {
  var url, model, models, on;
  // ....

  _handleOnLoad(event) {
    var request = event.target
      , list = JSON.parse(request.responseText);

    list.forEach((attrs) {
      models.add(model(attrs));
    });

    on.load.dispatch(new Event('load'));
  }
}
That is somewhat unsatisfying because my base class looks prettier than my sub-class. If I stick with this implementation, I will probably have to rename the model attribute to something like model_constructor. More bothersome than a long attribute name how verbose it has to be when I only want to describe that the model being collected is a ComicBook.


Day #289