Saturday, March 24, 2012

Trouble Mixing Remote and Local Dart Libraries

‹prev | My Chain | next›

While messing about with static variables in Dart, I ran into some annoying behavior with Dart libraries. The code in question was imported into the main application space from the Hipster MVC github repository:
#import("dart:html");

#import("https://raw.github.com/eee-c/hipster-mvc/master/HipsterRouter.dart");
#import("https://raw.github.com/eee-c/hipster-mvc/master/HipsterHistory.dart");

main() {
  // ...
}
I need to make changes to the HipsterHistory library, so I switched that import to point to my local copy:
#import("dart:html");

#import("https://raw.github.com/eee-c/hipster-mvc/master/HipsterRouter.dart");
// #import("https://raw.github.com/eee-c/hipster-mvc/master/HipsterHistory.dart");
#import("/home/cstrom/repos/hipster-mvc/HipsterHistory.dart");

main() {
  // ...
}
The main() entry point remained unchanged—it creates a route and starts the pushState History mechanism:
main() {
  HipsterRouter app = new MyRouter();
  HipsterHistory.startHistory();
}
Only now it did not work. When I loaded my page, it was behaving as if there was no route present at all.

At first I suspected that the versions of HipsterRouter and HipsterHistory between my local copy and github had somehow grown out of sync (or at least that I had forgotten to push a change to github). But a quick check revealed that they were, in fact, identical.

My next instinct was of a select-is-broken nature. That this was not my first instinct proves that I am growing as a developer. Maybe. But select is not broken and neither is mixed local vs. remote import in Dart.

It turns out that main() and HipsterRouter were using two different versions of HipsterHistory. As can be seen, main() is explicitly using HipsterHistory locally. The problem is that HipsterRouter contains the following import:
#library('Router for MVC pages.');

#import("dart:html");

#import("HipsterHistory.dart");

class HipsterRouter {
  //...
}
That is, HipsterRouter imports HipsterHistory from the same location in which it exists. In this case, HipsterRouter comes from github, so it imports HipsterHistory from github. The main() entry point, on the other hand, gets HipsterHistory locally.

Unfortunately for me, Dart treats these two definitions of HipsterHistory as separate. So, when the router registers itself with HipsterHistory, it is doing so with a HipsterHistory that has not been started.

The solution, of course, is to import both from the local filesystem:
// #import("https://raw.github.com/eee-c/hipster-mvc/master/HipsterRouter.dart");
#import("/home/cstrom/repos/hipster-mvc/HipsterRouter.dart");

// #import("https://raw.github.com/eee-c/hipster-mvc/master/HipsterHistory.dart");
#import("/home/cstrom/repos/hipster-mvc/HipsterHistory.dart");

main() {
  HipsterRouter app = new MyRouter();
  HipsterHistory.startHistory();
}
This was a somewhat subtle "bug" that took me a bit to identify. I could have solved this, perhaps, by moving all of Hispter MVC into a single file, but I really prefer the separate file approach. Maybe this is something that Dart packaging system might help somehow—making it easy to switch between local and remote versions of a library. For now, however, I will have to be wary of getting in this situation again.


Day #325

2 comments:

  1. I'm looking into package management and imports in Dart right now, and posts like this are really helpful for seeing what kinds of problems users are running into. Thanks for taking the time to write this down.

    ReplyDelete
    Replies
    1. Cool! I 100% meant to submit this as a use-case (I saw the call go up on the mailing list), but forgot by the next day. It's great that you're scouring the interwebs for this kind of thing :)

      I don't know how you handle something like this. I can't decide if it's a SCM thing or a package thing. Seems liked new territory either way because of the library / web nature of Dart. Let me know if I can help crafting and/or testing a solution.

      Delete