Friday, June 17, 2016

Using the factory pattern in a Dart abstract class

/*
 * This example demonstrates the use of interrelated parallel inheritance
 * hierarchies: a subclass in one hierarchy (A) contains a reference to
 * the base class of another hierarchy (B). When (A) subclass is
 * instantiated its reference to (B) is assigned an object of the correct
 * subclass of (B).
 *
 * In this case, when a CatStudio is instantiated, it knows to populate
 * its reference to Animal with a Cat.
 *
 * While it's possible to simply specify the Cat class in CatStudio, this
 * would force the AnimalStudio class to know about Cat.
 */

library app.models;

import "dart:mirrors";

class CatStudio extends AnimalStudio {
  final animalType = "Cat";
  CatStudio() : super();
}

class Cat extends AnimalBase {
  Cat() : super("Meow");
}

/* When instantiating a new AnimalStudio subclass in client code, it is
 * only necessary to specify animalType as a final String in order to
 * get an instance of the desired Animal subclass assigned to the
 * `animal` attribute. In turn, AnimalBase is not required to know
 * anything about Animal subclasses defined in the client code.
 */
abstract class AnimalStudio {
  Animal animal;
  final String animalType;

  String record() {
    print("$animalType: ${animal.sing()}");
  }

  AnimalStudio() {
    animal = new Animal(animalType);
  }
}

/* A factory is required here as the calling code (AnimalStudio) knows
 * only the name of the Animal subclass to instantiate. Dart's reflection
 * system is queried to retrieve, and instantiate, the Animal class
 * defined in the client code. The file with client code must contain
 * the "library app.models" statement.
 */
abstract class Animal {
  final String sound;
  String sing() => sound;

  factory Animal(String type) {
    LibraryMirror lib = currentMirrorSystem().findLibrary(
        new Symbol('app.models'));
    Map<Symbol, Mirror> classes = lib.declarations;
    ClassMirror cls = classes[new Symbol(type)];
    InstanceMirror inst = cls.newInstance(new Symbol(''), []);
    return inst.reflectee;
  }
}

/* This is the class that must be extended in client code when defining
 * Animal subclasses. When the `Animal` factory is called with the
 * `animalType` parameter, the constructor of the desired subclass is
 * called. This will result in the *generative* constructor of the
 * superclass being called. This would be `Animal()`, which doesn't
 * exist. A workaround for this is presented in various places which
 * calls for creating a generative constructor with a mangled name,
 * such as `_Animal()`. This is abstruse, however, and the presentation
 * herein is considered idiomatic Dart as seen in the standard libraries,
 * etc (reference needed).
 */
class AnimalBase implements Animal {
  final String sound;
  String sing() => sound;
  AnimalBase(this.sound);
}

void main() {
  new CatStudio().record();
}

Monday, January 4, 2016

Event handling in Polymer.dart

So, I finally managed to get all the basic functionality from the "Polymer and Dart Codelab Admin Console" into my fork. The last step was to implement a set of methods in the superclass elements. These methods are called in response to interaction with the elements such as clicking on buttons, etc.

As a bit of background, I decided it would be a worthwhile exercise to "abstract?" out as much of the code as possible from the Codelab elements into more general superclasses containing only "title" and "description" fields, and make use of these superclass elements in the Code lab.

It should be noted that my design is a bit quirky and is based on a deprecated version of Polymer.dart. Particularly, my custom elements subclass other custom elements, which is not possible in the latest Polymer.dart. Another aspect of my design is just plain wrong: I instantiate superclass elements *inside* of my subclass elements! For example:

<codelab-element attributes="editing" extends="item-element">
    <item-element>
        <!-- codelab property -->
        <!-- codelab property -->
    </item-element>
</codelab-element>

This allows for the insertion of codelab properties into <item-element> using the <content> tag in <item-element>. Polymer.dart doesn't complain, and Dartium is happy to render the result without error.

A challenging aspect of using polymorphism turned out to be getting the subclass methods to be called in response to events in the superclass elements. For example, when the "submit" button is clicked in <item-form> it's necessary that the CodelabElement::submit() event handler is called so that any CodelabElement-specific operations are performed before handing execution off to ItemElement::submit(). Naturally, if <item-element> calls "submit()", ItemElement::submit() is going to be executed. Not what we want.

So it finally occurred to me to add an ItemElement::submit() method. This then fires a new event, 'itemupdated', which is caught by <codelab-element> because it happens to be the parent.

So we have the kind of ridiculous situation where a subclass instance is a parent of the superclass instance. But it works.

