Wednesday, February 29, 2012

Dart Code Coverage (not quite implemented)

‹prev | My Chain | next›

I need to finish off some work for the beta release of Dart for Hipsters, but first must honor the gods of my chain, who have been so very good to me, with a (hopefully) brief link in the chain...

Because I love testing, have included a chapter on testing in Dart for Hipsters. This would not normally be that much of a stretch, but in a bleeding edge language like Dart, the testing harness is only available in the "bleeding_edge" branch. Now that's bleeding edge.

Surprisingly, this bleeding edge bleeding edge testing harness works fairly well and includes some worthwhile features. What it does not include is a test coverage tool even though it has a tab for it:


Checking the Dart console, I see:
Exception: NoSuchMethodException : method not found: 'getCoverageSummary'
Receiver: Instance of 'DARTest'
Arguments: []
Stack Trace:  0. Function: 'Object.noSuchMethod' url: 'bootstrap' line:669 col:3
 1. Function: 'DARTest._showCoverageControls@15eceda' url: 'file:///home/cstrom/repos/dart-comics/tests/lib/dartest/dartest.dart' line:436 col:61
 2. Function: 'DARTest.function' url: 'file:///home/cstrom/repos/dart-comics/tests/lib/dartest/dartest.dart' line:285 col:28
And indeed, the getCoverageSummary method is nowhere to be found in either the darttest or unittest paths. I eventually find it, not it bleeding_edge/dart/client/testing, but in bleeding_edge/dart/compiler/lib.

I export this path into my tests/lib directory (after installing subversion again):
svn export http://dart.googlecode.com/svn/branches/bleeding_edge/dart/compiler/lib lib/compiler
I have to edit bleeding_edge/dart/client/testing/darttest/darttest.dart to source the file defining getCoverageSummary():
// ...
#library('dartest');

#import('dart:dom');
#import('../unittest/unittest_dartest.dart');

#source('../compiler/coverage.dart');
#source('css.dart');
// ...
With that, I get something in the tab, but it is zero test coverage despite the fact that I do have some coverage:


