Browse Source

Use atpath, remove cow-value-model.

Herby Vojčík 5 years ago
parent
commit
70a76002b7
5 changed files with 9 additions and 390 deletions
  1. 0 271
      README.md
  2. 0 1
      index.js
  3. 3 0
      package.json
  4. 0 111
      src/cow-value-model.js
  5. 6 7
      src/sac.js

+ 0 - 271
README.md

@@ -129,236 +129,6 @@ composeReducers(
 
 ## Redux helpers
 
-### `deepGetOrNil(key, ...)`
-### `deget(key, ...)`
-
-Creates an accessor function allowing
-to get specified key from any object.
-
-Specify keys by passing a list of keys to `deepGetOrNil`.
-Key can be either:
- - number
- - `keyInMap(obj)`
- - array of Keys
- - anything else, which is `toString()`ed and dot-split.
-
-```js
-const name = deget("name");
-  
-name({name: "Tom"});
-// => "Tom"
-  
-const city = deget("address.city");
-const city2 = deget("address", "city");
-// and other forms, like:
-// const city3 = deget(["address", "city"]);
-// const city4 = deget("address", [[], "city"]);
-// const city5 = deget([[], "address.city"]);
-// etc.
-const object = {address: {city: "New York"}};
-  
-city(object);
-// => "New York"
-city2(object);
-// => "New York"
-  
-city(undefined);
-// => undefined
-city(null);
-// => undefined
-city({});
-// => undefined
-city({address: null});
-// => undefined
-city({address: {}});
-// => undefined
-city({address: {city: null}});
-// => null
-```
-
-If you put a number in a list of keys to use,
-an object will be treated as an array.
-
-If you put a `keyInMap(obj)` in a list of keys to use,
-an object will be treated as a `Map`.
-
-That way you can create eg. `const c = deget("person", 34, "name")`
-to access `obj.person[34].name` with `c(obj)`.
-
-### `deepCopyOnWrite(key, ...)(val)`
-### `decow(key, ...)(val)`
-
-Creates a modifier function allowing
-to "set" specified key to any object
-in an immutable fashion, eg. creating a modified copy
-when actual write happens.
-
-If properties that are to be parents of a sub-value
-are not present, they are created.
-
-Specify keys by passing a list of keys to `deepCopyOnWrite`.
-Key can be either:
- - number
- - `keyInMap(obj)`
- - array of Keys
- - anything else, which is `toString()`ed and dot-split.
-
-In case no change actually happens (same value is set which is
-already present), returns the original object.
-
-```js
-const setName = decow("name");
-  
-setName("Jerry")({name: "Tom"});
-// => {name: "Jerry"}
-  
-const setCity = decow("address.city");
-const setCity2 = decow("address", "city");
-// and other forms, like:
-// const setCity3 = decow(["address", "city"]);
-// const setCity4 = decow("address", [[], "city"]);
-// const setCity5 = decow([[], "address.city"]);
-// etc.
-const object = {address: {city: "New York"}};
-  
-setCity("London")(object);
-// => {address: {city: "London"}}
-setCity2("London")(object);
-// => {address: {city: "London"}}
-object;
-// => {address: {city: "New York"}}
-setCity("New York")(object) === object;
-// => true
-setCity("New York")(object) === object;
-// => true
-  
-const setCityLondon = setCity("London");
-  
-setCityLondon(undefined);
-// => {address: {city: "London"}}
-setCityLondon(null);
-// => {address: {city: "London"}}
-setCityLondon({});
-// => {address: {city: "London"}}
-setCityLondon({address: null});
-// => {address: {city: "London"}}
-setCityLondon({address: {}});
-// => {address: {city: "London"}}
-setCityLondon({address: {city: null}});
-// => {address: {city: "London"}}
-```
-
-If you put a number in a list of keys to use,
-an object will be treated as an array (unlike the default string case,
-where it is treated as an object), so copy wil be created
-using `[...obj]`, not using `{...obj}`.
-
-That way you can create eg. `const c = decow("person", 34, "name")`
-to "set" `obj.person[34].name` with `c(val)(obj)`.
-
-If you put a `keyInMap(key)` in a list of keys to use,
-an object will be treated as a `Map` (unlike the default string case,
-where it is treated as an object), so copy wil be created
-using `new Map(obj)`, not using `{...obj}`.
-
-### `deepCopyOnMap(key, ...)(fn)`
-### `decomap(key, ...)(fn)`
-
-Creates a modifier function allowing
-to "map" value at specified key
-in an immutable fashion, eg. creating a modified copy
-when actual modification happens.
-
-If properties that are to be parents of a sub-value
-are not present, they are _not_ created.
-In other words, if the key to modify does not exist,
-no change happens.
-
-Specify keys by passing a list of keys to `deepCopyOnMap`.
-Key can be either:
- - number
- - `keyInMap(obj)`
- - array of Keys
- - anything else, which is `toString()`ed and dot-split.
-
-In case no change actually happens (same value is set which is
-already present), returns the original object.
-
-```js
-const mapName = decomap("name");
-  
-mapName(x => x.toUpperCase())({name: "Tom"});
-// => {name: "TOM"}
-  
-const mapCity = decomap("address.city");
-const mapCity2 = decomap("address", "city");
-// and other forms, like:
-// const mapCity3 = decomap(["address", "city"]);
-// const mapCity4 = decomap("address", [[], "city"]);
-// const mapCity5 = decomap([[], "address.city"]);
-// etc.
-const object = {address: {city: "New York"}};
-  
-mapCity(x => "London")(object);
-// => {address: {city: "London"}}
-mapCity2(x => "London")(object);
-// => {address: {city: "London"}}
-object;
-// => {address: {city: "New York"}}
-mapCity(x => "New York")(object) === object;
-// => true
-mapCity2(x => "New York")(object) === object;
-// => true
-  
-const mapCityLondon = mapCity(x => "London");
-  
-mapCityLondon(undefined);
-// => undefined
-mapCityLondon(null);
-// => null
-mapCityLondon({});
-// => {}
-mapCityLondon({address: null});
-// => {address: null}
-mapCityLondon({address: {}});
-// => {address: {}}
-mapCityLondon({address: {city: null}});
-// => {address: {city: "London"}}
-```
-
-If you put a number in a list of keys to use,
-an object will be treated as an array (unlike the default string case,
-where it is treated as an object), so copy wil be created
-using `[...obj]`, not using `{...obj}`.
-
-That way you can create eg. `const c = decomap("person", 34, "name")`
-to "set" `obj.person[34].name` with `c(fn)(obj)`.
-
-If you put a `keyInMap(key)` in a list of keys to use,
-an object will be treated as a `Map` (unlike the default string case,
-where it is treated as an object), so copy wil be created
-using `new Map(obj)`, not using `{...obj}`.
-
-### `keyInMap(obj)`
-### `kim(obj)`
-
-Creates "`obj` as an index in a map".
-
-### `cowValueModel(key, ...)`
-
-Deprecated.
-
-Creates an overloaded function allowing
-to set or get specified key from any object.
-
-It gets when one arg passed (works as `deget(key, ...)`),
-sets when two args (obj, val) passed (works as `decow(key, ...)(val)(obj)`),
-with a caveat: setting `undefined` ends up as plain get
-(ES2015 default arguments semantics),
-so props are not created when not present
-if setting `undefined`; other values including `null` are ok
-and create nonpresent props.
-
 ### `typedAction(type, [fn])`
 
 This creates an action creator function that amends existing `fn` by:
@@ -383,44 +153,3 @@ as well as `fooBar` action creator.
 
 
 In case you don't pass the `fn` argument, the action creator only creates `{type}`.
-
-### `cowWorkshop(keys, fn = x => x)(obj, [options])`
-
-This is multipurpose enumerate-and-act function to manipulate objects
-using `deget` and `decow`. The `options` argument can contain these additional fields:
-  - `result` -- where to put elements (`obj` by default),
-  - `resultKeys` -- what keys to use to put into `result` (`keys` by default)
-  - `diff` -- where to put diffing elements (`undefined` by default)
-
-Function enumerates over keys and performs
-"get key from obj, call fn on value, put transformed value into resultKey in result"
-operations over them, using `deget` for getting and `decow` for putting.
-Additionally, if putting actually resulted in change,
-the result key and value is also put into `diff`.
-It then returns `{result, diff}` object.
-
-```js
-cowWorkshop(["a", "b.c"])();
-// does nothing
-// => {result: undefined, diff: undefined}
-  
-cowWorkshop(["a", "b.c"], () => null)();
-// sets a and b.c to null
-// => {result: {a: null, b: {c: null}}, diff: {a: null, b: {c: null}}}
-  
-const data = {a: 'foo', b: {c: null}};
-cowWorkshop(["a", "b.c"], JSON.stringify)(data);
-// changes a and b.c to string representation; change to a is noop
-// => {result: {a: 'foo', b: {c: 'null'}}, diff: {b: {c: 'null'}}}
-  
-const stored = {ay: 'bar', beecee: 'baz', cee: 'quux'};
-const data = {a: 'foo', b: {c: null}};
-cowWorkshop(["a", "b.c"])(data, {result: stored, resultKeys: ["ay", "beecee"]});
-// "copies" a and b.c into `stored` under different keys
-// => {result: {ay: 'foo', beecee: null, cee: 'quux'}, diff: {ay: 'foo', beecee: null}}
-  
-const data = {a: 'foo', b: {c: 'bar'}, c: 'quux'};
-cowWorkshop(["a", "b.c"], () => null)(data);
-// "nulls" a few fields
-// => {result: {a: null, b: {c: null}, c: 'quux'}, diff: {a: null, b: {c: null}}}
-```

+ 0 - 1
index.js

@@ -1,3 +1,2 @@
 export * from './src/sac';
-export * from './src/cow-value-model';
 export * from './src/typed-action';

+ 3 - 0
package.json

@@ -17,5 +17,8 @@
     "compose"
   ],
   "author": "Herby Vojčík <herby@mailbox.sk>",