While walking, today, it occurred to me that the design could be a bit cleaner by firing events directly from the superclass element which would be caught by the subclass parent, rather than the superclass. But it doesn't turn out to be possible to do this, as far as I can tell.</codelab-element></item-element></item-form></item-element></content></item-element>

Sunday, January 3, 2016

I've been hard at work, or at least at work, since my last blog post hammering the Polymer and Dart Codelab project into shape to my liking. I set myself upon a quest to abstract out as much as possible from the example Admin Console interface so as to ease the development of new interfaces. Sure, I could have just resigned myself to copy and past the entire project for each new project, but that would involve a lot of text substitutions and room for errors. Besides, how much fun is that?

The problem

After factoring out much of the code from the Codelab* classes and <codelab-*> custom elements into their respective Item* and <item-*> subclasses and getting it mostly working, there was the nasty problem of handling the situation wherein a superclass element such as ItemElement needs to instatiate a new Item based on the its own type. For example, CodelabElement::resetForm() requires that a new Codelab be instantiated to populate the new form. Since we're moving as much code as possible into the base classes, I wanted to also have CodelabElement's superclass ItemElement create the new Codelab instance. But how to do this without ItemElement requiring knowledge about its subclasses? I needed a way to instantiate a new Codelab based only on a text string to the superclass Item constructor.

Factory and mirrors to the rescue

Dart actually has the factory pattern built into the language. To use it, a superclass method is declared using the 'factory' keyword:

import 'package:polymer/polymer.dart';
import 'dart:mirrors';


class Item extends Observable {
  String _title;
  String _description;

  static const MIN_TITLE_LENGTH = 10;
  static const MAX_TITLE_LENGTH = 30;
  static const MAX_DESCRIPTION_LENGTH = 140;

  String get title => _title;
  set title(String s) => _title = s;

  String get description => _description;
  set description(String s) => _description = s;

   /// Allow a subclass instance to be generated with
   /// an empty argument list
  Item.created(this._title, this._description);

  factory Item(String type, String title, String description) {
    if(type == null)
      return; // Todo: Determ how this is being called w/ no type
    MirrorSystem libs = currentMirrorSystem();
    LibraryMirror lib = libs.findLibrary(new Symbol('polymer_and_dart.web.models'));
    Map<Symbol, Mirror> classes = lib.declarations;
    ClassMirror cls = classes[new Symbol(type)];
    InstanceMirror inst = cls.newInstance(new Symbol(''), [title, description]);
    return inst.reflectee;
  }
}
The code is pretty self-explanatory - the mirror system finds a list of class declarations in the "polymer_and_dart.web.models" namespace, and a ClassMirror instance is created with the one we want. This mirror is then used to invoke newInstance(), which returns a mirror on the new Codelab instance, contained in its "reflectee" attribute.

Some minor gotchas appeared when working out how to actually implement this in an inheritance hierarchy. For one, the concept of mixins and mixin applications needs to be clear in the mind of the developer. There's a decent discussion of how this works out in practical terms in the SO discussion at putting a factory method in an abstract class.

I need to think about this some more. There's the interesting aspect of this regarding making superclass attributes final...

Wednesday, September 23, 2015

Setting up NFS on openSuse

Something I've wanted to do for a long time is set up a network file share (NFS). And with the discovery that the /home partition on my old development box is rapidly filling up, it seemed like a good time to do it. That way, I can transfer some of the less server-centric stuff to my laptop, on which I'm doing more general-purpose computing, development, etc.

I'm not actually sitting at the box where I configured the server and made notes of this process. But it went something like the following.

The first order of business is to make the server filesystem available on my laptop, which also runs openSuse. It turns out that there is a package, nfs-server, implemented as a kernel module, which provides the service. It's not installed by default, so I head over to the package management tool, YaST, and install it.

No dependencies. Bada bing, bada boom.

