arnaucube
arnaucube/thoughtsPublic Self-hosted repository
thoughts/node_modules/mongoose/lib/statemachine.js
node_modules/mongoose/lib/statemachine.jsRaw
  1
  2/*!
  3 * Module dependencies.
  4 */
  5
  6var utils = require('./utils');
  7
  8/*!
  9 * StateMachine represents a minimal `interface` for the
 10 * constructors it builds via StateMachine.ctor(...).
 11 *
 12 * @api private
 13 */
 14
 15var StateMachine = module.exports = exports = function StateMachine () {
 16  this.paths = {};
 17  this.states = {};
 18}
 19
 20/*!
 21 * StateMachine.ctor('state1', 'state2', ...)
 22 * A factory method for subclassing StateMachine.
 23 * The arguments are a list of states. For each state,
 24 * the constructor's prototype gets state transition
 25 * methods named after each state. These transition methods
 26 * place their path argument into the given state.
 27 *
 28 * @param {String} state
 29 * @param {String} [state]
 30 * @return {Function} subclass constructor
 31 * @private
 32 */
 33
 34StateMachine.ctor = function () {
 35  var states = utils.args(arguments);
 36
 37  var ctor = function () {
 38    StateMachine.apply(this, arguments);
 39    this.stateNames = states;
 40
 41    var i = states.length
 42      , state;
 43
 44    while (i--) {
 45      state = states[i];
 46      this.states[state] = {};
 47    }
 48  };
 49
 50  ctor.prototype.__proto__ = StateMachine.prototype;
 51
 52  states.forEach(function (state) {
 53    // Changes the `path`'s state to `state`.
 54    ctor.prototype[state] = function (path) {
 55      this._changeState(path, state);
 56    }
 57  });
 58
 59  return ctor;
 60};
 61
 62/*!
 63 * This function is wrapped by the state change functions:
 64 *
 65 * - `require(path)`
 66 * - `modify(path)`
 67 * - `init(path)`
 68 *
 69 * @api private
 70 */
 71
 72StateMachine.prototype._changeState = function _changeState (path, nextState) {
 73  var prevBucket = this.states[this.paths[path]];
 74  if (prevBucket) delete prevBucket[path];
 75
 76  this.paths[path] = nextState;
 77  this.states[nextState][path] = true;
 78}
 79
 80/*!
 81 * ignore
 82 */
 83
 84StateMachine.prototype.clear = function clear (state) {
 85  var keys = Object.keys(this.states[state])
 86    , i = keys.length
 87    , path
 88
 89  while (i--) {
 90    path = keys[i];
 91    delete this.states[state][path];
 92    delete this.paths[path];
 93  }
 94}
 95
 96/*!
 97 * Checks to see if at least one path is in the states passed in via `arguments`
 98 * e.g., this.some('required', 'inited')
 99 *
100 * @param {String} state that we want to check for.
101 * @private
102 */
103
104StateMachine.prototype.some = function some () {
105  var self = this;
106  var what = arguments.length ? arguments : this.stateNames;
107  return Array.prototype.some.call(what, function (state) {
108    return Object.keys(self.states[state]).length;
109  });
110}
111
112/*!
113 * This function builds the functions that get assigned to `forEach` and `map`,
114 * since both of those methods share a lot of the same logic.
115 *
116 * @param {String} iterMethod is either 'forEach' or 'map'
117 * @return {Function}
118 * @api private
119 */
120
121StateMachine.prototype._iter = function _iter (iterMethod) {
122  return function () {
123    var numArgs = arguments.length
124      , states = utils.args(arguments, 0, numArgs-1)
125      , callback = arguments[numArgs-1];
126
127    if (!states.length) states = this.stateNames;
128
129    var self = this;
130
131    var paths = states.reduce(function (paths, state) {
132      return paths.concat(Object.keys(self.states[state]));
133    }, []);
134
135    return paths[iterMethod](function (path, i, paths) {
136      return callback(path, i, paths);
137    });
138  };
139}
140
141/*!
142 * Iterates over the paths that belong to one of the parameter states.
143 *
144 * The function profile can look like:
145 * this.forEach(state1, fn);         // iterates over all paths in state1
146 * this.forEach(state1, state2, fn); // iterates over all paths in state1 or state2
147 * this.forEach(fn);                 // iterates over all paths in all states
148 *
149 * @param {String} [state]
150 * @param {String} [state]
151 * @param {Function} callback
152 * @private
153 */
154
155StateMachine.prototype.forEach = function forEach () {
156  this.forEach = this._iter('forEach');
157  return this.forEach.apply(this, arguments);
158}
159
160/*!
161 * Maps over the paths that belong to one of the parameter states.
162 *
163 * The function profile can look like:
164 * this.forEach(state1, fn);         // iterates over all paths in state1
165 * this.forEach(state1, state2, fn); // iterates over all paths in state1 or state2
166 * this.forEach(fn);                 // iterates over all paths in all states
167 *
168 * @param {String} [state]
169 * @param {String} [state]
170 * @param {Function} callback
171 * @return {Array}
172 * @private
173 */
174
175StateMachine.prototype.map = function map () {
176  this.map = this._iter('map');
177  return this.map.apply(this, arguments);
178}
179