+  "dependencies": {
+    "atpath": ">=0.1.1"
+  },
   "license": "MIT"
 }

+ 0 - 111
src/cow-value-model.js

@@ -1,111 +0,0 @@
-function copyArrayWith (obj, key, value) {
-    const result = obj == null ? [] : [...obj];
-    result[key] = value;
-    return result;
-}
-
-function copyMapWith (obj, key, value) {
-    const result = obj == null ? new Map() : new Map(obj);
-    result.set(key, value);
-    return result;
-}
-
-function copyObjectWith (obj, key, value) {
-    const result = obj == null ? {} : {...obj};
-    result[key] = value;
-    return result;
-}
-
-const isMapKey = key => typeof key === 'object' && key.isKeyInMap === true;
-
-export const keyInMap = key => ({isKeyInMap: true, key});
-
-export const kim = keyInMap;
-
-const copyWith = (obj, key, value) =>
-    typeof key === 'number' ? copyArrayWith(obj, key, value) :
-        isMapKey(key) ? copyMapWith(obj, key.key, value) :
-            copyObjectWith(obj, key, value);
-
-const constructKeys = function (keyDescriptions) {
-    const keys = [];
-
-    function fillKeys (keyDescriptions) {
-        keyDescriptions.forEach(each => {
-            if (typeof each === 'number' || isMapKey(each)) keys.push(each);
-            else if (Array.isArray(each)) fillKeys(each);
-            else keys.push(...each.toString().split('.'));
-        });
-    }
-
-    fillKeys(keyDescriptions);
-
-    return keys;
-};
-
-const getKey = (x, key) => x == null ? undefined : isMapKey(key) ? x.get(key.key) : x[key];
-
-const getKeyWithCheck = (x, key) =>
-    x == null ? null :
-        isMapKey(key) ? x.has(key.key) ? {value: x.get(key.key)} : null :
-            x.hasOwnProperty(key) ? {value: x[key]} : null;
-
-export const deepGetOrNil = (...keyDescriptions) => {
-    const keys = constructKeys(keyDescriptions);
-    return obj => keys.reduce(getKey, obj);
-};
-
-export const deget = deepGetOrNil;
-
-const chain = (list, stop, step) => (function worker (index) {
-    return index >= list.length ? stop : step(list[index], worker(index + 1));
-})(0);
-
-const copyOnModification = (obj, key, value, modifierFn) => {
-    const modified = modifierFn(value);
-    return value === modified ? obj : copyWith(obj, key, modified);
-};
-
-export const deepCopyOnWrite = (...keyDescriptions) => {
-    const keys = constructKeys(keyDescriptions);
-    return val => chain(keys, () => val, (key, next) => x =>
-        copyOnModification(x, key, getKey(x, key), next)
-    );
-};
-
-export const decow = deepCopyOnWrite;
-
-export const deepCopyOnMap = (...keyDescriptions) => {
-    const keys = constructKeys(keyDescriptions);
-    return fn => chain(keys, fn, (key, next) => x => {
-        const valueWithCheck = getKeyWithCheck(x, key);
-        if (valueWithCheck == null) return x;
-        return copyOnModification(x, key, valueWithCheck.value, next);
-    });
-};
-
-export const decomap = deepCopyOnMap;
-
-export const cowValueModel = (...keyDescriptions) => {
-    const GET_SENTINEL = {};
-
-    return (obj, val = GET_SENTINEL) =>
-        (val === GET_SENTINEL ?
-            deget(...keyDescriptions) :
-            decow(...keyDescriptions)(val))
-        (obj);
-};
-
-export const cowWorkshop = (keys, fn = x => x) => (obj, {result = obj, resultKeys = keys, diff} = {}) => {
-    keys.forEach((key, index) => {
-        const value = fn(deget(key)(obj));
-        if (typeof value === "undefined") return;
-        const modifier = decow(resultKeys[index])(value);
-        const oldResult = result;
-        result = modifier(oldResult);
-        if (result !== oldResult) {
-            diff = modifier(diff);
-        }
-    });
-    return {result, diff};
-};

