-
Notifications
You must be signed in to change notification settings - Fork 1
Programming Model: Data Change Signals
Table of Contents // Back: Data Addressing // Continue: Observers & Signal Handling
See also: ONMjs.Store (store class reference)
See also: Observer & Signal Handling (programming model article)
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
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:
- A single
onComponentCreated
callback - 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) - A single
onNamespaceUpdated
callback on the newly created component's parent namespace - 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).
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:
- A series of
onNamespaceRemoved
callbacks for each of the removed component's namespace (dispatched in the reverse order thatonNamespaceCreated
callbacks are dispatched, or descending in ONMjs parlance) - A single
onComponentRemoved
callback - A single
onNamespaceUpdated
callback on the removed component's parent namespace - 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).
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:
- A single
onNamespaceUpdated
callback on the removed component's parent namespace - 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).
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".
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.
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.
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'.
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.
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.
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.
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.
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.
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.
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.
Identical to onComponentCreated
signal except that the onSubcomponentUpdated
signal is dispatched on parent components and not the actual component whose properties were updated.
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.
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.
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.
Identical to onNamespaceUpdated
except that onSubnamespaceUpdated
is dispatched on the updated namespace's parent namespaces.
Table of Contents // Back: Data Addressing // Continue: Observers & Signal Handling
Copyright (C) 2013 Christopher D. Russell