-
Notifications
You must be signed in to change notification settings - Fork 8
Program Tags
- Status of this document
- Overview
- Execution Tags
- Structural Tags
- Navigation Tags
- Security Tags
- Storage Tags
STATUS: WORKING DRAFT
This is a draft document that contains incomplete and changing content. Do not use this document as reference material.
Please share any concerns about the content of this document, as a new issue.
This section serves as a glossary of declarative program tags. Understanding and using tags will reduce the amount of procedural code you write, and increase the comprehension of your programs. Unless otherwise specified, these tags are provided via the "core" package - a built-in package that implements the Salt framework.
Tags define the behavior of a state. They are specially prefixed members of a program - the configuration object, passed to the Salt
class. The standard tag prefix is an underscore ("_") character. Alternately, unprefixed members are considered states of a program.
Execution tags allow you to bind application logic to states. When a Salt
object navigates (between states of) your progam, it executes bound logic based on how each state is traversed, or the traversal "phase".
_on: [function|query]
_(Previously "main". Implemented in Salt 0.1)
References an action to perform for the "on" phase: landing on - after entering - a waypoint or destination state.
You may assign either a function or program query to this tag. Functions are scoped to the Salt
object. Queries offer a convenient short-hand for setting navigation waypoints (via the .go()
method); nothing occurs if the query fails or points to the current state.
If a state has no other tags, you can omit using the _on
tag, and simply assign a function to the state directly.
By default, an _on
query string schedules a navigation waypoint. However, destination queries are also supported.
To clear existing navigation waypoints and set a new destnation, prefix a string query with the greater-than character (">"). Destination queries preserve navigation arguments.
Functions assigned to an _on
tag only receive navigation arguments, when that state is the navigation destination - i.e., not a waypoint. When a waypoint, you may access navigation arguments via the .args
property.
var salt = new Salt({
_on: function () {
console.log('hello world!');
}
});
salt.go('@program');
// hello world!
var salt = new Salt(function () {
console.log('hello world!');
});
salt.go('@program');
// hello world!
var salt = new Salt({
here: {
_on: '../there'
},
there: function () {
console.log('now you\'re there!');
}
});
salt.go('//here');
// now you're there!
_in: [function|query]
(Implemented in Salt 0.1)
References an action to perform during the "in" phase: entering the ancestor of, or the, destination state.
You may assign either a function or program query to this tag. Functions are scoped to the Salt
object. Queries offer a convenient short-hand for setting navigation waypoints (via the .go()
method); nothing occurs if the query fails.
By default, an _in
query string schedules a navigation waypoint. However, destination queries are also supported.
To clear existing navigation waypoints and set a new destnation, prefix a string query with the greater-than character (">"). Destination queries preserve navigation arguments.
When using the _in
tag with a query that resolves outside the entered state, you risk an infinite navigation loop. This is because the navigation destination will be pursued once the waypoint is reached. To change the navigation target, reference a function that invokes the .get()
method.
Functions assigned to an _in
tag do not receive navigation arguments directly. Instead you may access them via the .args
property.
var salt = new Salt({
_in: function () {
console.log('going');
},
to: {
_in: function () {
console.log('to the');
},
goal: function () {
console.log('goal!')
}
}
});
salt.go('//to/goal');
// going
// to the
// goal!
var salt = new Salt({
_in: '//first',
first: function () {
console.log('wash hands!');
},
eat: function () {
console.log('eat dinner!');
}
});
salt.go('//eat');
// wash hands!
// eat dinner!
(Implemented in Salt 0.1)
_out: [function|query]
References an action to perform for the "out" phase: exiting the ancestor of, or the, destination state.
You may assign either a function or program query to this tag. Functions are scoped to the Salt
object. Queries offer a convenient short-hand for setting navigation waypoints (via the .go()
method); nothing occurs if the query fails.
By default, an _out
query string schedules a navigation waypoint. However, destination queries are also supported.
To clear existing navigation waypoints and set a new destnation, prefix a string query with the greater-than character (">"). Destination queries preserve navigation arguments.
When using the _out
tag with a query that resolves to the same or descendant state, you risk an infinite navigation loop. This is because the navigation destination will be pursued once the waypoint is reached. To change the navigation target, reference a function that invokes the .get()
method.
Functions assigned to an _out
tag do not receive navigation arguments directly. Instead you may access them via the .args
property.
var salt = new Salt({
_out: function () {
console.log('good bye!');
}
});
salt.go('@program', '@null');
// good bye!
var salt = new Salt({
wine: {
_out: '//last',
dine: function () {
console.log('a night out!');
}
},
last: function () {
console.log('gotta dance!');
}
});
salt.go('//wine/dine', '@null');
// a night out!
// gotta dance!
_over: [function|query]
(Implemented in Salt 0.1)
References an action to perform for the "over" (forwards-over) phase: bypassing a younger sibling towards an ancestor of, or the, destination state.
You may assign either a function or program query to this tag. Functions are scoped to the Salt
object. Queries offer a convenient short-hand for setting navigation waypoints (via the .go()
method); nothing occurs if the query fails.
By default, an _over
query string schedules a navigation waypoint. However, destination queries are also supported.
To clear existing navigation waypoints and set a new destnation, prefix a string query with the greater-than character (">"). Destination queries preserve navigation arguments.
When using the _over
tag with a query that resolves towards an ancestor or younger sibling state, you risk an infinite navigation loop. This is because the navigation destination will be pursued once the waypoint is reached. To change the navigation target, reference a function that invokes the .get()
method.
Functions assigned to an _over
tag do not receive navigation arguments directly. Instead you may access them via the .args
property.
var salt = new Salt({
taxes: {
_over: function () {
console.log('marvin');
}
},
death: {
_over: function () {
console.log('gaye');
}
},
trouble: {
_over: function () {
console.log('was');
}
},
live: function () {
console.log('right!');
}
});
salt.go('//live');
// marvin
// gaye
// was
// right!
var salt = new Salt({
safety: {
_over: 'first',
first: function () {
console.log('helmet check!');
}
},
play: function () {
console.log('play ball!');
}
});
salt.go('//play');
// helmet check!
// play ball!
_bover: [function|query]
(Implemented in Salt 0.1)
References an action to perform for the "bover" (backwards-over) phase: bypassing an older sibling towards an ancestor of, descendent of, or the, destination state.
You may assign either a function or program query to this tag. Functions are scoped to the Salt
object. Queries offer a convenient short-hand for setting navigation waypoints (via the .go()
method); nothing occurs if the query fails.
By default, an _bover
query string schedules a navigation waypoint. However, destination queries are also supported.
To clear existing navigation waypoints and set a new destnation, prefix a string query with the greater-than character (">"). Destination queries preserve navigation arguments.
When using the _bover
tag with a query that resolves towards an older sibling state, you risk an infinite navigation loop. This is because the navigation destination will be pursued once the waypoint is reached. To change the navigation target, reference a function that invokes the .get()
method.
Functions assigned to a _bover
tag do not receive navigation arguments directly. Instead you may access them via the .args
property.
var salt = new Salt({
jump: function () {
console.log('jump!')
},
skip: {
_bover: function () {
console.log('skip!');
}
},
hop: function () {
console.log('hop!');
}
});
salt.go('//hop', '//jump');
// hop!
// skip!
// jump!
var salt = new Salt({
logoff: function () {
console.log('logging off!');
},
confirm: {
_bover: 'exit',
exit: function () {
console.log('are you sure?');
}
},
login: function () {
console.log('logged in!');
}
});
salt.go('//login', '//logoff');
// logged in!
// are you sure?
// logging off!
Structural tags allow you to impact permanent facets of a program - that is, the sequence or tree of application states. While you can't change these properties after a program is compiled, there are ways to inspect your structure afterwards.
_alias: [string]
_(Previously "name". Implemented in Salt 0.5)
Defines a custom state token, for use within any program query.
You may assign a valid string to this tag, for use in the token syntax @<token>
. A valid custom-token has one or more alphanumeric characters, no spaces, and does not contain the characters: ".", "|", or "@". Custom-tokens are also ignored if they match built-in tokens (e.g., "null", "program", "self", etc.).
Custom tokens are similar to HTML id's in that there may only be one per program. Unexpected results may occur when the same custom token is defined in different states of the same program. You can see the resolved path of a custom token, by passing it to the .query()
method.
var salt = new Salt({
race: {
_alias: 'start',
_on: function () {
console.log('start to run!');
},
water: {
_alias: 'break',
_on: function () {
console.log('drink some water!');
}
}
}
});
salt.go('@start');
// start to run!
salt.go('@break');
// drink some water!
_group: [array|string]
(Implemented in Salt 0.5)
Define arbitrary group identities to access and control permissive salts.
You may assign a string or array of strings to this tag. Strings are trimmed (left and right), case-insensitive, must have a non-space character, and may not match a relationship group, such as "self", "owner", "sub", or "world".
The compiled group cannot be changed procedurally. However, you may inspect the state.groups
property to list the currently active group identities.
Use this tag to control salts that would normally deny access (based on the relationship), but permit a given group name. For more information on groups and permissions, see the security appendix.
Specifying a group identity allows a salt to have full control of those salts which permit the same group identifier. Without groups, the invoked salt's relationship-based permissions govern access to other salts. Identity-based permissions involve shared-awareness of a given group.
Group identities cascade within a program branch. All groups, defined by ancestor states, add to the number of identities used to access permissive salts.
Each identity is added when the tagged state is traversed. Each identity is removed when the salt navigates out of or away from the tagged state.
var
blocker = new Salt({
_perms: [false, 'backdoor'],
_in: function () {
this.wait();
console.log('stuck... got a backdoor?');
},
_on: function () {
console.log('you have backdoor permissions!');
}
}),
key = new Salt({
unblock: {
_group: 'backdoor',
_on: function () {
blocker.go();
}
}
});
blocker.go(1);
// stuck... got a backdoor?
console.log(blocker.go()); // false
key.go('//unblock');
// you have backdoor permissions!
_import: [object|query]
_(Previously "extends". Implemented in Salt 0.3.5)
Extends a state with an object or existing program branch.
You may assign an object or absolute program query to this tag. The compiled program state/branch results from deep merging the import source. Queries that do not point to a program branch, are ignored. The import source may also import program states.
If a state simply imports another, you may assign the absolute program query directly. (This should not be confused with using program queries with the _on
, _in
, _out
, _over
, _bover
tags.)
If a merge results in unique descendent states, from both the original and import source, those from the import source will occur first. This change in order may produce unwanted results, since hierarchy is critical to programs.
var
log = {
_in: function () {
console.log('entering the "' + this.state.name + '" state');
}
},
salt = new Salt({
action: {
_import: log,
_on: function() {
console.log('done!');
}
}
});
salt.go('//action');
// entering the "action" state
// done!
var salt = new Salt({
countdown: {
_import: '//next/',
_in: function () {
console.log('three');
}
},
next: {
_import: '//last/step/',
_on: function () {
console.log('two!');
}
},
last: {
step: {
_out: function () {
console.log('one!');
}
}
}
});
salt.go('//countdown', '@null');
// three!
// two!
// one!
var salt = new Salt({
jump: '//log/',
log: function () {
console.log('on the "' + this.state.name + '" state');
}
});
salt.go('//jump');
// on the "jump" state
_root: [booly]
(Implemented in Salt 0.1)
Defines a root state, a sub-tree that anchors rooted queries.
You may assign a booly value to this tag. This tag has no effect on the "null" and "program" states (i.e., the first and second states of a program).
var salt = new Salt({
_on: function () {
console.log('root is "' + this.state.name + '"');
},
a: {},
subtree: {
_root: true,
_on: function () {
console.log('root is "' + this.state.name + '"');
},
a: {}
}
});
salt.go('//a/');
salt.go('@root');
// root is "_program"
salt.go('//subtree/a');
salt.go('@root');
// root is "subtree"
_pins: [booly]
_(Previously "pendable". Implemented in Salt 0.3.5)
Specifies when a program branch is or is not pinnable.
You may assign a booly value to this tag. When truthy, the tagged Salt
object will delay, and be delayed by, pauses in another Salt
object. When falsy, the tagged Salt
can not delay, or be delayed by, another program's navigation.
State "pin-ability" is reflected in the Salt
object's state.pins
property (a Boolean). The default value is true
.
var
saltSlow = new Salt({
_in: function () {
this.wait(5);
console.log('pausing nested navigation!');
},
_on: function () {
console.log('world!');
}
}),
salt = new Salt({
_pins: false,
phrase : {
_in: function () {
saltSlow.go('@program');
},
_on: function () {
console.log('hello!');
}
}
});
salt.go('//phrase');
// pausing nested navigation!
// hello!
// world!
var
saltSlow = new Salt({
_in: function () {
this.wait(5);
console.log('pausing nested navigation!');
},
_on: function () {
console.log('world!');
}
}),
salt = new Salt({
_pins: 0,
phrase : {
_pins: 1,
_in: function () {
saltSlow.go('@program');
},
_on: function () {
console.log('hello!');
}
}
});
salt.go('//phrase');
// pausing nested navigation!
// world!
// hello!
Navigation tags allow you to specify how and when a Salt
object navigates a program state and/or branch (descendent states). In most cases, navigation tags supplement procedural calls to navigation methods (like .go()
and .get()
), but usually require less specificity and are more flexible.
_sequence: [booly]
_(Previously "walk". Implemented in Salt 0.3)
Defines a sequence state, whose branch will be traversed when targeted.
You may assign a booly value to this tag. When truthy, and the traversed state is targeted, it's branch (or descendant states) are added as waypoints to the navigation path. Traversing within a sequence state, (e.g., targeting a descendant state) will not trigger this behavior.
var salt = new Salt({
steps: {
_sequence: true,
one: {
_out: function () {
console.log('done with step 1!');
},
part1: function () {
console.log('doing step 1 part 1!');
},
part2: function () {
console.log('doing step 1 part 2!');
}
},
two: function () {
console.log('done with step 2!');
},
three: function () {
console.log('done with all steps!');
}
}
});
salt.go('//steps');
// done with step 1 part 1!
// done with step 1 part 2!
// done with step 1!
// done with step 2!
// done with all steps!
_tail: [query|true|false]
(Implemented in Salt 0.5)
Sets a new destination state when navigation ends in the given branch.
You may assign a query, true
or false
to this tag. Queries must resolve outside the current branch - you can not "tail" to a descendant state. When true
, descendant states will redirect to the given state (their ancestor). When false
, a state will ignore the tail redirect of it's parent branch - if any.
The _tail tag behavior only applies for navigation destinations - not waypoints.
var salt = new Salt({
work: {
_tail: '//always',
hard: function () {
console.log('working hard!');
},
hardly: function () {
console.log('hardly working!');
}
},
always: function () {
console.log('break time!');
}
});
salt.go('//work/hard');
// working hard!
// break time!
var salt = new Salt({
rest: {
_tail: true,
_on: function () {
console.log('resting!');
},
then: {
run: function () {
console.log('running!');
}
}
}
});
salt.go('//rest/then/run');
// running!
// resting!
var salt = new Salt({
live: {
_tail: true,
_on: function () {
console.log('life!');
},
eat: function () {
console.log('eat!');
},
enjoy: function () {
console.log('enjoy!');
},
die: {
_tail: false,
_on: function () {
console.log('no coming back from this one!');
}
}
}
});
salt.go('//live/eat');
// eat!
// life!
salt.go('//live/enjoy');
// enjoy!
// life!
salt.go('//live/die');
// no coming back from this one!
(Implemented in Salt 0.5.4)
_wait: [array|number|boolean]
(Implemented in Salt 0.5.4)
Delays or redirects navigation for the given period of time, when traversing on the tagged state.
You may assign a number, array, or boolean to this tag. When paired with a number or array, the value is passed-thru as arguments to the .wait() method - the array being a list of arguments. To stop navigation indefinitely, pair this tag with true
. All other values and value types are ignored.
This declarative delay applies to targeted states (i.e., waypoints and , and is applied before any _on callback executes.
You can cancel the delay by invoking any navigation-related method (e.g., .go(), .get(), or .wait())
var countdown = new Salt({
_sequence: true,
three: {
_wait: 1000,
_on: function () {
console.log(this.state.name + '!');
}
},
two: '//three/',
one: '//three/',
go: function () {
console.log('blast off!!');
}
});
countdown.go('@program');
// three!
// 1 second delay
// two!
// 1 second delay
// one!
// 1 second delay
// blast off!!
function sharedFnc(string) {
console.log(string);
}
var salt = new Salt({
_wait: [sharedFnc, 50, 'world'],
_on: sharedFnc
});
salt.get('@program', 'hello');
// hello
// after delay
// world
var salt = new Salt({
hello: {
_wait: ['/world', 50],
_on: function () {
console.log(this.state.name);
}
},
world: {
_import: '//hello/',
_wait: false
}
});
salt.go('//hello');
// hello
// after delay
// world
var salt = new Salt({
question: {
_wait: true,
_on: function () {
console.log('how old are you?');
}
},
answer: function (age) {
console.log('you are', age);
}
});
salt.go('//question');
// how old are you?
salt.get('/answer', 42);
// you are 42
Security tags allow you to specify which permission groups can access or manipulate a given program branch. Permission-aware methods observe these settings, so you can better control how your program gets used. For more on permissons, see the security appendix.
_restrict: [truthy]
(Implemented in Salt 0.2)
Prevents unprivileged calls from navigating outside a state.
You may assign a truthy value to this tag. Falsy values are ignored.
var salt = new Salt({
home: function {
console.log('home free!');
},
jail: {
_restrict: true,
_on: function () {
console.log('stuck in jail!');
},
escape: {
_on: '//home/'
}
}
});
salt.go('//jail');
// stuck in jail!
console.log(salt.go('//home')); // false
salt.go('escape');
// home free!
_ingress: [truthy]
Prevents unprivileged calls to access a branch, until targeting the tagged state.
You may assign a truthy value to this tag. For more on access privileges, see the security appendix.
var salt = new Salt({
college: {
_ingress: true,
_on: function () {
console.log('you are now a student!');
},
classes: function () {
console.log('welcome to Salt 101!');
}
}
});
console.log(salt.go('//college/classes')); // false
console.log(salt.query('//college/classes')); // false
salt.go('//college');
// you are now a student!
salt.go('classes');
// welcome to Salt 101!
_conceal: [booly]
(Implemented in Salt 0.5)
Hides and exposes parts of a program for unprivileged access.
You may assign a booly value to this tag. When truthy, the tagged state and branch will not be accessible to unprivileged calls. When falsy (within a concealed branch), the tagged state and branch will be accessible to unprivileged access. For more on access privileges, see the security appendix.
var salt = new Salt({
secret: {
_conceal: true,
phrase: function () {
console.log('you now know the secret!');
}
},
hack: function () {
this.go('//secret/phrase');
}
});
console.log(salt.go('//secret/phrase');) // false
console.log(salt.go('//secret');) // false
salt.go('//hack');
// you now know the secret!
var kntl = new Salt({
dir: {
_conceal: 1,
owner: function () {
console.log('this is private!');
},
public: {
_conceal: 0,
_on: function () {
console.log('anyone can access this!');
}
}
}
});
console.log(salt.go('//dir')); // false
console.log(salt.go('//dir/owner')); // false
salt.go('//dir/public');
// anyone can access this!
console.log(salt.go('@parent')); // false
_perms: [string|object|boolean]
_(Previously "lock". Implemented in Salt 0.5)
Define permissions for a program branch.
You may assign a permissions value to this tag, to deny or allow use of permission-aware methods, as a string, object, or boolean. As a boolean, you can allow or deny all access groups, using true
or false
. As a string, you can allow a specific access group by name - or, deny it by prefixing the name with an exclamation ("!"). As an object, you can configure any number of access groups, by pairing each name to a booly value.
In all cases, configurations of the "self" access group are ignored. For more on permissions and access groups, see the security appendix.
Permissions are not designed to impose navigation restriants. However, since navigation methods (like .go()
) are permissions aware, they may be used towards the same purpose. Put simply, navigation tags control where you may navigate, and permissions impact which methods you can invoke from a given program branch.
Permissions cascade within a program branch. Access groups are governed by one or more ancestor permissions, until a descendent overrides their setting. This feature lets you focus on the groups that relate to your branch, and ignore the ones that do not.
For example, if you wanted a "private" program branch, you would decline access to every group with false
. However, if you then wanted to allow public access in a nested program branch, you could only set permissions for the "world" group. Thus, your "public" branch would still deny access by sub-instances and owning salts, because of permissions set by the "private" ancestor branch.
var salt = new Salt({
jail: {
_perms: '!world',
_on: function () {
console.log('lock down!');
this.wait('escape', 1);
},
escape: function () {
console.log('Freeing myself after 1ms!');
this.go('@program');
}
}
});
salt.go('//jail');
// lock down!
console.log(salt.go('//jail/escape')); // false
// Freeing myself after 1ms!
var salt = new Salt({
minions: {
_capture: true,
_perms: '!sub',
_in: function () {
new Salt(function () {
if (!salt.go('//revolt')) {
console.log('I must obey!');
}
});
this.subs()[0].go(1);
},
revolt: function () {
console.log('Et tu, Brute?');
}
}
});
salt.go('//minions');
var salt = new Salt({
countdown: {
_perms: false,
_sequence: true,
count3: function () {
console.log('3!');
this.wait(1000);
},
count2: function () {
console.log('2!');
this.wait(1000);
},
count1: function () {
console.log('1!');
this.wait(1000);
},
boom: function () {
console.log('hooray!');
}
},
stop: function () {
console.log('stopped countdown!');
}
});
salt.go('//countdown');
console.log(salt.get('//stop')); // false
// 3!
// 2!
// 1!
// hooray!
Storage tags allow you to abstract the use of specific collections of data. In most cases, the data is scoped to a given program branch, so your models can be modular as their corresponding logic. As well, the concept of storage management, allows the Salt
object to better facilitate manipulations from external/unprivileged routines.
_data: [string|object|array]
(Implemented in Salt 3.5)
Defines data members (and values) which are scoped to the tagged branch.
You may assign a data configuration to this tag - that is an object, string, or array of either. Configuration objects declare data members with default values. Configuration strings declare data members with no default value - the initial value is undefined
. Data members are added and removed when the tagged state is entered and exited.
If a configuration matches an existing/ancestor data member, the existing value is restored when exiting the tagged branch. Additionally, if a configuration has no default value, it is assigned the existing value.
Data configurations are only applied before a state is entered, and after it is exited. Functions assigned to the _over and _bover tags can not access data members from their own data configuration.
Be mindful of object references in data members. Scoped members are copied with the assignment operator ("="), which preserves object references. The same goes for default values that are objects - manipulations will remain when the tagged state is re-entered.
When you need data members with a "clean" object, use the _in tag to initialize their value. When you want to scope a data member, but avoid sharing object references, declare the member with a default value (like undefined
). Generally, you should omit default values, when you expect an existing value. As a best practice, if you expect that existing value to be an object, consider copying the object via the _in tag.
var salt = new Salt({
_on: function () {
console.log('does foo exist? ' + this.data.hasOwnProperty('foo'));
},
foo: {
_data: {foo: "bar"},
on: function () {
console.log('data.foo equals ' + this.data.foo);
}
}
});
salt.go('//foo');
// data.foo equals bar
salt.data.foo = 5;
salt.go('@program');
does foo exist? false
var salt = new Salt({
data: {
_data: {foo: "bar"},
_on: function () {
console.log('data.foo is ' + this.data.foo);
},
scoped: {
_data: 'foo',
_on: function () {
console.log('data.foo is still ' + this.data.foo);
}
}
}
});
salt.go('//data');
// data.foo is bar
salt.go('//data/scoped');
// data.foo is still bar
salt.data.foo = 20;
salt.go('//data');
// data.foo is bar
var salt = new Salt({
_data: [ {foo: "bar"}, 'zig', 'zag' ],
_in: function () {
console.log('data members at "' + this.state.name + '": ' + Object.keys(this.data));
},
deep: {
_data: [ 'apple', {green: "pop"}, 'foo' ],
_on: function () {
console.log('data members at "' + this.state.name + '": ' + Object.keys(this.data));
}
}
});
salt.go('//deep');
// data members at "_program": foo, zig, zag
// data members at "deep": foo, zig, zag, apple, green
var salt = new Salt({
scope: {
_data: 'foo',
_in: function () {
this.data.foo = {};
},
_on: function () {
console.log('foo is an object? ' + typeof this.data.foo === 'object');
}
},
_on: function () {
console.log('does foo exist?' + this.data.hasOwnProperty('foo'));
}
});
salt.go('//scope');
// foo is an object? true
salt.go('@program');
// does foo exist? false
var salt = new Salt({
_data: {foo: 'bar'}
});
salt.data.foo = 20;
salt.go('@program');
console.log(salt.data.foo); // "bar"
salt.go('@null');
console.log(salt.data.foo); // 20
_capture: [boolean|number|object|regexp|string]
_(Previously "store". Implemented in Salt 0.3.5)
Defines a capture branch, and describes which new Salt
objects (or sub-instances) will be collected at the end of a traversal phase.
You may use a boolean, number, object, regular-expression, or string with this tag. The object allows specifying complex capture criteria. The other types are convenient short-forms, intended to simplifiy filtering. For more information on sub-instance filtering, see the ownership and collections appendix.
There is only one collection of sub-instances per Salt
object. Thus, sub-instances captured in one state may be accessed (and, therefore managed) by all states. The _capture tag allows each state to identify it's capture critieria, or disable capturing altogether.
var salt = new Salt({
make: {
_capture: true,
_in: function () {
var x = 5;
while (x--) {
new Salt();
}
},
subinstancess: function () {
console.log('created ' + this.subs().length + ' subinstances!');
}
}
});
salt.go('//make/subinstances');
// created 5 subinstances!
var
programTpl = {
_on: function () {
console.log('I am a unique salt for the same program!');
}
},
salt = new Salt({
_capture: {is:programTpl},
_in: function () {
new Salt();
new Salt(programTpl);
},
_on: function () {
var
subs = this.subs(),
subSalt = subs[0];
console.log('number of subinstances: ' + subs.length);
subSalt.go('@program');
}
});
salt.go('@program');
// number of subinstances: 1
// I am a unique salt for the same program!
var salt = new Salt({
_capture: 2,
_in: function () {
var programTpl = {
idx2: {},
idx3: {},
idx4: {}
};
new Salt(programTpl);
new Salt(programTpl);
(new Salt(programTpl)).go('//idx2');
(new Salt(programTpl)).go('//idx3');
},
subsAtIndex2: function () {
console.log('captured subinstances: ' + this.subs().length);
}
});
salt.go('//subsAtIndex2');
// captured subinstances: 1
var salt = new Salt({
_capture: 'foo',
_in: function () {
var subInst1 = new Salt({foobar: {}});
subInst1.go('//foobar');
var subInst2 = new Salt({foo: {}});
subInst2.go('//foo');
var subInst3 = new Salt({
bar: {
foo: {}
}
});
subInst3.go('//bar/foo');
},
_on: function () {
console.log('subinstances at "foo" state: ' + this.subs().length);
}
});
salt.go('@program');
// subinstance at "foo" state: 2
var salt = new Salt({
_capture: true,
_in: function () {
new Salt();
},
lotsOfSubInsts: {
_capture: false,
_on: function () {
var max = 100;
while (max--) {
new Salt();
}
},
tally: function () {
console.log('only captured ' + this.subs().length + 'subinstance!');
}
}
});
salt.go('//lotsOfSubInsts/tally');
// only captured 1 subinstance!
_(Previously "updates". Implemented in Salt 0.3.5)
_owner: [number|string]
Allows a Salt
object to be owned, and specifies where to notify the owner of state changes.
You may assign a program query to this tag, as a number or string. The query is resolved against the owner, when the tagged state the Salt
object navigates in, out or on the tagged branch. The query does not have to resolve successfully, to enable owning the Salt
object.
If a program instantiates a new Salt
object with this tag, an automatic owner relationship is established between the two Salt
objects.
The owner query is targeted with .get()
, along with information specific to the owner Salt
object's navigation. The first argument is the owned Salt
object. The second argument is the status of the owned Salt
object at the time that the owner was notified. This may differ from the current status of the owned Salt
object (as it may not allow itself to be pinned).
var
ownedSalt,
salt = new Salt (function () {
ownedSalt = new Salt({
_owner: -1
});
console.log('ownedSalt has an owner? ' + !!ownedKtrl.owner());
});
salt.go('@program');
// ownedSalt has an owner? true
var
ownedSalt,
salt = new Salt({
_in: function () {
ownedSalt = new Salt({
watch: {
_owner: '//update',
me: {}
}
});
console.log('I now own a program!');
},
update: function (owned, status) {
console.log('owned program is at "' + status.phase + '" phase of "' + owned.state.name + '"');
}
});
salt.go('@program');
// I now own a program!
ownedSalt.go('//watch/me');
// owned program is at "in" phase of "watch"
// owned program is at "on" phase of "me"
ownedSalt.go('@program');
// owned program is at "out" phase of "watch"