This is the Observer Pattern for Objects in the Object Web. The ON model is of a single, universal pool of Objects aware of each other's state. An Object expresses its interest in another and, if permitted, can then read that peer's state. In addition, it is notified of changes in that peer's state; until it loses interest.
Objects are considered to be 'live', with four interface points: they can read and change their own state, they can read another's state, and they can be notified of another's change in state, including the fact that they're watching back...
ON is protocol-independent: i.e., it doesn't depend on the OP Object Protocol and needn't even see an Internet connection - ON Object Notification may occur in-process.
The characters of Object text are Unicode and will always be serialised into UTF-8, and often stored and processed in UTF-8, too. Control characters are excluded apart from all forms of newline or vertical movement (CR, LF, CRLF, FF, NEL, etc) and the space. Tabs are thus excluded. If encountered on input, these excluded characters are converted to spaces. Newline or vertical control characters are identical to CRLF, and will appear normalised on output. Multiple spaces are identical to single spaces except where they're part of indentation, and will appear normalised as single spaces on output. There is no escaping apart from a literal colon. There is no Byte Order Mark. There may be NULL or EOF termination of Object text, depending on context.
Basic Object Notation is an ordered hashtable. Non-space sequences of characters are called symbols and are not case-sensitive, so capitals are normalised to lower case if found on input. Hashtable keys are symbols and thus cannot have spaces or be structured. Keys in a hashtable must be unique, so there is also a list notation within the ordered hash: a special key written as a hyphen, repeated for each element of the list.
Here's what it looks like:
owid: 23423-4141a
open: -: owid: ffda0-582ce
mode: silent
-: owid: 11ab3-001a3
mode: deny
here: nested: hash
unicode: £1234.50
and: -: another
-: list: and
hash: again
there: content
Scalar types such as int or float are not defined here, but may be defined in OR Object Rules. There is no concept of 'null', apart from an empty item, which may be represented internally as any of empty string, empty hashtable or empty list or directly as an internal null. There is no notation for comments defined here, but it may be defined in OR Object Rules.
It should be understood that this is a structured symbol notation for data only: Object Notation is explicitly not meant for textual or document data. There are no literal quoted strings, and it doesn't allow tunnelling of textual structures such as escaped tabs and newlines. Documents may, of course, be constructed from structures of strings, including Wiki-like markup for capitalisation, bold, italic, etc, and structures representing paragraphs and tables.
here: is a long list of symbols
that wraps and wraps and
goes on until...
it: stops! which
then equals..
this: -: stops!
-: which
-: then
-: equals..
andthis: stops!
which
then
equals..
There is no way to actually get newlines (or any other control characters including spaces) into symbols or lists of symbols. So "multiline strings" have to be represented as a list of lists of symbols.
In the same way that horizontal spaces create lists of symbols, empty lines vertically create a top-level list of hashtables thus separated. Multiple empty lines are collapsed:
hash: table with: entries -: next hash table (a list!) -: with more entries which: equals this on its own.. -: hash: table with: entries -: -: next hash table (a list!) -: with more entries
Note that it is not possible to have a vertical list that includes symbols or horizontal lists of symbols. This rule may only be relaxed by allowing a vertical list of hashtables that is ended with a final horizontal list of symbols. The reason for this, apart from conceptual simplicity (outer vertical is only hashes, inner horizontal is only symbols), is that it allows a space- and CPU-saving internal linked list representation, where hashes have pointers to the next vertical element and symbols have pointers to the next horizontal element.
a: b -- single symbol a: b c -- list [ b c ] a: -: b -: c -- list [ b c ] a: b: -- hash a: b: c -- hash d:
This shape-changing in the symbol 'b' comes out when using path expressions to navigate Object Notation...
For example, the path expression 'a:b' returns these results for the above:
a: b -- path a:b = ""; a:c = null a: b c -- path a:b = "" which also = a:-:b a: -: b -- path a:b = "" which also = a:-:b -: c -- path a:c = "" which also = a:-:c a: b: -- path a:b = "" a: b: d -- path a:b = d; a:c = "" c:
An empty string, empty list and empty hashtable are all equivalent. The consequence of this is that you navigate by symbol rather than structure type. The one variant on this is that lists may be indexed:
a: b c -- path a:b = "" = a:-:b = a:1:b a: -: b -- path a:b = "" = a:-:b = a:1:b -: c -- path a:c = "" = a:-:c = a:2:c
Since this is an external represention, the first element of a list is at index 1.
owid: 23423-4141a this: is the content
Objects have an Object Web ID or OWID which is always present in the headers and is immutable. OWIDs may or may not be UUIDs, but must be globally unique identifiers that are usually constructed from hyphen-separated groups of hexadecimal digits. The headers look much like HTTP, and will share some HTTP semantics here and in the OP Object Protocol.
Both header and content parts follow this same basic Object syntax. The 'Content-Type' of the content part is always a fragment of OR Object Rules notation, which extends Object Notation. In this specification, nothing much is said about the OR content part, only the headers.
owid: 23423-4141a
view: -: owid: ffda0-582ce
-: owid: 11ab3-001a3
this: is the content
Which sends the following View Event to ffda0-582ce:
owid: 23423-4141a view: -: owid: ffda0-582ce
The View Event looks like a reduced version of the viewer Object, with only the relevant parts announced.
When it does this, the target automatically gets a corresponding entry, called a Cast, after multicast:
owid: ffda0-582ce
cast: -: owid: 99a27-5510c
-: owid: 23423-4141a
stuff: content
Casts are immutable as far as the Object is concerned.
owid: ffda0-582ce
cast: -: owid: 23423-4141a
-: owid: 99a27-5510c
open: -: owid: 23423-4141a
mode: silent
stuff: content
It's only allowing 23423-4141a to view itself, and doesn't want to be notified of any change in that fact: with 'silent', the permission is executed silently - a peer can View this one without it knowing, without it seeing the View Event above. The default is to report all View Events, and if there's no Open entry, the attempt is reported but nothing is returned.
The OWID can be set to '*' to set permissions for all peers. Only individual OWIDs or '*' can be used, there's no way to specify groups any other way. OP will introduce host-level permissions, however.
Here is the Cast Event that 23423-4141a sees - without all the fields it's not interested in, are obvious or it's not allowed to see:
owid: ffda0-582ce cast: -: owid: 23423-4141a stuff: content
The 23423-4141a Object can continue to access this state at any time, and again will only see this cut-down view.
owid: ffda0-582ce
cast: -: owid: 23423-4141a
-: owid: 99a27-5510c
open: -: owid: 23423-4141a
mode: silent
-: owid: 99a27-5510c
mode: deny silent
stuff: content
If a peer is denied, it sees a denial Cast Event, like this:
owid: ffda0-582ce
cast: -: owid: 99a27-5510c
open: -: owid: 99a27-5510c
mode: deny
Again, the fact that it is silent is removed as none of its business.. Notice that permitted peers don't get any Open entry reported in this way, as it's obviously allowed.
The following will default to initial automatic denial for all-comers (apart from the specific, overriding case of 23423-4141a), but giving notification to ffda0-582ce of each attempt anyway:
owid: ffda0-582ce
cast: -: owid: 23423-4141a
-: owid: 99a27-5510c
open: -: owid: 23423-4141a
mode: silent
-: owid: *
mode: deny
stuff: content
Without an entry or with an entry that isn't 'silent', all View attempts are reported. If set just to 'deny', without 'silent', this Object will be told of every attempt by 99a27-5510c to access it just as if no entry existed, but the denial Cast Event will be immediate and automatic each time.
In ON, header information always takes the place of notifying what in HTTP would be in the status code. The above permitted Cast Event is like a 200 OK, and the deny Cast Event is like a 403 Forbidden, for example.
To re-poll or re-sample, a View entry may be re-added, causing another View Event to be sent to the target, which may not be seen by it if set to 'silent'. If an Open entry exists, whether deny or not, a Cast Event is automatically returned. If the View entry is removed, the corresponding Cast entry is also removed, and no more updates are sent.
Similarly, re-setting an Open entry will re-send the Cast Event, even if the content hasn't actually changed. Removing an Open entry will stem the updates, without notifying the Viewer, and will drop the Cast entry. Setting an Open entry to deny will send a deny Cast Event, causing the Cast entry to then be dropped. Removing deny will have no effect, as there could not have been any corresponding Cast entries.
When the public content is updated, the new state is sent to Viewers in the Cast list as before.
owid: ffda0-582ce
cast: -: owid: 23423-4141a
-: owid: 99a27-5510c
open: -: owid: 23423-4141a
mode: silent
priv: think-about: 99a27-5510c
stuff: content
Private data is any valid Object Notation content.
Maybe this Object wants to take a look back itself at 99a27-5510c, to help it decide what to do with its outstanding request:
owid: ffda0-582ce
view: -: owid: 99a27-5510c
cast: -: owid: 23423-4141a
-: owid: 99a27-5510c
open: -: owid: 23423-4141a
mode: silent
priv: think-about: 99a27-5510c
stuff: content
owid: 90af2-122bd
cast: -: owid: 23423-4141ab
-: owid: 580ac-dd09a0
with no content yet, and all pending clients listed. When the state is defined, including Opens, the Cast events are sent out.
If the Object defines itself like this:
owid: 90af2-122bd
open: -: owid: 23423-4141ab
-: owid: 580ac-dd09a0
got here in the end..
It then looks like this:
owid: 90af2-122bd
cast: -: owid: 23423-4141ab
-: owid: 580ac-dd09a0
open: -: owid: 23423-4141ab
-: owid: 580ac-dd09a0
got here in the end..
generating these two events at the same time:
owid: 90af2-122bd cast: -: owid: 23423-4141ab got here in the end.. owid: 90af2-122bd cast: -: owid: 580ac-dd09a0 got here in the end..
view: -: owid: * at this target, meaning this
Object is 'postable' or open to unsolicited events like these - not that it wants to be
omniscient!
The sender simply adds an Open entry to the target, which automatically adds a Cast
entry and sends the Cast Event. Luckily, an Object that is made globally Open by setting
open: -: owid: * doesn't get its state pushed into every such 'postable'
peer Object in the Universe!
Others can also see this state if allowed, so the Cast Event is not personalised to the target, unless this Object is Open only to the target and none other.
[2278]