git.fiddlerwoaroof.com
Browse code

continue unifying lenses

Ed Langley authored on 26/05/2018 06:10:55
Showing 3 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,88 @@
1
+import * as R from 'ramda';
2
+import {
3
+    Map,
4
+    fromJS
5
+} from 'immutable';
6
+
7
+let Symbol = (window['Symbol'] !== undefined) ? window['Symbol'] : x => `_Symbol__${x}`;
8
+
9
+export const fireListeners = Symbol('fireListeners');
10
+
11
+const noOpTransform = val => val;
12
+
13
+export function lensTransformer(
14
+    lens,
15
+    getTransform = noOpTransform,
16
+    setTransform = noOpTransform
17
+) {
18
+    return {
19
+        get: () => getTransform(lens.get()),
20
+        set: (val) => lens.set(setTransform(val)),
21
+        withValue(cb) {
22
+            return cb(this.get());
23
+        },
24
+
25
+    };
26
+}
27
+
28
+export function makeLens(key, self) {
29
+    const createLens = (keyPath) => {
30
+
31
+        return {
32
+            get() {
33
+                let result = self._getState().getIn(keyPath);
34
+                if (result && result.toJS !== undefined) {
35
+                    result = result.toJS();
36
+                }
37
+                return result;
38
+            },
39
+            set(val) {
40
+                const oldState = self._getState();
41
+
42
+                self.localStore = self.localStore.setIn(keyPath, fromJS(val));
43
+                this._addSetAction(keyPath, R.clone(val));
44
+
45
+                self[fireListeners](oldState, self._getState());
46
+            },
47
+
48
+            _prepareForSet(keyPath) {
49
+                if (self.localStore.getIn(keyPath) === undefined) {
50
+                    for (let x = 0; x < keyPath.length; x++) {
51
+                        const subPath = keyPath.slice(0, x);
52
+                        if (self.localStore.getIn(subPath) === undefined && self.localStore.hasIn(subPath)) {
53
+                            self.localStore = self.localStore.setIn(subPath, Map());
54
+                        }
55
+                    }
56
+                }
57
+            },
58
+
59
+            _addSetAction(keyPath, val) {
60
+                let lastAction = self.actions[self.actions.length - 1];
61
+                if (lastAction && (lastAction[0][0] === 'lensFor') && R.equals(lastAction[0][1], keyPath)) {
62
+                    self.actions.pop();
63
+                }
64
+
65
+                self.actions.push([
66
+                    ['lensFor', keyPath],
67
+                    ['set', val]
68
+                ]);
69
+            },
70
+
71
+            lensFor(key) {
72
+                let subPath = key instanceof Array ? key : [key];
73
+                return createLens([...keyPath, ...subPath]);
74
+            },
75
+
76
+            withValue(cb, ...additional) {
77
+                return cb(this.get(), ...additional);
78
+            },
79
+
80
+            swap(cb) {
81
+                this.set(cb(this.get()));
82
+                return this.get();
83
+            }
84
+        };
85
+    };
86
+
87
+    return createLens(key instanceof Array ? key : [key]);
88
+}
0 89
\ No newline at end of file
... ...
@@ -3,111 +3,23 @@ import {
3 3
     Map,
4 4
     fromJS
5 5
 } from 'immutable';
6
-
7
-const noOpTransform = val => val;
8
-
9
-export function lensTransformer(
10
-    lens,
11
-    getTransform = noOpTransform,
12
-    setTransform = noOpTransform
13
-) {
14
-    return {
15
-        get: () => getTransform(lens.get()),
16
-        set: (val) => lens.set(setTransform(val)),
17
-        withValue(cb) {
18
-            return cb(this.get());
19
-        },
20
-
21
-    };
22
-}
23
-
24
-let Symbol = (window['Symbol'] !== undefined) ? window['Symbol'] : x => `_Symbol__${x}`;
25
-
26
-let fireListeners = Symbol('fireListeners');
27
-
28
-function makeLens(key, self) {
29
-    const createLens = (keyPath) => {
30
-        const lens = R.lensPath(keyPath);
31
-
32
-        return {
33
-            get() {
34
-                let result = self._getState().getIn(keyPath);
35
-                if (result && result.toJS !== undefined) {
36
-                    result = result.toJS();
37
-                }
38
-                return result;
39
-            },
40
-            set(val) {
41
-                const oldState = self._getState();
42
-
43
-                self.localStore = self.localStore.setIn(keyPath, fromJS(val));
44
-                this._addSetAction(keyPath, R.clone(val));
45
-
46
-                self[fireListeners](oldState, self._getState());
47
-            },
48
-
49
-            _prepareForSet(keyPath) {
50
-                if (self.localStore.getIn(keyPath) === undefined) {
51
-                    for (let x = 0; x < keyPath.length; x++) {
52
-                        const subPath = keyPath.slice(0, x);
53
-                        if (self.localStore.getIn(subPath) === undefined && self.localStore.hasIn(subPath)) {
54
-                            self.localStore = self.localStore.setIn(subPath, Map());
55
-                        }
56
-                    }
57
-                }
58
-            },
59
-
60
-            _addSetAction(keyPath, val) {
61
-                let lastAction = self.actions[self.actions.length - 1];
62
-                if (lastAction && (lastAction[0][0] === 'lensFor') && R.equals(lastAction[0][1], keyPath)) {
63
-                    self.actions.pop();
64
-                }
65
-
66
-                self.actions.push([
67
-                    ['lensFor', keyPath],
68
-                    ['set', val]
69
-                ]);
70
-            },
71
-
72
-            lensFor(key) {
73
-                let subPath = key instanceof Array ? key : [key];
74
-                return createLens([...keyPath, ...subPath]);
75
-            },
76
-
77
-            withValue(cb) {
78
-                return cb(this.get());
79
-            }
80
-        };
81
-    };
82
-
83
-    return createLens(key instanceof Array ? key : [key]);
84
-};
85
-
6
+import { makeLens, lensTransformer, fireListeners } from './lens';
86 7
 