+ 6 - 7
src/sac.js

@@ -1,16 +1,15 @@
-import {decow, deget} from './cow-value-model';
+import {atPath} from 'atpath';
 
 export const subReducer = (key, reducer, ...otherKeys) => {
-    const selector = deget(key),
-        modifier = decow(key),
-        otherParts = otherKeys.map(deget);
+    const atKey = atPath(key),
+        otherParts = otherKeys.map(atPath);
 
     return (state, action) => {
-        let newSubState = reducer(selector(state), action, ...otherParts.map(eachSelector => eachSelector(state)));
+        const newSubState = reducer(atKey.get(state), action, ...otherParts.map(eachSelector => eachSelector.get(state)));
         if (typeof newSubState === "undefined") {
             throw new Error(`The '${key}' reducer must not return undefined.`);
         }
-        return modifier(newSubState)(state);
+        return atKey.put(newSubState)(state);
     };
 };
 
@@ -18,7 +17,7 @@ export const composeReducers = (...reducers) => (state, action) =>
     reducers.reduce((x, r) => r(x, action), state);
 
 export const subMiddleware = (keyOrSelectorFn, middleware) => {
-    const selector = typeof keyOrSelectorFn === "function" ? keyOrSelectorFn : deget(keyOrSelectorFn);
+    const selector = typeof keyOrSelectorFn === "function" ? keyOrSelectorFn : atPath(keyOrSelectorFn).get;
     return (store, ...rest) => {
         const {getState} = store;
         return middleware({...store, getState: () => selector(getState())}, ...rest);