Monday, December 17, 2012

Packaging Main.dart for Organization and Testing

‹prev | My Chain | next›

I ended yesterday with the main entry point in my Dart application in the scripts directory. Thanks to the magic of Dart Pub, that main entry point was then able to import the remaining application code.

The web page looks like:
<head>
  <title>Dart Comics</title>
  <link rel="stylesheet" href="/stylesheets/style.css">

  <script src="/dart.js"></script>
  <script src="/packages/scripts/main.dart" type="application/dart"></script>
</head>
In other words, it is a pretty typical <script> tag that is pulling my main entry point from scripts/main.dart and looks like:
import 'package:scripts/Collections.Comics.dart' as Collections;
import 'package:scripts/Views.Comics.dart' as Views;
import 'package:scripts/Views.AddComic.dart' as Views;

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

import 'package:hipster_mvc/hipster_sync.dart';

main() {
  // initialize code here
}
Aside from the awesome import and package stuff, it is very much like the usual JavaScript code that I might write.

In the comments to yesterday's article, Ladislav Thon suggested another possible approach—one that leveraged Dart's imports a bit more. With this approach, I need to move my main.dart entry point out of the app/web/scripts directory and move it into app/lib. As such, it is no longer directly accessible via a <script> tag, but it is part of my application's packages.

So, my HTML now performs an inline import:
<head>
  <title>Dart Comics</title>
  <link rel="stylesheet" href="/stylesheets/style.css">

  <script src="/scripts/dart.js"></script>
  <script type="application/dart">
    import 'package:scripts/main.dart' as App;
    main() => App.main();
  </script>
</head>
I would prefer it if I could do that import as part of the <script> tag, but until <script> tags become package aware, I will have to settle for that.

I am not quite done yet. It is not possible to import a library in Dart without declaring the file as a library. So I add the library directive along with an identifier to the top of main.dart in the app/lib directory:
library app;

import 'package:scripts/Collections.Comics.dart' as Collections;
import 'package:scripts/Views.Comics.dart' as Views;
import 'package:scripts/Views.AddComic.dart' as Views;

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

import 'package:hipster_mvc/hipster_sync.dart';

main() {
  // initialize code here
}
It feels a little henky being forced to declare my main entry point as a library, but I can probably learn to live with it.

What that buys me is the ability to keep all of the Dart code associated with this application in one place—the app/lib directory:
app
├── lib
│   ├── Collections.Comics.dart
│   ├── main.dart
│   ├── ModalDialog.dart
│   ├── Models.ComicBook.dart
│   ├── Views.AddComic.dart
│   ├── Views.AddComicForm.dart
│   └── Views.Comics.dart
├── packages
│   └── // stuff installed by Dart pub
├── pubspec.lock
├── pubspec.yaml
├── test
│   └── // client-side tests
└── web
    ├── index.html
    ├── scripts
    │   └── dart.js
    └── stylesheets
        └── style.css
Given the inline <script> import and the weirdness with declaring my main.dart entry point, is the organization improvement worth it? I think it probably is. The main reason is that, in addition to keeping all the Dart code in one place, it is easier to test. I can write a simple test that uses the same import statement for main.dart and get a functioning test:
import 'package:unittest/unittest.dart';
import 'package:unittest/html_enhanced_config.dart';

import 'package:scripts/main.dart' as App;

class TestModel {
  String get url => '/test';
}

main() {
  useHtmlEnhancedConfiguration();

  group('reading', () {
    test('empty when no data previously stored', (){
      var it = new TestModel();
      expect(
        App.localSync('read', it),
        completion(equals([]))
      );
    });
  });
}
Just like that, I have a test passing against my main entry point:


I will mull it over a bit more, but I think I like this approach.


Day #602

No comments:

Post a Comment