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};
17
18/*!
19 * StateMachine.ctor('state1', 'state2', ...)
20 * A factory method for subclassing StateMachine.
21 * The arguments are a list of states. For each state,
22 * the constructor's prototype gets state transition
23 * methods named after each state. These transition methods
24 * place their path argument into the given state.
25 *
26 * @param {String} state
27 * @param {String} [state]
28 * @return {Function} subclass constructor
29 * @private
30 */
31
32StateMachine.ctor = function() {
33 var states = utils.args(arguments);
34
35 var ctor = function() {
36 StateMachine.apply(this, arguments);
37 this.paths = {};
38 this.states = {};
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 = new StateMachine();
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 _this = this;
106 var what = arguments.length ? arguments : this.stateNames;
107 return Array.prototype.some.call(what, function(state) {
108 return Object.keys(_this.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 _this = this;
130
131 var paths = states.reduce(function(paths, state) {
132 return paths.concat(Object.keys(_this.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};