The Third ISPN Meeting (
mentioned here) is fast approaching. I'm still hoping to have an alpha version of
Names on Nodes online by then, but it's going to be tricky. Currently I'm involved in a massive refactoring.
The basic point of
Names on Nodes, as I've mentioned before, is to automate taxonomy. That is, you feed it:
- a bunch of phylogenetic definitions,
- a phylogeny, and
- specifier links, that is, correlations between definition specifiers (apomorphies, species, specimens) and units in the phylogeny (e.g., taxa in NEXUS trees).
Once properly fed, it spits out
taxonomies, that is, associations of scientific names with sets of subtaxa.
That part of it is pretty much done. But there are two tricky parts I have yet to complete:
data persistence and
user interface. And what both basically boil down to is
CRUD.
I've mentioned CRUD before
here. It's an acronym for the four basic operations involved with data persistence, the four things you can do to a stored object. Those are:
- Create
- Retrieve
- Update
- Delete
As often happens in programming, I started out with an ad hoc sort of approach that worked well enough up to a point, but then I started finding myself in trouble. Duplicated code. Unnecessarily complex code. Code that was hard to update.
It was time to
refactor.
Here's a breakdown of the new system.
Every user interface involved with persistent objects is managed by something called a
FlowManager
. This stores a navigable tree of
FlowEntry
objects, each associated with a visual
FlowComponent
.
The main application handles requests from all components within. These are sent to the application as bubbling events, that is, events that "bubble" up through the display hierarchy (say, from a button, to the form that contains it, to the flow manager, on up to the main application). Two of the major types of request are
EditEntityEvent
and
DemandEntityEvent
.
An
EditEntityEvent
comes with a persistent object (e.g., a species, a definition, a NEXUS file, etc.). The application's responsibility is to create a
FlowComponent
object that will display the entity and allow the user to edit it (
Update).
A
DemandEntityEvent
comes with a
class of persistent object (e.g.,
Species
,
Definition
,
Nexus
, etc.). The application's responsibility, then, is to provide the "demander" with an object of that class, either by making a new one (
Create) or by selecting an existing one (
Retrieve).
(Note that I haven't mentioned
Delete. I'm leaving that out of the first version. Any deletions will have to be made by myself, manually.)
One problem with my old approach was that I had different user interface components for each combination of CRU[D] action and persistent object. Take a couple dozen types of persistent object and multiply by three: that's how many components I had to make.
The new system, though, has just two types of
FlowComponent
: one for handling
EditEntityEvent
requests and the other for handling
DemandEntityEvent
requests.
The first one is the simplest:
EntityEditor
. It basically consists of the following components:
- a form which shows all data in the persistent entity;
- a bit of text that summarizes the entity in its current state (e.g., for a
Binomen
entity, something like "Prenomen nomen Author 1970"); - a button that updates the entity in the database, or, if it is a new entity, creates it in the database; and
- a "cancel" button.
The form has to be done individually for each type of persistent entity, but the rest is completely generic.
The other
FlowComponent
object,
EntitySupplier
, actually uses an
EntityEditor
object. Once the
EntitySupplier
object is given the class of entity that it's supposed to supply, it creates a default instance of that class and gives it to an
EntityEditor
, which it displays to the left. To the right, it displays a
SuggestionBox
, a grid showing various existing entities that might match what's in the editor.
The
SuggestionBox
uses a nonvisual object called an
EntitySuggester
, whose job is to watch for changes that the user makes to the persistent entity and come up with relevant, existing suggestions. For example, suppose the user is editing a new
Publication
object and starts to type in the authorship as "Linn". The
PublicationSuggester
sees this and checks the database for possible matches, coming up with "Linnaeus 1758". If this is the publication that the user wanted, then they can select it from the
SuggestionBox
without having to fill in any more information.
I still have to create a form and a suggester for every type of persistent entity, but this is a much neater division of labor than I had in the last version, where forms generated their own suggestions (which would be wasteful if you were just editing an existing entity).
Hope to get much done this weekend.
(Have to get much done this weekend....)