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
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.
ReplyDeleteCool! 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 :)
DeleteI 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.