Up tonight, I document and, if necessary, polish off HispterModel, the model class in my Dart Hipster MVC library.
I see that I am optionally allowing the collection to be set on the model (it's useful for delegating the backend URL):
class HipsterModel implements Hashable {
// ...
HipsterModel(this.attributes, [this.collection]) { /* ... */ }
}This was a bit of premature optimization on my part as I am not using the option collection parameter anywhere. Even when the collection creates a model, it assigns itself after the model is built:class HipsterCollection implements Collection {
// ...
_buildModel(attrs) {
var new_model = modelMaker(attrs);
new_model.collection = this;
return new_model;
}
}If this ever becomes useful, I can add it back or define a named constructor (e.g. new ComicBook.withCollection(comic_collection)). For now, I remove it.This leads me to wonder again about the model subclass. Currently, I have to define a redirection to
super() in all subclasses:class ComicBook extends HipsterModel {
ComicBook(attributes) : super(attributes);
}I would prefer to only define that as necessary. In other words, I would like to reduce the minimal HipsterModel subclass to:class ComicBook extends HipsterModel {}
In this case, Dart has an implied redirection constructor that effectively calls the superclass constructor with no arguments. In other words, the above is the same as defining the subclass as:class ComicBook extends HipsterModel {
ComicBook(): super();
}As defined now, HispterModel will not support this because I still require attributes. This will work if I make attributes optional:class HipsterModel implements Hashable {
/// The internal representation of the record.
Map attributes;
HipsterModel([this.attributes]) {
on = new ModelEvents();
if (attributes == null) attributes = {};
}
// ...
}I still have the option to define a constructor on ComicBook that takes an argument, but now I do not have to do so. Since I do not, my modelMaker() factory function can no longer pass arguments to the constructor:class Comics extends HipsterCollection {
get url() => '/comics';
modelMaker(_) => new ComicBook();
}That modelMaker() function still needs to accept an argument in case another subclass wants to use it. Here, I use the convention of an underscore parameter to signify that it is being discarded. One last hoop that I have to go through is for the collection to assign attributes when they are discarded like that. A simple
isEmpty() suffices: _buildModel(attrs) {
var new_model = modelMaker(attrs);
// Give the factory a chance to define attributes on the model, if it does
// not, explicitly set them.
if (new_model.attributes.isEmpty()) new_model.attributes = attrs;
new_model.collection = this;
return new_model;
}With that, my empty model definition now works, as evidenced by my sample comic book application still working:That might seem like a lot of work to go through just to eliminate one line, but I am eliminating one line that users of my library need to define. Forcing potential users to write extra code is never cool.
So... win!
Lastly, I notice that I have a TODO in
HipsterModel#save(): // TODO: updateBack before I switched the data sync layer to HipsterSync, this must have seemed to me a difficult task. Now, all that I need to do is pass in 'create' or 'update' depending on whether or not the model has been previously saved to the backend: Future save() {
Completer completer = new Completer();
String operation = isSaved() ? 'update' : 'create';
Future after_call = HipsterSync.call(operation, this);
// ...
} With that, I have a well-documented, fully operational Death S... er, model class in Hipster MVC.Day #341

No comments:
Post a Comment