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: update
Back 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