87 8
 function RecordingStateContainer(container) {
88 9
     this.localStore = Map();
89 10
     this.container = container;
90 11
     this.listeners = [];
91 12
     this.actions = [];
92
-};
93
-
13
+}
94 14
 
95 15
 RecordingStateContainer.prototype = {
96
-    set(key, value) {
97
-        this.lensFor(key).set(value);
98
-    },
16
+    set(key, value) { this.lensFor(key).set(value); },
99 17
 
100
-    get(key) {
101
-        return this.lensFor(key).get();
102
-    },
18
+    get(key) { return this.lensFor(key).get(); },
103 19
 
104 20
     setState(newState) {
105 21
         this.localStore = this.localStore.merge(newState);
106
-        this.actions = [
107
-            [
108
-                ['setState', this.localStore]
109
-            ]
110
-        ];
22
+        this.actions = [ [ ['setState', this.localStore] ] ];
111 23
     },
112 24
 
113 25
     _getState() {
... ...
@@ -163,24 +75,24 @@ RecordingStateContainer.prototype = {
163 75
 
164 76
 };
165 77
 
166
-export default function StateContainer(data = {}, computed = {}) {
78
+export { lensTransformer };
79
+
80
+export default function StateContainer(data = {}) {
167 81
 
168 82
 
169 83
     var listeners = [];
170 84
     var state = [fromJS(data)];
171 85
 
172
-    function fireListeners(oldState, newState) {
173
-        listeners.forEach((listener) => {
174
-            listener(oldState, newState);
175
-        });
176
-    }
177
-
178 86
     return {
87
+        [fireListeners](oldState, newState) {
88
+            listeners.forEach((listener) => {
89
+                listener(oldState, newState);
90
+            });
91
+        },
92
+
179 93
         lensFor(key) {
180 94
             let self = this;
181 95
             const createLens = (keyPath) => {
182
-                const lens = R.lensPath(keyPath);
183
-
184 96
                 return {
185 97
                     get() {
186 98
                         let result = self._currentState.getIn(keyPath);
... ...
@@ -202,7 +114,7 @@ export default function StateContainer(data = {}, computed = {}) {
202 114
                         }
203 115
 
204 116
                         self._currentState = self._currentState.setIn(keyPath, fromJS(val));
205
-                        fireListeners(oldState, self._currentState);
117
+                        self[fireListeners](oldState, self._currentState);
206 118
                     },
207 119
                     lensFor(key) {
208 120
                         let subPath = key instanceof Array ? key : [key];
... ...
@@ -211,6 +123,11 @@ export default function StateContainer(data = {}, computed = {}) {
211 123
 
212 124
                     withValue(cb) {
213 125
                         return cb(this.get());
126
+                    },
127
+
128
+                    swap(cb) {
129
+                        this.set(cb(this.get()));
130
+                        return this.get();
214 131
                     }
215 132
                 };
216 133
             };
... ...
@@ -250,14 +167,10 @@ export default function StateContainer(data = {}, computed = {}) {
250 167
          * */
251 168
         setState(newState) {
252 169
             const oldState = this._currentState;
253
-            let mergedState;
254 170
 
255 171
             this._currentState = this._currentState.merge(newState);
256 172
 
257
-            fireListeners(oldState, this._currentState);
258
-        },
259
-        beforeUpdate(listener) {
260
-
173
+            this[fireListeners](oldState, this._currentState);
261 174
         },
262 175
         onUpdate(listener) {
263 176
             listeners.push(listener);
... ...
@@ -1,8 +1,6 @@
1
-import StateContainer, {LensWrapper, lensTransformer} from '../src/state_container';
1
+import StateContainer, {lensTransformer} from '../src/state_container';
2 2
 import sinon from 'sinon';
3 3
 
4
-import R from 'ramda';
5
-
6 4
 test("initial state setting works", () => {
7 5
    const container = new StateContainer({foo: 'bar'});
8 6