Skip to content
Bemi Faison edited this page Feb 20, 2014 · 7 revisions

Program Tags: Table of Contents

Status of this document

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.

Overview

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.

What are tags?

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

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".

The _on tag

  _on: [function|query]

_(Previously "main". Implemented in Salt 0.1)

Description

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.

Function shorthand

If a state has no other tags, you can omit using the _on tag, and simply assign a function to the state directly.

Destination queries

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.

Handling 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.

Examples

Invoke a function after reaching a target state
var salt = new Salt({
  _on: function () {
    console.log('hello world!');
  }
});
salt.go('@program');
// hello world!
Using the shorthand form for otherwise empty states
var salt = new Salt(function () {
  console.log('hello world!');
});
salt.go('@program');
// hello world!
Redirect after reaching a target state, using a query
var salt = new Salt({
  here: {
    _on: '../there'
  },
  there: function () {
    console.log('now you\'re there!');
  }
});
salt.go('//here');
// now you're there!

The _in tag

  _in: [function|query]

(Implemented in Salt 0.1)

Description

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.

Destination queries

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.

Caution with queries

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.

Handling navigation arguments

Functions assigned to an _in tag do not receive navigation arguments directly. Instead you may access them via the .args property.

Examples

Using _in with a function
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!
Redirect using _in with a query
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!

The _out tag

(Implemented in Salt 0.1)

  _out: [function|query]

Description

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.

Destination queries

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.

Caution with queries

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.

Arguments

Functions assigned to an _out tag do not receive navigation arguments directly. Instead you may access them via the .args property.

Examples

Using _out with a function
var salt = new Salt({
  _out: function () {
    console.log('good bye!');
  }
});
salt.go('@program', '@null');
// good bye!
Redirect using _out with a query
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!

The _over tag

  _over: [function|query]

(Implemented in Salt 0.1)

Description

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.

Destination queries

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.

Caution with queries

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.

Arguments

Functions assigned to an _over tag do not receive navigation arguments directly. Instead you may access them via the .args property.

Examples

Using _over with a function
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!
Redirect using _over with a query
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!

The _bover tag

  _bover: [function|query]

(Implemented in Salt 0.1)

Description

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.

Destination queries

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.

Caution with queries

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.

Arguments

Functions assigned to a _bover tag do not receive navigation arguments directly. Instead you may access them via the .args property.

Examples

Using _bover with a function
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!
Redirect using _bover with a query
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

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.

The _alias tag

  _alias: [string]

_(Previously "name". Implemented in Salt 0.5)

Description

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.).

Duplicate custom tokens

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.

Examples

Define and use custom tokens
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!

The _group tag

  _group: [array|string]

(Implemented in Salt 0.5)

Description

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.

Relationship vs Identity

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.

Cascading groups

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.

Examples

Gain access to a salt using groups
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!

The _import tag

  _import: [object|query]

_(Previously "extends". Implemented in Salt 0.3.5)

Description

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.

Existing branch shorthand

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.)

Merge order

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.

Examples

Extend a state using _import with an object
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!
Merge states using _import with a query
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!
Clone a state using the _import short-form
var salt = new Salt({
  jump: '//log/',
  log: function () {
    console.log('on the "' + this.state.name + '" state');
  }
});
salt.go('//jump');
//  on the "jump" state

The _root tag

  _root: [booly]

(Implemented in Salt 0.1)

Description

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).

Examples

Using a rooted query in a rooted branch
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"

The _pins tag

  _pins: [booly]

_(Previously "pendable". Implemented in Salt 0.3.5)

Description

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.

Examples

Run programs asynchronously without pinning
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!
Run programs synchronously with pinning
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

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.

The _sequence tag

  _sequence: [booly]

_(Previously "walk". Implemented in Salt 0.3)

Description

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.

Examples

Auto-execute a branch using _sequence
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!

The _tail tag

  _tail: [query|true|false]

(Implemented in Salt 0.5)

Description

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.

Examples

Perform follow-up logic using the _tail tag.
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!
Redirect to the tagged state using _tail with true
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!
Exclude specific states using _tail with false
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!

The _next tag

  _next: query

(Implemented in Salt 0.5.4)