I decide to see if I can get it running. In the System Services panel, it's now there as nfs-server. It's disabled (won't start automatically) and not running. I click "start/stop". Clicking on the "Details" button opens a dialog with a complaint about not being able to bind to a port, as it's already taken (by another service). Hmm. What service? Why wouldn't it choose an available port? In an intuitive stroke of genius, I start the rcp-bind service, which I read somewhere is needed by NFS. The service now starts. Beginner's luck takes the day. You gotta love this stuff.

On the client machine (laptop), I open the NFS client panel in YaST system services. There's a couple of text fields, server domain and filesystem path. Hoping for the best, I just click "Choose". A pop-up informs me that it's scanning the local network for a NFS service. It fails to find one.

A Google search turns up some good documentation at https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/6/html/Storage_Administration_Guide/s2-nfs-nfs-firewall-config.html
There's a command "showmount" that may help. I enter the following in the root terminal window:

$> showmount -e 10.0.0.7

And what do you know? It reports the following:

Export list for 10.0.0.7: /multimedia/music

As this is the directory I had specified in the server config file (etc/exports), I know it speaks sooth. Amazing? How was it able to find the service while the YaST NFS Client tool was unable to even when pointing it at the server box? I mess around with it some more but nothing can make it find the service.

But now life is good. The laptop found the service.

Next, mounting the filesystem on the client provided another challenge. I'm not sure exactly what solved it. It was one of the following: actually mounting the server filesystem to be exported (for some odd reason), or adding "fsid=0" to the entry in /etc/exports. The "fsid=0" issue is discussed at http://forums.fedoraforum.org/showthread.php?t=164662 .

At the end of the day, it works. And it appears to be maintainable. Even after a server restart, the service is available on the client.

Saturday, August 22, 2015

Links

My JQuery resume

Polymer web components and Dart

A couple of weeks ago I finished reworking the Polymer and Dart Codelab example SPA produced by the good people at Google demonstrating the use of the 0.5 release of Polymer. After looking it over, I determined it would make a good general template for the creation of CRUD clients for various REST APIs. In other words, it's a great little example of a single-page app (SPA) to perform the standard operations - create, read, update and delete (CRUD) - in the browser.

I forked the Github repository and issued a pull request. I'll try to remember to include a link here when I get around to deploying it on App Engine.

But I dreaded the idea of having to reproduce the entire project for each REST API - or, more accurately, service - for which I want to provide a browser UI. So the task became how to minimize the repetition involved in creating a CRUD client for an API service. Note that the concept of web services isn't exactly required at this point in our discussion. The data could also come from a local file or AJAX request.

Subclassing Polymer custom components

At the 0.5 milestone, Polymer supports the subclassing of custom components. What this means, exactly, is somewhat unclear, as evidenced by the dropping of its support in the 1.0 release. But I was intrigued with the possibility of abstracting out the CRUD boilerplate and getting some nifty custom web component polymorphism action. I was somewhat successful.

First came the task of determining what the base class(es) would contain. All conceivable, at least to me, interactions with a collection of objects consist of our basic CRUD operations. Therefore, a base class called ItemElement was created. ItemElement would consist of the following:

  1. An instance of the model class Item, item
  2. Stubs, at least, for the CRUD methods read() and update()
Since the other CRUD methods, create() and delete(), are natural as members of a collection class, the ItemList class was created for them. ItemList contains, at a minimum, the following members:

  1. A list of Item instances, items
  2. Stubs, at least, for the CRUD methods create() and delete()
At this point, much of the implementation of Codelab, CodelabElement and CodelabList could be moved into the Item, ItemElement and ItemList classes, respectively. And so it was, leaving only calls to the base class implementations in the Codelab classes.

Next steps

With the success of this basic refactoring, I have stepped back to consider what to do next. Some targets are obvious, such as the difficulty in controlling the placement of HTML elements in the Codelab components. When a base class contains HTML elements, interspersing HTML elements into them from within subclass components is tricky and largely undocumented outside of a brief discussion of the <content> and <shadow> elements on the Polymer website. One is left with the idea that a great deal of flexibility can be leveraged from these. But how?

The next candidate for refactoring consists of removing the form classes ItemForm and CodelabForm contained in the files item_form.[dart/html] and codelab_form.[dart/html] It looks to me that these four files can be easily eliminated from the project by factoring their contents into the ItemElement class. Since the custom Polymer component <item-element> contains logic to control the display of an item based on whether it is being edited, the <item-form> component appears to be at best a convenient abstraction and at worst a design flaw.

Finally, adding the calls to a Codelab web service is on this list of enhancements. This presents particularly attractive possibilities for refactoring. Initial experiments indicate that large portions of the REST API interaction can live in the base classes ItemElement and ItemList. This should be most enjoyable and I expect to report back how Dart made doing this a pleasure.

Introduction

I plan to write about web 2.0 computing, languages, etc, and invite the reader to take a broad, long-range perspective on events which are shaping the digital and social worlds, increasingly merging into one.

There are lots of blogs covering every imaginable aspect of computing and software. But here lets take a moment to stop and reflect on what it all means... 42.

Just joking. I haven't read Hitchhiker's Guide all the way through, but it's always good to know, when you're feeling lost, that everyone else is, too.