After looking through some of the code in compiler/coverage.dart, I begin to suspect that this code simply holds coverage information, but does not actually perform the tracking (perhaps the compiler does this). In fact, back in my test code, I can use a couple of the methods to set the expected number of function calls, and pretend that I made that call:
// ...
main() {
  setCoverageTotals('foo', 1, 0, 0);
  coverFunction('foo', 'funcBar');

  test('HipsterCollection has multiple models', (){
  // ...
}
Now when I check coverage, I suddenly have 100% function coverage:


I have already warned readers that the testing chapter is bleeding edge, so I don't think it will come as a shock to them that the Coverage tab is not ready yet. Nevertheless, I am still excited enough by Dart testing to retain the chapter.


Day #311

Tuesday, February 28, 2012

Overloading Dart Operators for Great Evil

‹prev | My Chain | next›

One of the cool things about working through a brand new language like Dart is happening across weird little nooks and crannies of the language specification. One such example is the call operator which seems to be a way to get objects to behave like functions. Or I could be misreading the spec, I have a hard time with that kind of thing.

My best guess is that I can define an operator named call() to set this up, so I will try this out with my Hipster MVC framework. For now, I will make it an alias for save() on the HipsterModel class.

I have already defined other operator methods in Dart. In the HipsterModel, for instance, I have the square brackets operator that allows developers to lookup attributes in models as if the model were a Map (e.g. comic_book['title']). This is defined using the operator keyword:
class HipsterModel implements Hashable {
  // ...
  operator [](attr) => attributes[attr];
}
So I try doing something similar with call():
class HipsterModel implements Hashable {
  // ...
  operator call([callback]) {
    save(callback: callback);
  }
}
But, when I try to load the application that is build with Hipster MVC, I get a compile time error:
Internal error: 'http://localhost:3000/scripts/HipsterModel.dart': Error: line 23 pos 12: invalid operator overloading
  operator call([callback]) {
           ^
After fiddling a bit, I concede that perhaps I mis-read the spec. Perhaps call is just meant to represent the parens operator:
class HipsterModel implements Hashable {
  // ...
  operator ([callback]) {
    save(callback: callback);
  }
}
With that, I no longer receive a compile time warning. When I try to create a new model with something like:
new_model(callback:(event) {
  print("added a new model!");
});
I am greeted with a run time error:
2FIXME:1Exception: Object is not closure
Stack Trace:  0. Function: 'HipsterCollection.create' url: 'http://localhost:3000/scripts/HipsterCollection.dart' line:48 col:14
 1. Function: 'AddComicForm._submit_create_form@3b72b637' url: 'http://localhost:3000/scripts/Views.AddComicForm.dart' line:89 col:22
 2. Function: 'AddComicForm.function' url: 'http://localhost:3000/scripts/Views.AddComicForm.dart' line:24 col:26
 3. Function: 'HipsterView.function' url: 'http://localhost:3000/scripts/HipsterView.dart' line:41 col:15
 4. Function: 'EventListenerListImplementation.function' url: 'dart:htmlimpl' line:23128 col:35
Despite receiving my very favorite exception warning ("Object is not closure" seems to be little more than "something went wrong"), this is progress.

Maybe now is when I need call():
new_model.call(callback:(event) {
  print("added a new model!");
});
This changes the error, but I am still not quite right:
Exception: NoSuchMethodException : method not found: 'call'
Receiver: Instance of 'ComicBook'
Arguments: [Closure]
Stack Trace:  0. Function: 'Object.noSuchMethod' url: 'bootstrap' line:669 col:3
 1. Function: 'HipsterCollection.create' url: 'http://localhost:3000/scripts/HipsterCollection.dart' line:48 col:19
 2. Function: 'AddComicForm._submit_create_form@3b72b637' url: 'http://localhost:3000/scripts/Views.AddComicForm.dart' line:89 col:22
 3. Function: 'AddComicForm.function' url: 'http://localhost:3000/scripts/Views.AddComicForm.dart' line:24 col:26
 4. Function: 'HipsterView.function' url: 'http://localhost:3000/scripts/HipsterView.dart' line:41 col:15
 5. Function: 'EventListenerListImplementation.function' url: 'dart:htmlimpl' line:23128 col:35
I try:
new_model.(callback:(event) {
  print("added a new model!");
});
But get:
Internal error: 'http://localhost:3000/scripts/HipsterCollection.dart': Error: line 48 pos 15: identifier expected
    new_model.(callback:(event) {
              ^
Sigh. Re-reading the spec again, it really seems as though it ought to be call(). Perhaps it is not yet supported? I try a dummy operator to see if the message is the same, and indeed, "invalid operator overloading" seems to mean that the token is not an operator:
Internal error: 'http://localhost:3000/scripts/HipsterModel.dart': Error: line 23 pos 12: invalid operator overloading
  operator asdf([callback]) {
           ^
I try the latest version of Dartium only to find the same thing. Grr...

It is at this point that I finally stumble across a description of how call() will work in Dart. At the very top, it states that this is brand new and not implemented yet. Darn it. On the bright-side, it does seem that my understanding of how it will work is correct.

Regardless, I refuse to give up until I have done something tonight, no matter how ridiculous. So I overload the pipe operator for HispterModel so that it saves itself and pipes the response into the supplied callback:
class HipsterModel implements Hashable {
  // ...
  operator |(callback) => save(callback: callback);
}
I can then invoke this save/pipe like so:
new_model | (event) {
  print("added a new model!");
};
And that works.

It seems only fitting to end this post thusly: Mwah haha!


Day #310

Monday, February 27, 2012

noSuchMethod in Dart

‹prev | My Chain | next›

While writing the dynamic behaviors chapter in Dart for Hipsters, it occurred to me that I had skipped right over the noSuchMethod() method in my exploration of Dart. So tonight I revisit my Hipster MVC library to see if I can vary between local a remote storage with noSuchMethod().

I have yet to even try noSuchMethod(), but my guess is that it serves the same purpose as method_missing() in Ruby. That is, if a method is not explicitly defined in a class or any of its super classes, then noSuchMethod() should be invoked with the name of the method and the list of arguments. Furthermore, as a method on Object, it should be inherited by all classes. Then again, I could be completely off-base.

I glossed over the topic in Dart for Hipsters, but left the following psuedo-code as placeholder:
class HipsterModel {
  // ...
  noSuchMethod(name, args) {
    if (useLocal()) {
      _localSave(args);
    }
    else {
      _defaultSave(args);
    }
  }
}
Even if my assumptions are correct, I already see a mistake in that code—I am not checking the method name. It should only act if the method is expected, otherwise it ought to throw an exception:
class HipsterModel {
  // ...
  noSuchMethod(name, args) {
    if (name != 'save') {
      throw new NoSuchMethodException(this, name, args);
    }

    if (useLocal()) {
      _localSave(args);
    }
    else {
      _defaultSave(args);
    }
  }
}
To get there, I first rename save() as _defaultSave() and add a simplified noSuchMethod():
class HipsterModel implements Hashable {
  // ...
  noSuchMethod(name, args) {
    if (name != 'save') {
      throw new NoSuchMethodException(this, name, args);
    }

    _defaultSave(args);
  }

  _defaultSave([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);
  }
}
And that works. But not really.

It works in the sense that I do not see my NoSuchMethodException. It also works in the sense that _defaultSave() is invoked. Those two small victories mean that my understanding of noSuchMethod() is more or less correct—it is invoked if Dart is otherwise unable to resolve the requested method.

Things go awry inside the on.load event listener. After the record is saved via XHR, the event listener attempts to invoke the optional callback, with less than ideal results:
Exception: Object is not closure
Stack Trace:  0. Function: 'HipsterModel.function' url: 'http://localhost:3000/scripts/HipsterModel.dart' line:39 col:37
 1. Function: 'EventListenerListImplementation.function' url: 'dart:htmlimpl' line:23128 col:35
Ah, "Object is not closure", you're so helpful, you might as well say "Something went wrong".

What I suspect went wrong is how I invoked _defaultSave() from noSuchMethod(). I called _defaultSave(args) which I suspect is rather like calling _defaultSave() with a List.

The documentation for noSuchMethod says that args is a List, but I am calling save with optional parameter syntax:
    // ...
    new_model.save(callback:(event) {
      this.add(new_model);
    });
    // ...
What does Dart do with the optional label (callback in this case)? Since the result is a List, I can only assume that it ignores the optional parameter label, so I grab the first argument in noSuchMethod() and use that as the optional callback parameter to _defaultSave():
  noSuchMethod(name, args) {
    if (name != 'save') {
      throw new NoSuchMethodException(this, name, args);
    }

    _defaultSave(callback: args[0]);
  }
That does the trick—the Ajax save now works:


Armed with that knowledge, I can implement a stateful save() in noSuchMethod():
class HipsterModel implements Hashable {
  // ...
  bool useLocal() => true;

  noSuchMethod(name, args) {
    if (name != 'save') {
      throw new NoSuchMethodException(this, name, args);
    }

    if (useLocal()) {
      _localSave(callback: args[0]);
    }
    else {
      _defaultSave(callback: args[0]);
    }
  }

  _defaultSave([callback]) { /* ... */ }
}
Since useLocal() is hard-coded to true, I give it a try and, indeed, I am storing locally:


I am now well-armed to write the noSuchMethod() section. That said, the current usefulness is limited to methods with fixed arity. Optional parameters in Dart boast that position does not matter: new Comics(el: '#comics', collection: my_comics) is the same as new Comics(collection: my_comics, el: '#comics'). This would definitely not be the case with dynamic methods implemented by noSuchMethod. Whether this is by design to limit dynamic methods or if a newer implementation is on the way, I cannot say. But I will have to find out for the book...


Day #309

Sunday, February 26, 2012

Not Too Custom Events in Dart

‹prev | My Chain | next›

While writing the Events chapter in Dart for Hipsters, I got to thinking about the square brackets operator for the Element#on getter. I know that it works for the existing events, such as click events:
main() {
  Element el = document.query('#status');

  el.
    on['click'].
    add((event) {
      el.style.textDecoration = 'underline';
    });
}
Normally, I would write the above using the click getter:
  el.
    on.
    click.
    add((event) {
      el.style.textDecoration = 'underline';
    });
The square bracket operator approach allows for some run-time variance. I could also see adding the same event handler to a bunch of different events by iterating over the event names as strings.

Anyhow, with the square bracket version in place, when I load the page and click on the previously not underlined "Status!" header:


It is now underlined:


This got me to thinking about custom events. Since this is a "status" element, perhaps I can use this to listen to custom "status" events through the square brackets operator:
main() {
  Element el = document.query('#status');

  el.
    on['status'].
    add((event) {
      print("event received: ${event.type}");
      el.style.color = event.type;
    });
}
I try to dispatch this event by calling dispatch() on the event listener list returned by the square brackets:
  Event green_event = new Event('green');

  el.
    on['status'].
    dispatch(green_event);
Only nothing happens.

Firing this up in type checking mode, I am greeted by an exception indicating that the event type (the string "green" in this case) needs to be known by the event listener list:
Exception: 'dart:htmlimpl': Failed assertion: line 23081 pos 12: 'evt.type == _type' is not true.
Stack Trace:  0. Function: 'AssertionError._throwNew@127eafe4' url: 'bootstrap' line:552 col:3
 1. Function: 'EventListenerListImplementation.dispatch' url: 'dart:htmlimpl' line:23081 col:12
 2. Function: '::main' url: 'file:///home/cstrom/repos/dart-book/book/includes/events/main.dart' line:36 col:13
Dang.

Ah well, on the bright side, that is one less section that I need to write for the book.


Day #308

Saturday, February 25, 2012

Checked Dart Views

‹prev | My Chain | next›

Yesterday, I began exploring type checking in Dart by running my test suite in checked mode (starting Dartium with --enable_type_checks). It did not have quite the effect that I expected and I cannot say that it improved my test suite. So today, am going to have a type-checked look at some of my code that is not yet tested. Perhaps type checking will prove of more use there.

So I load up my application in test mode and... it still works:


When I try to delete one of those test comic books, however, I get a big old stack trace:
Exception: 'http://localhost:3000/scripts/HipsterModel.dart': Failed type check: line 27 pos 31: type 'Comics' is not assignable to type 'bool' of 'boolean expression'.
Stack Trace:  0. Function: 'HipsterModel.get:urlRoot' url: 'http://localhost:3000/scripts/HipsterModel.dart' line:27 col:31
 1. Function: 'HipsterModel.get:url' url: 'http://localhost:3000/scripts/HipsterModel.dart' line:25 col:18
 2. Function: 'HipsterSync._default_sync@2c7ab367' url: 'http://localhost:3000/scripts/HipsterSync.dart' line:50 col:31
 3. Function: 'HipsterSync.call' url: 'http://localhost:3000/scripts/HipsterSync.dart' line:24 col:27
 4. Function: 'HipsterModel.delete' url: 'http://localhost:3000/scripts/HipsterModel.dart' line:45 col:21
 5. Function: 'Comics.function' url: 'http://localhost:3000/scripts/Views.Comics.dart' line:49 col:48
 6. Function: 'HipsterView.function' url: 'http://localhost:3000/scripts/HipsterView.dart' line:35 col:15
 7. Function: 'EventListenerListImplementation.function' url: 'dart:htmlimpl' line:23128 col:35
Although the large stack trace is not indicative of a huge bug, there is a bug here. Specifically, I am using a ternary in a very non-Dart way:
class HipsterModel implements Hashable {
  // ...
  get urlRoot() => collection ?
    collection.url : "";
}
Dart has a funny view of truthiness—everything but the boolean true is false. So even if my collection is non-null, the above will always return an empty string. So, yay! Type checking found a bug. I address it by explicitly comparing to null:
class HipsterModel implements Hashable {
  // ...
  get urlRoot() => (collection == null) ?
    "" : collection.url;
}
And I can again delete comics from the application:


When I try to open the add form, I get another intimidating type check stack trace:
Exception: String expected
Stack Trace:  0. Function: 'ElementImplementation._querySelector@1c3f8015' url: '/mnt/data/b/build/slave/dartium-lucid64-inc/build/src/out/Release/obj/gen/webkit/bindings/dart/generated/dart/ElementImplementation.dart' line:144 col:3
 1. Function: 'ElementImplementation.querySelector' url: '/mnt/data/b/build/slave/dartium-lucid64-inc/build/src/out/Release/obj/gen/webkit/bindings/dart/generated/dart/ElementImplementation.dart' line:139 col:26
 2. Function: 'ElementWrappingImplementation.query' url: 'dart:htmlimpl' line:22910 col:51
 3. Function: 'HipsterView.HipsterView.' url: 'http://localhost:3000/scripts/HipsterView.dart' line:14 col:57
 4. Function: 'AddComicForm.AddComicForm.' url: 'http://localhost:3000/scripts/Views.AddComicForm.dart' line:8 col:5
 5. Function: 'AddComic._toggle_form@19ec418e' url: 'http://localhost:3000/scripts/Views.AddComic.dart' line:19 col:19
 6. Function: 'EventListenerListImplementation.function' url: 'dart:htmlimpl' line:23128 col:35
This stack trace leaves my code on line 14 of the HipsterView base class, which is the first line of the constructor:
class HipsterView {
  // ...
  HipsterView([el, this.model, this.collection]) {
    this.el = (el is Element) ? el : document.query(el);
    this.post_initialize();
  }
  // ...
}
This turns out to be a bit more subtle. When an instance of AddComicForm is created, I am not assigning the optional el parameter in the constructor. Thus el is null, which is not an Element, resulting in a document.query() of null. Since document.query() expects a String, I am now getting a type checking error.

In this particular case, it is not a bug because AddComicForm overrides post_initialize to create this.el. Thus, any other view operations will succeed because this.el is defined. But that is only true for this particular class. If someone else created a view with no el, there would be errors. What I really need is something like Backbone.js's ensureElement. I will worry about that another day. For now, I make the this.el assigment conditional on el being supplied and I assert that this.el has been defined after post_initialize:
class HipsterView {
  // ...
  HipsterView([el, this.model, this.collection]) {
    if (el != null) {
      this.el = (el is Element) ? el : document.query(el);
    }

    this.post_initialize();

    // TODO define an ensureElement to create a default, anonymous element
    assert(this.el != null);
  }
  // ...
}
I also leave myself a little TODO note to make this easier to use for others. If I comment out the post_initialize() method in my sub-class, I now get an assertion error:
Exception: 'http://localhost:3000/scripts/HipsterView.dart': Failed assertion: line 17 pos 12: 'this.el != null' is not true.
Stack Trace:  0. Function: 'AssertionError._throwNew@127eafe4' url: 'bootstrap' line:552 col:3
 1. Function: 'HipsterView.HipsterView.' url: 'http://localhost:3000/scripts/HipsterView.dart' line:17 col:12
 2. Function: 'AddComicForm.AddComicForm.' url: 'http://localhost:3000/scripts/Views.AddComicForm.dart' line:8 col:5
 3. Function: 'AddComic._toggle_form@19ec418e' url: 'http://localhost:3000/scripts/Views.AddComic.dart' line:19 col:19
 4. Function: 'EventListenerListImplementation.function' url: 'dart:htmlimpl' line:23128 col:35
With the post_initialize() method in place, I am again able to bring up the add-new-comic form:


Everything else seems to work. I can again add, delete and view my comics collection—even in type checking mode.

The type checking option seemed more of an annoyance with testing than a help, but it definitely seems to help when actually coding. Without it, I had allowed at least one bug to get into my Hipster MVC framework along some questionable choices. It seems that coding in type checking mode is good advice—even if most users will run in regular mode.


Day #307

Friday, February 24, 2012

Checked Dart Testing

‹prev | My Chain | next›

An intriguing feature of Dart is the ability to run the VM in checked mode. As I have coded Dart for the past couple of months, I have noticed a certain... looseness in its enforcement of types. It is not just that I avoided declaring any type information for the better part of the first month that I coded in Dart. I have also noticed that Dart is perfectly willing to let me declare a variable as one thing, but assign something else entirely to it.

I have come to regard types in Dart as little more than documentation. To be sure, it is very useful documentation, but since types are not enforced (as best I can tell), I can code very like I am used to in dynamic languages like Ruby and Javascript.

There are times, though, that it bothers me knowing that I could be missing out on bugs that loose typing might be allowing. That was part of the motivation of getting comfortable with testing these last couple of days, but still... I should not be able to assign a list of Maps:
  test('HipsterCollection has multiple models', (){
    HipsterCollection it = new HipsterCollection();
    it.models = [{'id': 17}, {'id': 42}];

    Expect.equals(2, it.length);
  });
Because I declared models as a list of HipsterModels:
class HipsterCollection implements Collection {
  List<HipsterModel> models;
//...
Or maybe I should. I am not wise in the way of typing so maybe that is perfectly OK. There is an easy way to find out—run my test suite in Dart's checked mode.

The current state of my test suite for the HipsterCollection in my Hipster MVC is all green:


As I said, I am reasonably sure that some of those should not be passing because of mismatched types. So I start up a recently downloaded copy of Dartium with the checked flags:
DART_FLAGS='--enable_type_checks --enable_asserts' dartium
When I reload and rerun my test suite, I do have a failure, but not exactly where I expected:


Running test:HipsterCollection add dispatch add event
  Result: ERROR Caught 'bootstrap_impl': Failed type check: line 1193 pos 14: type 'LinkedHashMapImplementation<Dynamic, Dynamic>' is not assignable to type 'HipsterModel' of 'value'.
  took 10 ms
Breakpoints do not seem to be working in the Dartium build that I have (r4584 based on the directory name in the download), so I resort to good old print-STDERR debugging:
Running test:HipsterCollection add dispatch add event
[asyncTest] HipsterCollection add
[add] list add
  Result: ERROR Caught 'bootstrap_impl': Failed type check: line 1193 pos 14: type 'LinkedHashMapImplementation<Dynamic, Dynamic>' is not assignable to type 'HipsterModel' of 'value'.
  took 11 ms
The last statement output comes from inside HipsterCollection:
class HipsterCollection {
  // ...
  add(model) {
    print("[add] list add");
    models.add(model);
    print("[add] add listern");
    on.add.
      dispatch(new CollectionEvent('add', this, model:model));
  }
  // ...
}
Interesting. It seems that I can overwrite the List<HipsterModel> models instance variable with a List<Map>:
  test('HipsterCollection has multiple models', (){
    HipsterCollection it = new HipsterCollection();
    it.models = [{'id': 17}, {'id': 42}];

    Expect.equals(2, it.length);
  });
But I cannot add a Map to List<HipsterModel> models instance variable. I do not quite understand this, but I do know how to resolve the failure. I need to import HispterModel into the test suite so that I can create a dummy sub-class:
#import("../public/scripts/HipsterModel.dart");

class TestHipsterModel extends HipsterModel {
  TestHipsterModel() : super({});
}
Now, I can add an instance of a HipsterModel to the collection:
 asyncTest('HipsterCollection add dispatch add event', 1, () {
    noOpSync(method, model, [options]) {}
    HipsterSync.sync = noOpSync;

    HipsterCollection it = new TestHipsterCollection();

    it.
      on.
      add.
      add((event) {
        callbackDone();
      });

    it.add(new TestHipsterModel());
  });
And I have my test suite passing again:


I don't know that I have really gained much through this exercise of typed checking testing. If anything, I was closer to using mocks in a situation in which it was fine to do so. I think tomorrow I will explore more of the code to see if there are any other potential benefits to this type checking stuff.


Day #306

Thursday, February 23, 2012

Async Testing in Dart

‹prev | My Chain | next›

Today, I continue exploring unit testing in Dart. I am using the bleeding edge in-browser testing suite, which has worked fairly well so far. It seems as though it is meant to be run right on top of the live application, which could be interesting—add a <script> tag and see the test suite results overlayed on the application.

I have tested most of the Collection aspects of the HipsterCollection class from my Hipster MVC framework. Tonight I hope to cover the Ajax features. First up, the fetch() method. I am not sure how to go about doing this, so I start by verifying that calling fetch() directly on the base class fails (a sub-class needs to define url()):
  test('HipsterCollection fetch() fails without a url', () {
    HipsterCollection it = new HipsterCollection();
    Expect.throws(() {it.fetch();});
  });
That succeeds, and by success, I have verified that an exception is being thrown:


Next, I define a dummy implementation of the HipsterCollection base class:
class TestHipsterCollection extends HipsterCollection {
  String get url() => 'test.json';
}
And try to write a spec that at least will not crash:
  test('HipsterCollection fetch() fetches url', () {
    HipsterCollection it = new TestHipsterCollection();
    it.fetch();
  });
This fails. And by "fails" I mean that I now have a failing test:


In the Dart console, I see a CORS exception:
XMLHttpRequest cannot load file:///home/cstrom/repos/dart-comics/tests/test.json. Cross origin requests are only supported for HTTP.
  Result: ERROR Caught Error: NETWORK_ERR: XMLHttpRequest Exception 101
That test.json file exists. Even if I load it in the test page via script tag, I still get a CORS violation.

I deal with with the same thing when I use Jasmine to test JS applications, but at least with those I can use libraries like sinon.js to fake XHR responses. I cannot do that in Dart without somehow telling my MVC library to use a different XHR object. Luckily, I can do just that.

Based on Backbone.sync, the HipsterSync class allows me to redefine the manner in which storage operations are performed. By default, it uses XHR, but I can swap that out for a no-op:
  test('HipsterCollection fetch() fetches url', () {
    HipsterSync.sync = (method, model, [options]) {
      print("[sync] $method / $model");
    };

    HipsterCollection it = new TestHipsterCollection();
    it.fetch();
  });
When I run the test suite now, I see the no-op print() output in the Dart console:
[sync] get / Instance of 'TestHipsterCollection'
And all of my tests are again passing:


They might be passing, but I am not really testing anything other than that the application does not crash. To be sure, there is value in that, but I can do better. The HipsterSync call is an asynchronous callback. Rooting through the bleeding edge test suite, I come across the asyncTest() function. This is not a vows.js-style assertion that a specific callback is invoked. Rather, it provides a mechanism to say that any number of callbacks will be invoked and a way to signify that they were actually called.

In this case, I expect that my sync callback will be invoked once. To verify that it has been called, I invoke callbackDone() inside the sync method:
asyncTest('HipsterCollection fetch() fetches via callback', 1, () {
    HipsterSync.sync = (method, model, [options]) {
      print("[sync] $method / $model");
      callbackDone();
    };

    HipsterCollection it = new TestHipsterCollection();
    it.fetch();
  });
And it works!


If I had specified an expectation of more callbacks than were actually invoked:
asyncTest('HipsterCollection fetch() fetches via callback', 42, () { /* ... */ });
, then the spec no longer passes:


Actually, that is not so much failing as preventing the suite from completing. That is better than nothing, but it would be preferable to say that, if the proper number of callbacks have not been invoked in a certain amount of time, then the test fails. Something to investigate another day.


Day #305

Wednesday, February 22, 2012

Testing Library Code in Dart

‹prev | My Chain | next›

Having gotten a dummy Dart unit test to pass last night, I try my hand at testing some of the Hipster MVC library code with which I have been playing.

First, I copy the testing directory from dart bleeding edge into my app. Tonight I store the contents of testing, the unittest and darttest sub-directories, under tests/lib in my main application directory:
➜  dart-comics git:(mvc-sync) ✗ tree -d tests 
lib
+-- tests
    +-- dartest
    |   +-- resources
    +-- unittest

4 directories
In tests, I recreate last night's index.html test runner:
<html>
<head>
  <script type="application/dart" src="HipsterCollectionTest.dart"></script>

  <script type="text/javascript">
    // start dart
    navigator.webkitStartDart();
  </script>
</head>

<body>
<h1>Test!</h1>

</body>
</html>
The HipsterCollectionTest.dart test file will, of course, test my collection base class. I start with a test skeleton to ensure that everything in the testing library is in the proper place:
#import('lib/unittest/unittest_dartest.dart');
#import('lib/dartest/dartest.dart');

main() {
  test('Test Description',(){
    Expect.equals(3, myAddFunc(1,2));
  });
  new DARTest().run();
}

int myAddFunc(int x, int y) => x + y;
And, indeed, when I open tests/index.html and run the tests, I see green:


Next, I #import() the HipsterCollection library and test the simplest thing that I can think of—that the collection holds multiple models:
#import('../public/scripts/HipsterCollection.dart');

main() {
  test('HipsterCollection has multiple models', (){
    HipsterCollection it = new HipsterCollection();
    it.models = [{'id': 17}, {'id': 42}];

    Expect.equals(2, it.length);
  });
  new DARTest().run();
}
Trying it out in the browser, it works:


To be sure I have not made a useless test, I intentionally break it:
    // ...
    Expect.equals(0, it.length);
    // ...
This results in red:


So I have succeeded in testing actual Dart code. Yay!

After adding a few more tests, I pine for two things: a better way to test Map equality and a way to group tests:
  test('HipsterCollection can lookup a model by ID', () {
    HipsterCollection it = new HipsterCollection();
    it.models = [{'id': 17}, {'id': 42}];

    Expect.listEquals([17], it[17].getValues());
    Expect.listEquals(['id'], it[17].getKeys());
  });

  test('HipsterCollection lookup is null when it does hold ID', () {
    HipsterCollection it = new HipsterCollection();
    it.models = [{'id': 17}, {'id': 42}];

    Expect.isNull(it[1]);
  });
Fortunately, bleeding edge test has a solution for the latter problem—I can group() tests:
  group('HipsterCollection lookup', () {
    HipsterCollection it = new HipsterCollection();
    it.models = [{'id': 17}, {'id': 42}];

    test('works by ID', () {
      Expect.listEquals([17], it[17].getValues());
      Expect.listEquals(['id'], it[17].getKeys());
    });

    test('is null when it does not hold ID', () {
      Expect.isNull(it[1]);
    });
  });
Nice! I can reuse the same test subject, it for both lookup tests. I am not a fan of indirection in tests, but I do not mind simple re-use where possible.

I am in something of a bind with comparing Maps because the following fails:
  test('map equality', () {
    Expect.equals({'foo': 'bar'}, {'foo': 'bar'});
  });
I settle for declaring model variables and testing that the results of collection lookup is the same as the original model:
  group('HipsterCollection lookup', () {
    var model1 = {'id': 17},
        model2 = {'id': 42};

    HipsterCollection it = new HipsterCollection();
    it.models = [model1, model2];

    test('works by ID', () {
      Expect.listEquals([17], it[17].getValues());
      Expect.listEquals(['id'], it[17].getKeys());
      Expect.equals(model1, it[17]);
    });
    // ...
  });
Even with that passing, I opt to leave the two listEquals for the keys and values of the Map—the output on failure is easier to read.

This will do as a good stopping point for tonight. The remainder of HipsterCollection is Ajax or Event related. That is going to require a different kind of testing. Tomorrow.


Day #304

Tuesday, February 21, 2012

Getting Started with Dart Testing

‹prev | My Chain | next›

I am somewhat test obsessed. I have been exploring Dart for nearly 2 months and have not even considered testing, which is why I am only somewhat test obsessed. But tonight I can ignore the nagging doubts no longer.

One of the reasons that I have yet to do much with testing is that Dart does not bundle a unit testing framework—at least not yet. There is some test framework stuff in the bleeding edge at: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/client/testing/.

So first, I install subversion (seriously, who still uses subversion?):
sudo apt-get install subversion
Then I can checkout the testing path:
➜  tmp  svn co http://dart.googlecode.com/svn/branches/bleeding_edge/dart/client/testing/
A    testing/unittest
A    testing/unittest/shared.dart
A    testing/unittest/dom_for_unittest.js
A    testing/unittest/unittest.dart
A    testing/unittest/coverage_controller.js
A    testing/unittest/unittest_vm.dart
A    testing/unittest/dom_for_unittest.dart
A    testing/unittest/test_controller.js
A    testing/unittest/unittest_node.dart
A    testing/unittest/unittest_dartest.dart
A    testing/dartest
A    testing/dartest/dartest.dart
A    testing/dartest/css.dart
A    testing/dartest/resources
A    testing/dartest/resources/expand.png
A    testing/dartest/resources/min.png
A    testing/dartest/resources/close.png
A    testing/dartest/resources/max.png
A    testing/dartest/resources/img2base64.py
A    testing/dartest/README.txt
Checked out revision 4421.
Lastly, I remove subversion, just on principle:
➜  tmp  sudo apt-get remove subversion
It seems as though this is meant to be run in the browser (I would hazard a guess that it needs to be Dartium)—the README mentions an overlay window and the libraries make use of dart:html. So I create a test HTML in the testing directory with a reference to the Dart code being tested:
<html>
<head>
  <script type="application/dart" src="test.dart"></script>

  <!-- Start the dart engine -->
  <script type="text/javascript">
    if (navigator.webkitStartDart) {
      navigator.webkitStartDart();
    }
  </script>
</head>
<body>
<h1>Test!</h1>

</body>
</html>
As for the test.dart test code, I copy the #import() statements and test from the README (the myAddFunc() function is my own!):
#import('unittest/unittest_dartest.dart');
#import('dartest/dartest.dart');

main() {
  test('Test Description',(){
    Expect.equals(3, myAddFunc(1,2));
  });
  new DARTest().run();
}

int myAddFunc(int x, int y) => x + y;
Loading this up in Dartium, I see an overlay for test results, but no actual results. I also see a bunch of red in the Javascript, er... Dart console:


In the hopes that this a function of using an older version of Dartium, I grab the latest available from the Dartium download page and try again. Unfortunately, I receive the same error:
Exception: String expected
Stack Trace:  0. Function: 'NodeImplementation.set:textContent' url: '/mnt/data/b/build/slave/dartium-lucid64-inc/build/src/out/Release/obj/gen/webkit/bindings/dart/generated/dart/NodeImplementation.dart' line:52 col:3
 1. Function: 'DARTest._addTestDetails@5f927d8' url: 'file:///home/cstrom/repos/dart-comics/testing/dartest/dartest.dart' line:131 col:24
 2. Function: 'DARTest.function' url: 'file:///home/cstrom/repos/dart-comics/testing/dartest/dartest.dart' line:80 col:22
 3. Function: 'GrowableObjectArray.forEach' url: 'bootstrap_impl' line:1303 col:8
 4. Function: 'DARTest._createResultsTable@5f927d8' url: 'file:///home/cstrom/repos/dart-comics/testing/dartest/dartest.dart' line:76 col:18
 5. Function: 'DARTest.run' url: 'file:///home/cstrom/repos/dart-comics/testing/dartest/dartest.dart' line:58 col:24
 6. Function: '::main' url: 'file:///home/cstrom/repos/dart-comics/testing/test.dart' line:8 col:20
Line 131 for darttest.dart attempts to assign testId.textContent:
  void _addTestDetails(TestCase t, HTMLTableRowElement row) {
    HTMLTableCellElement testId = _runnerWindow.document.createElement('td');
    testId.textContent = t.id;
    // ...
  }
Since the exception states that a string was expected, the smallest change that might do something is to tack on a toString() call:
  void _addTestDetails(TestCase t, HTMLTableRowElement row) {
    HTMLTableCellElement testId = _runnerWindow.document.createElement('td');
    testId.textContent = t.id.toString();
    // ...
  }
That resolves the stack trace, but I am not quite sure that it worked. The results overlay seems oddly free of green:


Ah ha! It seems that I need to manually run my test suite by clicking on that play button. When I do so, I see green:


If I intentionally break the test:
  test('Test Description',(){
    Expect.equals(42, myAddFunc(1,2));
  });
Then, when I reload and run my suite again, I see red. I can even click on the failing spec to get a good idea what went wrong:


That is a good stopping point for today. The Coverage tab is not working for me, so I may pick up with that tomorrow. Then again, I am eager to see how this plays with live code, so I may ignore the Coverage tab for a little while longer.


Day #303

Monday, February 20, 2012

Dart Iterable

‹prev | My Chain | next›

Up tonight, hopefully a quick exploration of iterators in Dart. Iterators are the mechanism in Dart that allow any object to be used in a for-in block. I do not necessarily think that my ImageCarousel class needs an iterator to do its thing, but it is the most recent collection-like thing that I have to play with, so...

First up, I define the carousel as extending Iterable:
class ImageCarousel implements Iterable {
  // ...
}
To actually implement Iterable, I need to define an iterable() method:
class ImageCarousel implements Iterable {
  // ...
  iterator() => new ImageCarouselIterator(this);
}
That iterator need only define two methods: hasNext(), a boolean method that returns true unless there are no more elements to return, and next(), which return the next element.

In addition to those two methods, I am going to need a pointer, for which I employ the _pos instance variable. I also need a pointer to the object being iterated:
class ImageCarouselIterator implements Iterator {
  ImageCarousel imageCarousel;
  int _pos;

  ImageCarouselIterator(this.imageCarousel) { _pos = -1; }
}
I am really coming around to the this.XXX constructor parameter format in Dart. It is a crazy easy way to say that the first argument passed to the constructor (in this case an instance of ImageCarousel from its iterator() method), should be assigned to the imageCarousel instance variable. In addition to assigning the instance variable, I also initialize the position of the iterator to -1 so that the next element will be the zeroeth of the array in ImageCarousel (ah, Array indexing).

Next, I define the hasNext() boolean function. That is easy enough—as long as _pos is before the last index in the array, there is a next element to be returned:
class ImageCarouselIterator implements Iterator {
  // ...
  bool hasNext() => _pos < lastIndex;
  int get lastIndex() => imageCarousel.length-1;
}
I am also starting to really dig the hash-rocket short hand for function and method bodies—I can skip the return keyword and the curly braces (win, win). Because arrays start at zero, the lastIndex of an array is the length minus 1. I define a getter for this solely to make the hasNext() function easier to read.

Last up, I implement the next() method, which return the element at _pos as well as increments _pos for the next time through. I also have to throw an exception if next() has navigated past the bounds of the object being iterated:
class ImageCarouselIterator implements Iterator {
  // ...
  ImageCarousel next() {
    if (_pos++ >= lastIndex) throw new NoMoreElementsException();

    return imageCarousel[_pos];
  }
}
Again, that lastIndex getter comes in handy. Or at least makes things a little easier on the eye.

One last thing that I need to do is define the [] operator and length methods on the original ImageCarousel class:
class ImageCarousel implements Iterable {
  // ...
  iterator() => new ImageCarouselIterator(this);
  String operator [](i) => image_urls[i];
  int get length() => image_urls.length;
}
Have I mentioned yet, that I really, really like the hash-rocket method notation?

Anyhow, with ImageCarousel now implementing Iteratable properly with a real iteratable() method, I can give it try with a for-in loop in my main() entry point:
main() {
  // ...
  var gallery = new ImageCarousel(gallery_el, urls);

  for (var img in gallery) {
    print(img);
  }
}
And it works! In my console, I see the names of the images printed out:


It is pretty cool to be able to make any object for-in capable. But not as cool as that hash-rocket method syntax. That's just awesome.


Day #302

Sunday, February 19, 2012

Darty Animation

‹prev | My Chain | next›

Last night, I was able to get my Dart-based image carousel more or less working:

video

When the image on the right appears, it immediately covers the image that used to be there before it has a chance to rotate. The effect is less than desirable. Part of the problem is that I am starting the carousel at image #3 instead of #4:


The carousel achieves this effect by drawing each face of the carousel one at a time. When the carousel moves to the next image, it moves each image on the carousel. The animation effect comes from the CSS3 transform and translate transitions:
    // ...
    img.style.transition = 'all 1s ease-in-out';
    img.style.transform = 'rotateY(-45deg) translateZ(${_depth()}px)';
    // ...
With each move, I apply a different rotateY to the image. The transition takes care of the rest. Before fixing the right image, I factor the image drawing functionality into a single private method:
  _draw(offset, angle) {
    var img = imageFor(image_urls[_pos + offset]);

    img.style.transition = 'all 1s ease-in-out';
    img.style.position = 'absolute';
    img.style.transformStyle = 'preserve-3d';
    img.style.opacity = '0.8';
    img.style.transform = 'rotateY(${angle}deg) translateZ(${_depth()}px)';

    return img;
  }
With that, the five faces of the visible carousel can be expressed quite simply as:
  _draw0() => _draw(0, '-90');
  _draw1() => _draw(1, '-45');
  _draw2() => _draw(2, '0');
  _draw3() => _draw(3, '45');
  _draw4() => _draw(4, '90');
I really dig that compact dart function/method syntax.

The externally facing draw() is expressed as:
  void draw() {
    _draw0();
    _draw1();
    _draw2();
    _draw3();
    _draw4();

    // make sure old images don't linger
    _removeOld();
  }
Finally, I have a next() method that rotates things by incrementing the internal _pos counter and telling the carousel to draw itself:
  void next() {
    _pos++;
    draw();
  }
Actually starting at #4 instead of #3 helps, but there is still some bleed though of #4 superimposed over #3. To resolve that, I start #4 with zero opacity and give #3 an opacity of 0.8. For good measure, I give #0 an opacity again of zero so that there is no abrupt disappearance when the image is removed. Since the _draw() method returns an Image element, and since the shorthand syntax for _draw0 through _draw4() returns the result on the right-hand side of the hash-rocket (=>) operator, I can apply these opacities in the draw() method like so:
  void draw() {
    _draw0().style.opacity = '0';
    _draw1();
    _draw2();
    _draw3().style.opacity = '0.8';
    _draw4().style.opacity = '0';

    _removeOld();
  }
When an image is added in slot #4, it has opacity zero. When it rotates to #3, it is assigned opacity of 0.8, which it retains as it move to #2 and #1. Finally, when it reaches #0, its opcacity is again set to zero where it can be safely removed from the UI.

The effect is much nicer:

video

After adding a prev() method to my carousel, I swap out the auto-rotating for a rotate by arrow key handler:
main() {
  final gallery_el = document.query("#gallery");
  final urls = images.
    split("\n").
    filter((img) { return !img.isEmpty(); }).
    map((img) { return "images/${img}"; });

  var gallery = new ImageCarousel(gallery_el, urls);

  document.on.keyUp.add((event) {
    if (event.keyCode == 37) gallery.prev();
    if (event.keyCode == 39) gallery.next();
  });
}
Last up tonight, I try compiling this into Javascript using Dart's frogc compiler. It works, but nothing displays, at least not under Firefox version 10. I trace this to frogc's use of -moz-transform-style in the generated JS. If I remove that:
// ...
CSSStyleDeclarationWrappingImplementation.prototype.set$transformStyle = function(value) {
//  this.setProperty(("" + CSSStyleDeclarationWrappingImplementation.get$_browserPrefix() + "transform-style"), value, "");
  this.setProperty(("" + "transform-style"), value, "");
}
Then it works, although the images are not scaled quite a smoothly in Firefox as they were in Dartium:


All in all, it was relatively easy to get this carousel working in Dart. It took me longer to figure out the necesary CSS3 styles than it did to get the Dart coded.


Day #301

Saturday, February 18, 2012

Rotating Image Carousel in Dart

‹prev | My Chain | next›

For the past few days I have been enjoying playing with the concept of an image gallery in Dart. Actually, it is as much playing with experimental CSS3 features as it has been Dart, but regardless, it has been fun.

I had gotten the gallery working to the point that I could move the main image off to the side, shrink and blur it:


The animation was all done with simple CSS3 (well, some of it was really CSS1):
removeEl(el) {
  if (el == null) return;

  el.style.transition = 'all 1s ease-in-out';

  el.style.left = '550px';
  el.style.height = '150px';
  el.style.width = '250px';
  el.style.filter = 'blur(3px)';

  window.setTimeout(() {
    el.remove();
  }, 7*1000);
}
Instead of doing something like that, I would like to make a more realistic carousel. Using CSS3 transform and translate, I ought to be able to make the images rotate about, like a carousel or merry-go-round. So I break things up in to an ImageCarousel class:
class ImageCarousel {
  Element el;
  List image_urls;

  int panelWidth = 250;
  int _pos = 0;

  Map _image_cache;

  ImageCarousel(this.el, this.image_urls) {
    _image_cache = {};

    el.style.perspective = '1000px';

    draw();
  }
}
I am going to use panels on this carousel of width 250 pixels -- that should yield a carousel that is a little larger than 500 pixel wide, depending on perspective. For perspective, or the distance that the user is from the page, I use 1000 pixels.

I instanstiate this object with:
main() {
  final gallery_el = document.query("#gallery");
  final urls = images.
    split("\n");

  var gallery = new ImageCarousel(gallery_el, urls);

  window.setInterval(() {
    gallery.next();
  }, 3*1000);
}
The images are a newline separated list of image URLs. The #gallery element is a simple <div> tag on the page.

After the object is created, the constructor calls the draw() method. The draw() method calls five private draw methods that draw each side of the carousel:
void draw() {
    _draw0();
    _draw1();
    _draw2();
    _draw3();
    _draw4();
  }
Each of which looks something like:
_draw1() {
    var img = imageFor(image_urls[_pos + 1]);

    img.style.transition = 'all 1s ease-in-out';
    img.style.position = 'absolute';
    img.style.transformStyle = 'preserve-3d';
    img.style.transform = 'rotateY(-45deg) translateZ(${_depth()}px)';
  }
I am still using the same transition of 1 second, easing into and out of motion. I "preserve-3d" so that the images do not look flat on the page. And lastly, I rotate the images, depending where on the carousel they happen to be. The actual 3D transform is done with CSS3 transforms. Here, I am rotating the image about the Y axis. The translate along the z-axis (out of the page) is necessary because the images to the left and right of the main image are rotated about the center of the image. That is, it would look as if one edge were sticking out of the page. To get everything looking OK, I have to ensure that the images behave as if they are a certain distance away from the center. The _depth() function is swiped from the very cool Intro to CSS 3D transforms article.

And it works. Kind of:

video

I need to improve the rotate-in transform so that it does not seem as though it overwrite the previous image, only for the previous image to pop out. Still, this is a pretty good start. I think tomorrow I will take some time to make it a little more Darty.


Day #300

Friday, February 17, 2012

Multi-Transition Dart

‹prev | My Chain | next›

Today I continue to play with last night's images gallery. I hope to combine a few more animations together, if for no other reason than to see how easy it is in Dart. But first...

I keep coming across the #resource directive in the dart language spec. The spec states that they are used to "include extralinguistic resources (e.g., audio, video or graphics files)", which seems like they might be useful for an image gallery.

So I add some #resource directives to my main.dart file:
#library('image gallery');

#resource('images/01-4_failures_to_go.png');
#resource('images/01-add_comix_form.png');
#resource('images/01-add_from_event.png');
#resource('images/01-anumation.png');
#resource('images/01-asdf_comic.png');
#import('dart:html');

main() {
  // ...
}
When I load my gallery however, I find:
Internal error: 'file:///home/cstrom/repos/dart-gallery/scripts/main.dart': Error: line 3 pos 10: Unrecognized library token
#resource('images/01-4_failures_to_go.png');
I try fiddling with the order a bit, but it seems pretty clear that, even though #resource() is in the spec, it it not yet implemented in Dartium.

Back to my regularly scheduled programming then...

The current gallery combines two animations as it moves through the list of images in the gallery. The two animations are mirrors of each other with the new image fading in while the old one fades out:

video

Tonight I am going to change the remove animation to shift the image to the right and behind the main image, making it smaller as it goes. I think this ought to be easy, but "ought" is usually the bane of my learning. And by bane, I mean it usually goes wildly wrong and I end up learning something.

The simple version of the remove element animation is:
removeEl(el) {
  if (el == null) return;

  el.style.transition = 'opacity 1s ease-in-out';
  el.style.opacity = "0";
  window.setTimeout(() {el.remove();}, 1000);
}
I try to convert this to a reusable function:
animateEl(el, property, end) {
  el.style.transition = '$property 1s ease-in-out';
  el.style[property] = end
}
But that does not work for two reasons. First, the style property of a Dart element does not support a [] operator. Second, I am overwriting the transition property each time I use this function.

Instead I end up using the "all" property, eventually settling on the following for my animation:
removeEl(el) {
  if (el == null) return;

  el.style.position = 'absolute';
  el.style.transition = 'all 2s ease-out';

  el.style.left = '550px';
  el.style.height = '150px';
  el.style.width = '250px';
  el.style.opacity = '0.5';

  window.setTimeout(() {
    el.remove();
  }, 7*1000);
}
Which does the trick:

video


Hrm.. I think I can turn this into a carousel. With controls and everything. Sounds like a project for tomorrow.

Day #299

Thursday, February 16, 2012

Simple Image Gallery in Dart

‹prev | My Chain | next›

Adam Smith has a very nice canvas-based image gallery written in Dart (may take a while to load) up on the githubs. I am still undecided about including SVG / canvas stuff in Dart for Hipsters, but I do think a chapter on animation is in order. I have already experimented with simple animations in Dart, this seems like a good chance to do something a little more interesting.

I start with a list of images:
var images = """
01-4_failures_to_go.png
01-add_comix_form.png
01-add_from_event.png
01-anumation.png
01-asdf_comic.png
01-back_to_funky.png
01-calendar_full_o_appointments.png
// ...
01-whoopsies.png
01-working_in_firefox.png
""";
In my main entry point, I split that string list into an array and create a queue:
main() {
  var q = new Queue.from(images.split("\n"));

  showNext(q);
}
The showNext() function can then pop the first element off the stack, wait for a period of time, and then repeat:
showNext(queue, [last_el]) {
  if (last_el != null) {
    last_el.remove();
  }

  var el = new Element.html("""<img src="images/${queue.removeFirst()}"/>""");
  document.query('#gallery').nodes.add(el);

  if (!queue.isEmpty()) {
    window.setTimeout(() {showNext(queue, last_el: el);}, 1500);
  }
}
And that is enough to get started:


Update: I had a heck of a time getting an animation transition working with this. I factored adding and removing the element out into functions that add/remove with animation:
showNext(queue, [last_el]) {
  removeEl(last_el);

  var el = new Element.html("""<img src="images/${queue.removeFirst()}"/>""");
  addEl(el);

  if (!queue.isEmpty()) {
    window.setTimeout(() {showNext(queue, last_el: el);}, 5*1000);
  }
}
The remove effect worked fine:
removeEl(el) {
  if (el == null) return;

  el.style.transition = 'opacity 1s ease-in-out';
  el.style.opacity = "0";
  window.setTimeout(() {el.remove();}, 1000);
}
I am just doing a simple "ease-in-out" (start and end slow) animation. The problem is the addEl() animation:
addEl(el) {
  el.style.position = 'absolute';
  el.style.opacity = "0";
  document.query('#gallery').nodes.add(el);

  el.style.transition = 'opacity 1s ease-in';
  el.style.opacity = "1";
}
The problem? There is no animation. Instead the element displays immediately. Since it is the newly added element, it immediately covers up the old element. After way too much fiddling, I find that a timeout, even of only a couple of milliseconds allows the transition to work:
addEl(el) {
  el.style.position = 'absolute';
  el.style.opacity = "0";
  document.query('#gallery').nodes.add(el);

  window.setTimeout(() {
    el.style.transition = 'opacity 1s ease-in';
    el.style.opacity = "1";
  }, 2);
}
I lack a good explanation for this. Perhaps it has something to do with adding nodes, but setting the style on elements.

video


Day #298

Wednesday, February 15, 2012

HipsterSync: Swapping MVC Storage Behavior in Dart

‹prev | My Chain | next›

Last night I figured out how to inject shared behavior in Dart. Today I need to use that knowledge to support an injectable sync in my HipsterMVC framework.

First up, I convert yesterday's proof of concept to something resembling what I envision as the final product. The HipsterSync class exposes a setter (sync=) allowing application code to inject behavior, and a method for internal libraries to invoke the appropriate behavior. I start with:
#library('Sync layer for HipsterMVC');

class HipsterSync {
  // private class variable to hold an application injected sync behavior
  static var _injected_sync;

  // setter for the injected sync behavior
  static set sync(fn) {
    _injected_sync = fn;
  }

  // static method for HipsterModel and HipsterCollection to invoke -- will
  // forward the call to the appropriate behavior (injected or default)
  static call(method, model, [options]) {
    if (_injected_sync == null) {
      return _default_sync(method, model, options:options);
    }
    else {
      return _injected_sync(method, model, options:options);
    }
  }

  // default sync behavior
  static _default_sync(method, model, [options]) {
    print("[_default_sync]");
  }
}
In my HipsterCollection base class, I then remove the local storage that I had been doing, replacing it with HipsterSync.call():
class HipsterCollection implements Collection {
  // ...
  fetch() {
    HipsterSync.call('get', this);
  }
  // ...
}
When I load my app, I see the print() tracer bullets from the current _default_sync() implementation:


Then, in my main.dart entry point, I inject a different sync behavior:
main() {
  HipsterSync.sync = (method, model, [options]) {
    print("[new_sync] $method");
  };
  // ...
}
When I load my app now, I see the injected behavior:


Nice. My proof of concept is closer to being reality. Next, I need to do something real with the default implementation. I remove the injected behavior. Then, back in HipsterSync, I add the Ajax that used to be in HipsterCollection#fetch():
#import('dart:html');

class HipsterSync {
  // ...
  // default sync behavior
  static _default_sync(method, model, [options]) {
    print("[_default_sync]");

    var req = new XMLHttpRequest();

    req.on.load.add(_handleOnLoad);
    req.open('get', model.url, true);
    req.send();
  }
}
Ah. That _handleOnLoad callback method is defined back in the collection class. I need a mechanism to pass that into HipsterSync. Those options look like a good place to start. In HipsterCollection, pass the on-load callback via an 'onLoad' key:
  fetch() {
    HipsterSync.call('get', this, options: {
      'onLoad': _handleOnLoad
    });
  }
Then in HipsterSync, I add the value (_handleOnLoad) to the list of on.load event handlers for the Ajax request:
  // default sync behavior
  static _default_sync(method, model, [options]) {
    print("[_default_sync]");

    if (options == null) options = {};

    var req = new XMLHttpRequest();

    if (options.containsKey('onLoad')) {
      req.on.load.add(options['onLoad']);
    }
    req.open('get', model.url, true);
    req.send();
  }
Trying this out, it works and I have my application again loading my comic book collection over Ajax:


Now for the moment of truth—I define a local storage sync behavior in main.dart:
main() {
  HipsterSync.sync = (method, model, [options]) {
    print("[local_sync] $method");

    if (method == 'get') {
      var json =  window.localStorage.getItem(model.url),
          data = (json == null) ? {} : JSON.parse(json);

      if (options is Map && options.containsKey('onLoad')) {
        options['onLoad'](data.getValues());
      }
    }
  };
  // ...
}
In here, I have adapted my local storage solution. In order for both local storage and Ajax to submit data to the on-load callback, I had to alter the callback to expect data rather than an event from which the data could be extracted. Once that is done, the injected behavior of local storage is working:


Cool! I can switch back and forth between local storage and the default Ajax implementation by adding and removing the HipsterSync.sync local behavior. I still need to get all of this working with my model class, but hopefully that will prove relatively easy now that I have my collection working properly.


Day #297

Tuesday, February 14, 2012

Injecting Shared Behavior in Dart

‹prev | My Chain | next›

At the B'more on Rails meetup tonight, Nick Gauthier, my Recipes with Backbone co-author, included an interesting discussion on frameworks vs. libraries. He pointed out that frameworks generally call user written application code whereas libraries encourage application code to call library code (usually through inheritance). It occurs to me that this is the crux of my recent struggles with my Dart MVC framework—I am having a hell of a time injecting a sync() method for my model and collection classes to call.

I had planned on giving this a miss, but, partly egged on by Nick and partly by comments from Justin Fagnani in last night's post, I am going to try again.

My first attempt tonight focuses on using a global variable to hold this information. I do not expect to stick with a global, but just hope to use it as a proof of concept. First, I establish a "internal" version of a variable that will point to my sync function:
var internal_sync = null;
Then I define an externally facing setter function for the internal version:
var internal_sync = null;
set sync(fn) {
  internal_sync = fn;
}
In my framework, internal_sync would have a default Ajax implementation, but the developer could inject different behavior by setting a different function via the external sync= setter.

And this works, to a certain extent. In my main() entry point, I can set sync and invoke the new behavior:
var internal_sync = null;
set sync(fn) {
  internal_sync = fn;
}

main() {
  sync = (arg) {
    print("[new_sync] $arg");
    return "hip new behavior";
  };
  print("sync: ${internal_sync('Injected, yo')}");
}
(try.dartlang.org)

Inside main() I set the sync= setter to a function that prints out an argument and returns a "hip new behavior". I then invoke the function that is now referred to by the internal_sync global variable. And it works. I see the set function being called and I see the "hip new behavior":
[new_sync] Injected, yo
sync: hip new behavior
The problem is that this internal_sync variable is not available inside libraries. For instance, the Collections.Comics.dart library is imported and used in main() after the updated internal_sync is set:
#import('Collections.Comics.dart', prefix: 'Collections');
// ...

var internal_sync = null;
set sync(fn) {
  internal_sync = fn;
}

main() {
  sync = (arg) {
    print("[new_sync] $arg");
    return "hip new behavior";
  };
  print("sync: ${internal_sync('Injected, yo')}");

  var my_comics_collection = new Collections.Comics();
 
  my_comics_collection.fetch();
  // ...
}
In turn, the Comics class imports and extends HipsterCollection from my framework:
#import('HipsterCollection.dart');

class Comics extends HipsterCollection {
  // ...
}
And, in HipsterCollection, I try to access the global internal_sync variable:
class HipsterCollection implements Collection {
  // ...
  fetch() {
    print("[fetch] sync: ${internal_sync}");
    // ...
  }
}
Unfortunately, I am denied:
Exception: NoSuchMethodException - receiver: 'Instance of 'Comics'' function name: 'get:internal_sync' arguments: []]
Stack Trace:  0. Function: 'Object.noSuchMethod' url: 'bootstrap' line:665 col:3
 1. Function: 'HipsterCollection.fetch' url: 'http://localhost:3000/scripts/HipsterCollection.dart' line:54 col:41
 2. Function: '::main' url: 'http://localhost:3000/scripts/main.dart' line:23 col:29
I get similar results if I try to treat this internal_sync as a function.

It is not possible to declare variables before #import() statements in Dart, nor is it possible to include code via #source() before #import(). So, if the problem is that the global is declared too late, then I am out of luck.

I switch tactics at this point. If a global variable will not work, perhaps a class methods will. So I define a HipsterSync class in HipsterSync.dart with a familiar class variable and class setter:
#library('Sync layer for the Hipster MVC');

class HipsterSync {
  static var internal_sync;

  static set sync(fn) {
    internal_sync = fn;
  }
}
I can then import and set HipsterSync.sync= in main():
// ...
#import('HipsterSync.dart');

main() {
  HipsterSync.sync = (arg) {
    print("[new_sync] $arg");
    return "hip new behavior";
  };
  print("sync: ${HipsterSync.internal_sync('Injected, yo')}");
  // ...
}
Finally, I import and attempt to use HipsterSync in my collection base class:
// ...
#import('HipsterSync.dart');

class HipsterCollection implements Collection {
  // ...
  fetch() {
    print("[fetch] sync: ${HipsterSync.internal_sync('fetch, yo')}");
    // ...
  }
  // ...
}
And... that does the trick! In the Dart console, I see the output from main():
[new_sync] Injected, yo
sync: hip new behavior
Followed immediately by the output from the newly set HipsterSync.sync when invoked by the collection's fetch() method:
[new_sync] fetch, yo
[fetch] sync: hip new behavior
It took me a while to figure that out (and much thanks to Justin Fagnani for pointing me in the right direction), but I am finally able to inject model and collection syncing behavior into my MVC framework. Using static methods to set and hold this behavior also ensures that the behavior can be shared between both the collection and model classes. I am excited.


Day #296