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