Skip to content

Programming Model: Data Change Signals

ChrisRus edited this page Nov 12, 2013 · 13 revisions

See also: ONMjs.Store (store class reference)
See also: Observer & Signal Handling (programming model article)

Getting Started

In the parlance of ONMjs, the term "event" is used canonically to refer to the occurrence of something of semantic significance to ONMjs. Events actuate signals. A signal is a function call made by ONMjs to a signal handler function. Signal handler functions are implemented by Observers which you implement as part of your application logic.

As a data management library, the events that have significance to ONMjs are occurrences that alter the structure (e.g. addition/removal data components), or semantics (e.g. the update of a property value) of data managed by the library. When a data change event occurs, ONMjs typically dispatches more than one signal in response. Thus there is a 1:N relationship between a data change event and the signals that ONMjs dispatches to convey the event's occurrence to your application logic. See: Observer & Signal Handling

Data Change Events that Actuate Signal Callbacks

Component Creation

The addition of a data component to a ONMjs.Store instance via the ONMjs.Store.createComponent methods causes ONMjs to immediately dispatch data change signals (i.e. callbacks) to all registered observers of the modified store instance.

In detail, ONMjs.Store dispatches to all registered observers the following signal callbacks:

  1. A single onComponentCreated callback
  2. A series of onNamespaceCreated callbacks for each namespace in the newly created component (depth-first from the component's root namespace, or ascending in ONMjs parlance)
  3. A single onNamespaceUpdated callback on the newly created component's parent namespace
  4. A series of onSubnamespaceUpdated callbacks for the newly created component's grandparent, and its parents...

Typically observer routines leverage onComponentCreated and onNamespaceCreated signal callbacks to allocate resources (e.g. data views) associated with the newly-created data component, and onNamepsaceUpdated and onSubnamespaceUpdated signal callbacks to update previously-allocated resources (e.g. to update an existing data view).

Note that the order of onNamespaceCreated callbacks is determined by a depth-first, topological sort of the component's declared namespace(s) ensuring that parent namespaces are always signalled before their child namespace(s).

Component Removal

The removal of a data component from a ONMjs.Store instance via ONMjs.Store.removeComponent method causes ONMjs to immediately dispatch data change signals (i.e. callbacks) to all registered observers of the modified store instance.

In detail, ONMjs.Store dispatches to all registered observers the following signal callbacks:

  1. A series of onNamespaceRemoved callbacks for each of the removed component's namespace (dispatched in the reverse order that onNamespaceCreated callbacks are dispatched, or descending in ONMjs parlance)
  2. A single onComponentRemoved callback
  3. A single onNamespaceUpdated callback on the removed component's parent namespace
  4. A series of onSubnamespaceUpdated callbacks on the removed component's grandparent, and its parents...

Typically observer routines leverage onNamespaceRemoved and onComponentRemoved signal callback to de-allocate resources (e.g. data views) that were previously allocated when the component was created, and onNamespaceUpdated and onSubnamespaceUpdated signal callbacks to update previously allocated resources (e.g to update an existing data view).

Namespace Property Value Updates

ONMjs presumes that the addition and removal of data components from an ONMjs.Store is semantically significant to your application and automatically dispatches signal callbacks to registered observers immediately when methods ONMjs.Store.createComponent and ONMjs.Store.removeComponent are called.

The library does not however automatically dispatch data change signals when your application logic mutates the properties of data managed by an ONMjs.Store instance. Instead, you choose the granularity and frequency of change signals dispatched by ONMjs based on the semantics of your data, and your application by manually calling the ONMjs.Namespace.update method.

There are two reasons why this protocol was chosen: it's costly to monitor high-frequency property changes and performance matters, and ONMjs doesn't have enough insight into the semantics of your data or your application to deduce an appropriate granularity or frequency for these signal callbacks.

So when you application is done making some semantically meaningful change to your data's properties, it should call the ONMjs.Namespace.udpate method to initiate the change signals. In response, ONMjs dispatches to all registered observers the following signal callbacks:

  1. A single onNamespaceUpdated callback on the removed component's parent namespace
  2. A series of onSubnamespaceUpdated callbacks on the removed component's grandparent, and its parents...

Typically observer routines leverage onNamespaceUpdated and onSubnamespaceUpdated signal callbacks to update previously allocated resources (e.g to update an existing data view).

Observer Attach and Detach

ONMjs' implementation of the observer pattern presumes that observer routines manage private run-time state data (e.g. data views) that derive from the topology (i.e. data component hierarchy), properties, and meta-data codified by your data model in some application-defined way. To make is possible (and simple) to write observer routines, ONMjs provides a special signalling protocol that is leveraged when an observer routine is registered and unregistered with an ONMjs.Store instance that allows the observer to initialize its run-time state when it is first registered, and take whatever action(s) are necessary to cleanup this run-time state when it is unregisterd. This protocol is referred to as the attach/detach protocol.

In essence, when an observer is registered the 'attach' signal protocol "catches it up" to the current state of the data managed by the ONMjs.Store instance. And, similarly when it is unregistered the 'detach' protocol "tears it back down".

Attach

To register an observer routine with an instance of ONMjs.Store, your application calls the ONMjs.Store.registerObserver method. In the context of the method call, and before returning to your application, ONMjs.Store dispatches the signal onObserverAttachBegin, followed by a series of onComponentCreated and onNamespaceCreated signal callbacks for each data component managed by the ONMjs.Store instance, and concludes by dispatching the onObserverAttachEnd signal callback.

Only the newly-registered observer receives these signals; all other observers that may be registered with the ONMjs.Store instance are unaffected.

Detach

To unregister an observer routine from an instance of ONMjs.Store, your application calls the ONMjs.Store.unregisterObserver method. In the context of the method call, and before returning to your application, ONMjs.Store disaptches the signal onObserverDetachBegin, followed be a series of onNamespaceRemoved and onComponentRemoved signal callbacks for each data component managed by the ONMjs.Store instance, and concludes by dispatching the onObserverDetachEnd signal callback.

Only the newly-registered observer receives these signals; all other observers that may be registered with the ONMjs.Store instance are unaffected.

Signals

By convention, ONMjs observer routines are typically defined in a named JavaScript object (or class). The implementation of an observer is a collection of one or more of the ONMjs-defined signal callback functions listed below. You only need to define the signals you're actually interested in receiving callbacks on - if you don't define a signal callback function it simply will not be called.

You can see the general form of an observer here (CoffeeScript) and here (compiled JavaScript). This particular example is included as part of the ONMjs-lib-observer-* package and all it does is log the receipt of callbacks to your browser's debug console using helper class called 'backchannel'.

onObserverAttachBegin

    var observerInterface = {
        onObserverAttachBegin: function(store, observerId) { /*... */ };
    };

Parameters:

  • store - a reference to an ONMjs.Store instance.
  • observerId - this observer's registration ID string (unique and opaque handle).

Remarks:

Signal dispatched in response to an application call on method ONMjs.Store.registerObserver to indicate to the observer that it is about to receive a series of onComponentCreated and onNamespaceCreated signal callbacks per the ONMjs observer attach protocol.

onObserverAttachEnd

    var observerInterface = {
        onObserverAttachEnd: function(store, observerId) { /*... */ };
    };

Parameters:

  • store - a reference to an ONMjs.Store instance.
  • observerId - this observer's registration ID string (unique and opaque handle).

Remarks:

Signal dispatched in response to an application call on method ONMjs.Store.registerObserver to indicate to the observer that it has received the last of its onComponentCreated and onNamespaceCreated signal callbacks per the ONMjs observer attach protocol, and that it is entering steady state where it will receive additional signal callbacks in response to data component adds, removes, and property updates.

onObserverDetachBegin

    var observerInterface = {
        onObserverDetachBegin: function(store, observerId) { /*... */ };
    };

Parameters:

  • store - a reference to an ONMjs.Store instance.
  • observerId - this observer's registration ID string (unique and opaque handle).

Remarks:

Signal dispatched in response to an application call on method ONMjs.Store.unregisterObserver to indicate to the observer that it is about to receive a series of onNamespaceRemoved and onComponentRemoved signal callbacks per the ONMjs observer detach protocol.

onObserverDetachEnd

    var observerInterface = {
        onObserverDetachEnd: function(store, observerId) { /*... */ };
    };

Parameters:

  • store - a reference to a ONMjs.Store instance.
  • observerId - this observer's registration ID string (unique and opaque handle).

Remarks:

Signal dispatched in response to an application call on method ONMjs.Store.unregisterObserver to indicate to the observer that it has received the last of its onNamespaceRemoved and onComponentRemoved signal callbacks per the ONMjs observer detach protocol. Upon receipt of onObserverDetachEnd the observer may be disposed, or re-attached to the same or other ONMjs.Store instance.

onComponentCreated

    var observerInterface = {
        onComponentCreated: function(store, observerId, address) { /* ... */ };
    };

Parameters:

  • store - a reference to a ONMjs.Store instance.
  • observerId - this observer's registration ID string (unique and opaque handle).
  • address - a reference to a ONMjs.Address instance.

Remarks:

Signal dispatched to alert the observer that a new new data component has been added to observed ONMjs.Store instance via a call to ONMjs.createComponent. The specific component created is indicated by the address parameter.

onComponentRemoved

    var observerInterface = {
        onComponentRemoved: function(store, observerId, address) { /* ... */ };
    };

Parameters:

  • store - a reference to a ONMjs.Store instance.
  • observerId - this observer's registration ID string (unique and opaque handle).
  • address - a reference to a ONMjs.Address instance.

Remarks:

Signal dispatched to alert the observer that a data component has been removed from the observed ONMjs.Store instance via a call to ONMjs.Store.removeComponent. The specific component removed is indicated by the address parameter.

onComponentUpdated

    var observerInterface = {
        onComponentUpdated: function(store, observerId, address) { /* ... */ };
    };

Parameters:

  • store - a reference to a ONMjs.Store instance.
  • observerId - this observer's registration ID string (unique and opaque handle).
  • address - a reference to a ONMjs.Address instance.

Remarks:

Signal dispatched to alert the observer that the properties of a data component have been modified and that the application has called the ONMjs.Namespace.update method. The specific component that has been updated is indicated by the addresss parameter.

onSubcomponentUpdated

Identical to onComponentCreated signal except that the onSubcomponentUpdated signal is dispatched on parent components and not the actual component whose properties were updated.

onNamespaceCreated

    var observerInterface = {
        onNamespaceCreated: function(store, observerId, address) { /* ... */ };
    };

Parameters:

  • store - a reference to a ONMjs.Store instance.
  • observerId - this observer's registration ID string (unique and opaque handle).
  • address - a reference to a ONMjs.Address instance.

Remarks:

Signal dispatched to alert the observer that a new data namespace has been added to the observed ONMjs.Store in response to a call to ONMjs.Store.createComponent. Note that for each component added, distinct onNamespaceCreated signals are dispatched for each of the component's constituent namespace(s). The specific namespace that has been created is indicated by the address parameter.

onNamespaceRemoved

    var observerInterface = {
        onNamespaceRemoved: function(store, observerId, address) { /* ... */ };
    };

Parameters:

  • store - a reference to a ONMjs.Store instance.
  • observerId - this observer's registration ID string (unique and opaque handle).
  • address - a reference to a ONMjs.Address instance.

Remarks:

Signal dispatched to alert the observer that a data namespace has been removed from the observed ONMjs.Store in response to a call to ONMjs.Store.removeComponent. Note that for each component removed, distinct onNamespaceRemoved signals are dispatched for each of the component's constituent namespace(s). The specific namespace that has been removed is indicated by the address parameter.

onNamespaceUpdated

    var observerInterface = {
        onNamespaceUpdated: function(store, observerId, address) { /* ... */ };
    };

Parameters:

  • store - a reference to a ONMjs.Store instance.
  • observerId - this observer's registration ID string (unique and opaque handle).
  • address - a reference to a ONMjs.Address instance.

Remarks:

Signal dispatched to alert the observer that the application has has mutated the properties of the namespace indicated by the address parameter.

onSubnamespaceUpdated

Identical to onNamespaceUpdated except that onSubnamespaceUpdated is dispatched on the updated namespace's parent namespaces.


Copyright (C) 2013 Christopher D. Russell