Description

Schedules navigating toward the given query, before traversing on the tagged state.

You may assign a numeric or string query to this tag. Numbers reference states by their compilation index. String queries are resolved against the tagged state. This tag has no effect when the query is invalid.

Tag interactions

The _next tag impacts navigation after a _on tag short-form redirect. In other words, the _next redirect is observed before the _on redirect.

The _next tag impacts navigation before the callback of an _on tag. Callbacks are able to observe the queued navigation target via .status('targets'). "On" callbacks of destination states will continue to receive navigation arguments.

The _next tag impacts navigation before a _wait tag. Whenever the _wait delay expires (and unless directed otherwise), navigation will resume towards the target set by a _next tag.

Destination queries

By default, _next 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.

Caution with queries

When using the _next tag with a query that resolves to the same state, or a route that does the same, risks an infinite navigation loop. This is because _next operates during the "on" traversal phase, and any valid state is pursued. The only way to avoid navigating towards the _next state is by setting a new destination with the .get() method.

Examples

Direct navigation
var inAndOut = new Salt({
  _next: 0,
  _in: function () {
    console.log(1);
  },
  _on: function () {
    console.log(2);
  }
  _out: function () {
    console.log(3);
  }
});
inAndOut.go(1);
// 1
// 2
// 3
Stepped sequence
var countdown = new Salt({
  three: {
    _next: '@next',
    _on: function () {
      console.log(this.state.name + '!');
    }
  },
  two: '//three/',
  one: '//three/'
});
countdown.go('//three');
// three!
// two!
// one!
Double redirect
var phrase = new Salt({
  _next: 'hello',
  _on: 'world!',
  hello: function () {
    console.log(this.state.name);
  },
  "world!": '//hello/'
});
phrase.go(1);
// hello
// world!

The _wait tag

  _wait: [array|number|boolean]

(Implemented in Salt 0.5.4)

Description

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())

Examples

Delay navigation
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!!
Invoke function after delay
function sharedFnc(string) {
  console.log(string);
}

var salt = new Salt({
  _wait: [sharedFnc, 50, 'world'],
  _on: sharedFnc
});

salt.get('@program', 'hello');
// hello
  // after delay
// world
Redirect navigation after delay
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
Pause navigation indefinitely
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

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.

The _restrict tag

  _restrict: [truthy]

(Implemented in Salt 0.2)

Description

Prevents unprivileged calls from navigating outside a state.

You may assign a truthy value to this tag. Falsy values are ignored.

Examples

Deny external routines from navigating outside a state
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!

The _ingress tag

  _ingress: [truthy]

Description

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.

Examples

Identify a "gateway" state using _ingress
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!

The _conceal tag

  _conceal: [booly]

(Implemented in Salt 0.5)

Description

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.

Examples

Deny external access to a program branch using _conceal
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!
Expose specific program branches using _conceal with true and false
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

The _perms tag

  _perms: [string|object|boolean]

_(Previously "lock". Implemented in Salt 0.5)

Description

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 vs Navigation

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.

Cascading Permissions

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.

Examples

Deny the "world" group from directing a salt
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!
Prevent sub-instances from directing their capturing instance
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');
Ignore directions from anything but the original salt
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

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.

The _data tag

  _data: [string|object|array]

(Implemented in Salt 3.5)

Description

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 during "over" and "bover" traversal phases

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.

Working with object values

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.

Examples

Add a data member with a default value
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
Scope an existing data member
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
Define data configurations with an array
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
Set a data member to an object via the _in tag
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
Preserve existing data members
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

The _capture tag

  _capture: [boolean|number|object|regexp|string]

_(Previously "store". Implemented in Salt 0.3.5)

Description

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.

Examples

Collect all sub-instances
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!
Capture sub-instances with a specific program
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!
Capture sub-instances at a specific state index
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
Capture sub-instances on a named state
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
Stop capturing within specific branches
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!

The _owner tag

_(Previously "updates". Implemented in Salt 0.3.5)

  _owner: [number|string]

Description

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.

Owner notification

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).

Examples

Establish an owner for a new Salt object
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
Update an owner while navigating a branch.
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"
Clone this wiki locally