Table of Contents
Design Goals
In general, frameworks get a pretty bad rap. They are bemoaned from one side for enormous bloat; they are denigrated for a lack of features from the other. Developers working within them quickly find limitations and constraints imposed from above in order to satisfy a framework’s strange sense of rightness or purity, edginess or Victorian convention.
What begins as an application’s foundation can, in the end, become its cage. More time is spent ‘working around the framework’ than is gained through its presence. Absolutely no one wants to be in this situation.
Every application of sufficient complexity and scope has a framework. The framework is just ‘the how’ and ‘the why’ and ‘the glue’ holding an application together. Sometimes, it makes sense to build exactly what you need; the framework and the application become nearly indistinguishable beasts. Many frameworks, like Python’s Django, began as custom applications – and their most appropriate use-cases often reflect their origins.
Off-the-shelf frameworks only work when the design goals of the framework are in solid alignment with the actual needs of an application. Even the best written, most professional and elegant of frameworks will be detrimental to a project if each seeks a separate purpose.
The Cognition framework has a very specific set of design goals and interests. First and foremost, it is tailored for the creation of large, sprawling single-page web applications, i.e. the kind of thing you’re likely to find on a corporate intranet.
Cognition is a completely client-side framework; it has no server-side rendering options. It enforces a strict reliance on a service-oriented architecture.
It assumes that programmers are fallible and messy and makes an honest attempt to clean things up. If one group wants to use 3rd party components and another wants to visualize things in D3, it gives them the leeway to use their preferred tools. It caters to large teams – as they can encapsulate functionality completely within their modules – and limit the access of modules they load. And it handles routing and deep-linking more or less automatically.
Cognition does not handle SEO. Search engines are currently nearly blind to Cognition applications. Public, consumer-facing websites that desire the nightly visitations of web crawlers and ninja bots are not well served by this framework. Page rank will not be theirs.
Cognition also requires modern ES5-capable browsers. No attempt has been made to sustain legacy systems. This simplifies the code-base and opens up a bevy of technical options and browser features. But it does completely preclude support for older browser environments.
Development Philosophy
composition gang of four
simple
embrace fallibility
frameworks should do their best to stay out of your way, providing minimal limits on creativity. they should handle the things that are onerous, that are easy to screw up, the things that can have hidden consequences.
resource contention – not ioc, command clear military hierarchy, ambiguous leadership kind of bites file management house cleaning and the state of my listeners race conditions error handling encapsulation and coupling – pipes and valves
rogue pragmatism dependency declaration
Catbus
Data Store (Cat?) and Message Bus (Bus!) in Javascript
Sensor Lifecycle
Sensors watch data locations and relay messages through function calls that always pass 3 arguments: message, topic and source. Generally, the message contains the data being passed, the topic acts like an event name and the source is the name of data location from which the message originated. Note that various methods can shift the meanings of these for a variety of use cases.
When a sensor receives a message, it goes through the following series of steps in order to process it.
- Check Enabled: If the sensor has been deactivated (dropped), the message is silently aborted.
- Gather: The values of any data locations to gather are bundled into a hash of values by source name, augmenting the original message data.
- Extract: The extract attribute is plucked from the message and replaces it.
- Consider: The consider attribute acts as a functor to modify or replace the incoming message (before filters are applied.
- Change: If change is true, the message will silently abort if it equals the last message seen and stored at this point in the process.
- Filter: If present, a filter function is called with arguments (message, topic, source) and will continue if true is returned.
- Batch and Group: If batch or group attributes are defined, the sensor will begin bundling messages in a list or hash, respectively.
- Transform: The transform attribute acts as a functor to modify or replace the outgoing message.
- Emit: The emit attribute acts as a functor to modify or replace the outgoing topic.
- Queue: The message is either sent directly or queued for delivery on flush (invoked manually or by the next cycle of the event loop).
Catbus API Documentation
Bus Methods
Name | Parameters | Description | Returns |
---|---|---|---|
at, location | name: string, (optional) tag: string | Creates or retrieves a Location with the given name stored on the bus. A tag can be given to the Location; this will travel with any messages generated at the Location. The tag is the same as the name by default. | Location |
dropHost | name: string | Drops (i.e. destroys) any Sensors or Locations with the host name provided. | boolean (host existed?) |
flush | none | Triggers the processing of all pending messages on the bus. This is called automatically | self |
Location Methods
Name | Parameters | Description | Returns |
---|---|---|---|
on, topic | (optional) topic: string | Creates a Sensor watching this Location for messages associated with the given topic. The default topic is ‘update’. | Sensor |
peek | (optional) topic: string | Returns the metadata associated with the last message written to the Location for the given topic (default: ‘update’). | message metadata |
read | (optional) topic: string | Returns the last message written to the Location for the given topic (default: ‘update’). | msg: * |
write | msg: *, (optional) topic: string, (optional) tag: string | Writes a message to the Location, triggering any Sensors currently watching it under a matching topic (default: ‘update’). The default tag is the one associated with the Location. | self |
refresh | (optional) topic: string, (optional) tag: string | Rewrites the current data at the Location, triggering any interested Sensors (note: the change flag would completely suppress this). | self |
toggle | (optional) topic: string, (optional) tag: string | Writes the (!current data) at the Location, triggering any interested Sensors. | self |
tag | none | Gets the tag of the Location. | tag: string |
name | none | Gets the name of the Location. | name: string |
Sensor Attributes
Name | Parameter | Description | Setter Default | Sensor Default |
---|---|---|---|---|
at, location | location: string or Location | Assigns a new Location to the Sensor (thus no longer watching a prior Location). | current Location | original Location |
on, topic | topic: string | Assigns a new topic to the Sensor (thus no longer following a prior topic). | ‘update’ | ‘update’ |
name | name: string | Assigns a name to the Sensor. | null | null |
run | callback: function | Sets a callback to be invoked by the Sensor when triggered. Can run in specified context attribute. | null | null |
filter | handler: function | Sets a function to silently filter messages in the Sensor so they do not trigger or accumulate. The handler will receive (msg, topic, tag) and should return true to continue processing the message. Can run in specified context attribute. | null | null |
transform | handler: function | Sets a function to transform messages (if not filtered). The handler will receive (msg, topic, tag) and should return a new modified message. Can run in specified context attribute. | null | null |
pipe | location: string or Location | Sets a target Location to which the Sensor writes when triggered. | null | null |
change | flag: boolean | Prevents a Sensor from triggering unless an incoming value differs from the last value received. | true | false |
batch | flag: boolean | Causes a Sensor to accumulate messages as specified by the Sensor’s keep attribute – until flushed (via nextTick(), requestAnimationFrame() or by manually invoking bus.flush()). | true | false |
group | flag: boolean | Causes a Sensor to accumulate messages in a hash by tag as specified by the Sensor’s keep attribute – until flushed (via nextTick(), requestAnimationFrame() or by manually invoking bus.flush()). Often used in tandem with batch and/or retain. | true | false |
keep | string: ‘last’, ‘first’ or ‘all’ | Causes a Sensor to keep certain messages (only the first, the last or all of them). If the group flag is set, the keep rules will be applied to messages by tag (not by the full set). | ‘last’ | ‘last’ |
defer | flag: boolean | Delays triggering the Sensor until messages without the defer flag have been processed. | true | false |
retain | flag: boolean | Retains messages even after a flush in order to accumulate a fuller list (batch) or hash (group) | true | false |
host | name: string | Assigns a new host name to the Sensor. When a host is dropped through bus.dropHost(name), all Sensors and Locations assigned to the host are dropped and/or destroyed. | null | null |
need | tag(s): string or [strings] | Prevents a Sensor from triggering until it has received messages for all specified tags. Generally used with batch, group and/or retain flags. | null | null |
active | flag: boolean | Enables or disables the Sensor ability to trigger. | true | true |
max | count: integer | Limits the number of times a Sensor can trigger. When the max is reached, the Sensor will automatically drop(). A value of -1 has no trigger limit. | -1 | -1 |
as | context: object | Sets the ‘this’ context that the filter, run and transform functions will use if needed. | self | self |
Sensor Methods
Name | Parameters | Description | Returns |
---|---|---|---|
once | none | Sets the max triggers attribute to 1. | self |
wake | none | Sets the active attribute to true. | self |
sleep | none | Sets the active attribute to false. | self |
peek | none | Returns the packet containing the Sensor’s last incoming msg and metadata (not filtered or transformed). | msg metadata |
read | msg: * | Returns the Sensor’s last incoming msg (not filtered or transformed). | self |
tell | msg: *, topic: string, tag: string | Writes a message to the Sensor. This should generally only be called by Location objects – but is exposed for hacking or debugging. | self |
drop | none | Drops the Sensor’s subscription to a Location, effectively destroying it. | self |
attr | name: string | Gets the value of the given attribute. | attribute value: * |
attr | name: string, value: * | Sets the value of the given attribute. | self |
attr | {name: value, … } | Sets multiple attribute values on the Sensor. | self |
The Framework
Typed Attributes
Some tag attributes support a type prefix to allow for more complexity (or even clarity). Typed attribute values are preceded by a type name and a space. The data type, in particular, will also create sensors to dynamically bind the value.
Auto
The auto type stores most values as strings but will convert ‘true’, ‘false’ and ‘null’ to their non-string counterparts; this is the default type for the value attribute of data tags.
Note: to store these values explicitly as strings, use ‘string true’, ‘string false’ or ‘string null’.
Boolean
The boolean type results in a stored boolean value of true or false. It expects strings of ‘true’ or ‘false’ as inputs.
Note: This is generally not needed – see the auto type.
Config
A config attribute is resolved upon instantiation and does not respond to subsequent changes; it’s a once and done setup variable.
In this example, the ‘url’ would be set to whatever string is contained within the config option named currentPage.
Data
Data types provide the name of a data location. The attribute is bound to the data’s value via sensors that are automatically configured in the background.
For instance, a cog can load various URLs dynamically by using a url attribute typed as data.
In this case, the DOM element with an id of holder would always contain a cog whose URL was obtained from a data tag named currentPage. If the value of currentPage changed, a new cog would be loaded, replacing the prior one, using the string value of currentPage as its URL.
Index
The index type signifies that an array index will be used and requires no further naming specifics. It is currently only used as the default setting for the key attribute in chain tags.
Number
The number type attempts to cast and store a value as a Javascript Number.
Most attributes would store the number as a ‘string’ without this specification.
Prop
The prop type reads a property from the current cog’s script declaration. This is done only once at instantiation and does not respond to later updates.
Data tags can use the prop type to initialize complex values such as objects or arrays.
Run
The run type executes a function declared in the current cog’s script declaration. This is done only once at instantiation and does not respond to later updates.
This cog would resolve its ‘url’ based on the result of calling the function ‘getCatPage’ – which should be a method on the current cog that returns a string.
Likewise, data tags can run a function to set their initial value.
String
The string type simply specifies that the following value will be treated as a raw string. For cogs, the default type for the ‘url’ attribute is ‘string’.
These example statements are thus equivalent. They would both load ‘cats_of_the_world.html’ with the full path being relative to the location of the cog file containing this code.
For data locations, the default type is auto – meaning that ‘true’, ‘false’ and ‘null’ would be converted to non-strings.
Cognition Tag Reference
Alias
TLDR: For your project, use this to safely move directories around, rename files, flip between versions of code, or refactor all the things. Update an alias
or two and it all still works!
The alias tag provides a way to create references to specific URLs. Each of these can reference a parent alias via the path attribute in order to construct cascading paths. Although ‘../’ is supported as a convenience, this tag is designed to discourage its usage.
Other cognition tags with ‘url’ or ‘path’ attributes can use alias names as values to handle file path resolution.
Using alias tags, one can rearrange the directory structure of an existing application without modifying the underlying code.
By convention, the ‘name’ attribute of an alias tag is always fully capitalized with underscores between words.
Alias Tag Examples
Alias Tag Attributes
Name | Description | [Types] & Options |
---|---|---|
name | The actual string used to reference the alias. | [string] |
url | A relative or absolute path to an actual file or directory. | [string] |
path | A directory used as a prefix for the url attribute. It can be an actual relative or absolute path or the name of another alias defined previously. They can be chained together such that a change to a root alias can affect how every dependent alias resolves. | [string] |
Alloy
The alloy tag includes additional blueprint and script functionality on (as if just above) the cog in which it is declared. Alloys can act as libraries or state machines and do not have a display declaration. They can be stacked within a cog, but they cannot be nested within other alloys.
The script contents of an alloy are exposed on the script of the containing cog as a property based on the alloy’s name attribute.
Alloy Tag Examples
Ally Tag Attributes
Name | Description | Types |
---|---|---|
url | A relative or absolute path to a file containing an alloy definition that implements script and/or blueprint declarations. Alloy files are like cog files without display declarations. | [string, data] |
path | A directory used as a prefix for the url attribute. It can be an actual path or the name of another alias defined previously. | [string] |
Chain
Manages arrays of cogs. It is inspired heavily by D3.js’s system of mapping DOM nodes to discrete data elements with enter, update and exit events.
With chains, a series of cogs are created based on one url. They are mapped to the keys (or indices) of a source array of data via the ‘from’ attribute. Data is injected into each via the chain’s ‘to’ attribute.
Other options can help to control how these cogs are layered and applied to the DOM.
Chain Tag Examples
Chain Tag Attributes
Name | Description | [Types] & Options |
---|---|---|
id | The DOM element id used as placeholder by the chain. | [string] |
url | A relative or absolute path to a file containing a cog definition with at least a display declaration. Used by every cog in the chain. | [string] |
from | The data source used to construct the chain; it should resolve to an array. By default, it senses changes to a named data tag. | [data, prop, run] |
to | The data item created within each cog created from the source array. By default, it builds a data tag named ‘item’ that is wired back to the source data. | [data item, prop, run] |
path | A directory used as a prefix for the url attribute. It can be an actual path or the name of another alias defined previously. | [string] |
key | A way to identify data elements that persist across source changes. An item’s index in the source array is used by default. An object property name or a method to compute a key (function(data) { … return key; } can be used as key values. | [index, string, prop, run] |
build | By default, build is set to ‘append’, and will append new elements into the node containing the chain. Use ‘scratch’ to destroy and rebuild with each change to the data source. Use ‘sort’ to rearrange nodes to match their order in the source array. | append, scratch, or sort |
order | The order attribute applies the CSS order style to each cog in the chain using the source array’s index values. | [bool] |
depth | The depth attribute applies the CSS z-index style to each cog in the chain using the source array’s index values. | [bool] |
Cog
TLDR: The core component of the Cognition framework. Put cogs within cogs to build an application as a big nested tree (it’s turtles all the way down). Each cog is constructed from a file containing a display tag and optional blueprint and script tags.
The cog tag embeds a visible cog component within the HTML display of the current cog, loading (lazy-style) into (or about) the DOM container referenced by its ‘id’ attribute.
Cog Tag Examples
Cog Tag Attributes
Name | Description | [Types] & Options |
---|---|---|
node | The DOM element id used as placeholder or reference point by the cog. | [string] |
url | A relative or absolute path to a file containing a cog definition with at least a display declaration. | [string, prop, data, run] |
path | A directory used as a prefix for the ‘url’ attribute. It can be an actual path or the name of another alias defined previously. | [string] |
from | The data source or configuration injected into the cog, generally an options object in the defined in the script tag. | [prop, data, run] |
to | The name of the storage item dynamically created within the cog to hold its configuration; it defaults to a ‘config’ attribute named ‘item’. | [data item, prop, run] |
and | The DOM method used to place the cog relative to the placeholder identified by the ‘id’ attribute. | append, prepend, before, after, replace |
Config
TLDR: A spot for immutable configuration data, storing a value only at creation. Does not support sensors or writes; it’s once and done.
The config tag stores data in its containing cog. When accessed via a prop tag, the raw value stored in the config is returned. Note, data tags differ in that they provide an interface with read and write access. Config tags are intended to provide static, unchanging data.
Config Tag Examples
Config Tag Attributes
Name | Description | [Types] & Options |
---|---|---|
name | The configuration property declared. | [string] |
value | The associated value of this configuration. | [auto, any type] |
inherit | If a config of the same name exists higher in the cog hierarchy, the value will be inherited, overriding the value attribute specified locally. | [bool] |
Data
TLDR: Stores observable data on cog. Watched by sensors.
The data tag creates a named location owned and governed by the cog in which it is declared. Its name is required to be unique within the cog. The containing cog and its descendants can read, write and subscribe via sensors to the data contained within it. Child cogs can reference data locations above themselves by using the prop tag. Parent cogs have no direct access to the data contained within their children by design.
Data Tag Examples
Data Tag Attributes
Name | Description | [Types] & Options |
---|---|---|
name | A unique name within the containing cog by which this data location may be referenced. | [string] |
value | The initial value to be stored within this data location. By default, no value is assigned and it would read as undefined. Unless specified as a ‘string’ type, primitive values in the tag are automatically converted to true, false and null (see type auto). | [auto, any] |
inherit | Set the inherit flag to true to override the initial set value with the value of a data location of the same name if there is a match higher up the cog hierarchy. | [bool] |
prop | (Deprecated: will soon be true by default) Set the prop flag to true to expose the data location object as a property in the cog’s script declaration. | [bool] |
Preload
The preload tag requires that a file be loaded before the cog containing its declaration is initialized. Preloaded files are simply stored in the cache; they are not executed and are not placed in the cog hierarchy.
Preload Tag Examples
Preload Tag Attributes
Name | Description | [Types] & Options |
---|---|---|
url | A relative or absolute path to any file. | [string] |
path | A directory used as a prefix for the url attribute. It can be an actual path or the name of another alias defined previously. | [string] |
Prop
The prop tag lets one reference a data location, alias or config within a script such that the name of the property is added as a property within a cog’s script. It is used to expose and access properties defined in cogs above the current one.
Prop Tag Examples
Prop Tag Attributes
Name | Description | [Types] & Options |
---|---|---|
find | The name of the data or alias property to find. | [string] |
name | A local name used to expose the property on the current script. This defaults to the ‘find’ value. | [string] |
is | The tag type to find | data, alias |
where | Where to search for the given tag name. ‘first’: looks locally and goes up the cog tree, returning the first match. ‘parent’: like ‘first’ but skips the current cog. ‘outer’: returns the second match. ‘last’: returns the highest, final match in the cog hierarchy. | first, parent, outer, last |
Require
The require tag mandates that a Javascript file must be loaded and executed before the cog containing its declaration is initialized. This tag is used to declare library dependencies for the entire application, but allows the load to be deferred until needed.
Require Tag Examples
Require Tag Attributes
Name | Description | [Types] & Options |
---|---|---|
url | A relative or absolute path to a file containing a Javascript library. | [string] |
path | A directory used as a prefix for the ‘url’ attribute. It can be an actual path or the name of another alias defined previously. | [string] |
Sensor
TLDR: Watches data locations as a subscriber. Fires output by invoking a ‘run’ method or piping a message to a data location with the ‘pipe’ name. Can do fun stuff like transforms, filters, batching, etc.
Sensors detect events and data flows, acting as subscription instances that can gather, filter, transform and emit message data.Â
Using the ‘watch’ and/or ‘need’ attributes, sensors can monitor data locations by name. Sensors search for each data location in turn, checking the current cog and then walking up the cog hierarchy until a match is found. Note: the search pattern can be modified using the ‘where’ attribute.
With the ‘node’ attribute, a sensor can listen for DOM events on a node with a matching ‘id’ attribute in the cog’s display declaration. Specify the event (such as ‘click’ or ‘mouseup’) using the ‘on’ attribute.
Sensor Tag Examples
Sensor Tag Attributes
Name | Description | [Types] & Options |
---|---|---|
watch (1) | A list of names referring to data tags (specifically, the ‘tag’ attribute of data locations) that the sensor will subscribe to. If multiple names are listed, the underlying bus will automatically merge and batch them. Anything listed in the ‘need’ attribute is added to the watch list as a union of the two (distinct by tag, i.e. no duplicate subscriptions). | [string (comma delimited)] |
need (1) | The ‘need’ attribute subscribes to data tags just like the ‘watch’ attribute, but prevents the sensor from firing (via ‘run’ or ‘pipe’) until it has received data from every listed tag. It requires fresh data from each named tag in order to fire again unless the ‘retain’ attribute is also used. | [string (comma delimited)] |
node (1) | Finds a ‘node’ by ‘id’ in the cog’s display and listens for the event specified by the ‘on’ attribute. Use the ‘node’ attribute in lieu of the ‘watch’ and ‘need’ attributes. The message passed via ‘run’ or ‘pipe’ will be the associated event object. | [string] |
on | If the sensor is watching data tags using ‘watch’ and/or ‘need’, the ‘on’ attribute specifies a topic on the data location. Generally, the default setting of ‘update’ should suffice. For sensors using the ‘node’ attribute, ‘on’ should be set to a DOM event name, such as ‘click’ or ‘mouseover’. | [string] |
optional | Makes the sensor’s subscriptions optional such that missing data tags in the ‘watch’ or ‘need’ attributes will simply be ignored (as opposed to throwing errors). | [bool] |
where | Where to search for the given tag name(s). ‘first’: looks locally and goes up the cog tree, returning the first match. ‘parent’: like ‘first’ but skips the current cog. ‘outer’: returns the second match. ‘last’: returns the highest, final match in the cog hierarchy. | first, parent, outer, last |
pipe (2) | When the sensor fires, writes to the ‘first’ data location found with the name specified by ‘pipe’. The attributes ‘pipe’ or ‘run’ are the only output methods for sensors and are mutually exclusive. | [string] |
run (2) | When the sensor fires, runs a function of the given name as a method of the script in the containing cog. The method’s signature should be f(message, topic, tag, sensor); the message argument contains the data being transferred and is typically the only argument needed. The attributes ‘pipe’ or ‘run’ are the only output methods for sensors and are mutually exclusive. | [string] |
filter | When the sensor receives new data, a function of the given name is invoked as a method of the script in the containing cog. The method’s signature should be f(message, topic, tag) and it should return true in order to continue processing the data. If the filter method returns false, the incoming data will be silently ignored. | [string] |
transform | When the sensor receives new data, a function of the given name is invoked as a method of the script in the containing cog. The method’s signature should be f(message, topic, tag) and it should return true in order to continue processing the data. If the filter method returns false, the incoming data will be silently ignored. For ‘batch’ sensors, this currently applies the ‘transform’ method to the messages before they are batched. (Future versions might include something like ‘arrive’ and ‘leave’ as methods to handle both outputs.) | [bool] |
once | This flag will automatically destroy the sensor after firing a single time. | [bool] |
change | This flag will prevent the sensor from firing unless the incoming message differs from the last message received, i.e., it skips (or silences) duplicates. | [bool] |
retain | If the sensor has a ‘need’ attribute, it will remain fulfilled once satisfied. Normally, the needs are reset when a sensor fires such that fresh data is required for each ‘need’. | [bool] |
keep | A sensor set to ‘batch’ keeps an internal history of messages received that it will process when the bus flushes. By default, it will pass along the ‘last’ message received when it fires. It can also pass the ‘first’ message seen, or even an array containing ‘all’ messages received between flushes. | last, first, all |
auto | Upon creation, the sensor will automatically read and pass along the value stored in the data location it is monitoring IF the message is not undefined. | [bool] |
batch | When data is received, the sensor will note it but not fire until the bus is flushed (which occurs via requestAnimationFrame). Sensors watching multiple data locations set ‘batch’ to true automatically. Note that any ‘need’ attributes must also be met in order for the sensor to fire. | [bool] |
1) All sensors require something to watch, an input source, be it a DOM ‘node’ or data location. 2) All sensors should produce an output; they can ‘run’ a method defined on their cog or ‘pipe’ messages to another data location.
Valve
The valve tag lets one sandbox portions of the cog hierarchy. It provides a communications ‘white-list’ such that descendant cogs cannot access alias or data tags that exist at the valve’s level and above. It does not apply restrictions to the cog in which it is declared.
A single valve can specify one or more permitted names and a single tag type. Multiple valves can be placed within a single cog, resulting in a white-list consisting of their union. Tag types without any associated valves can be accessed without restriction.
Valve Tag Examples
Valve Tag Attributes
Name | Description | [Types] & Options |
---|---|---|
allow | Tag name (or names) that descendant cogs can access. | [string (comma delimited)] |
is | The tag type to which the valve applies. | data, alias |