2 Commits 4b085ade7d ... 7e9c188905

Author SHA1 Message Date
  Herby Vojčík 7e9c188905 0.6.1 6 years ago
  Herby Vojčík a9fbd6f229 Add deget, decow; deprecate cowValueModel. 6 years ago
3 changed files with 125 additions and 50 deletions
  1. 89 40
      README.md
  2. 1 1
      package.json
  3. 35 9
      src/cow-value-model.js

+ 89 - 40
README.md

@@ -129,41 +129,30 @@ composeReducers(
 
 
 ## Redux helpers
 ## Redux helpers
 
 
-### `cowValueModel(key, ...)`
-
-Creates an overloaded function allowing
-to set or get specified key from any object.
+### `deepGetOrNil(key, ...)`
+### `deget(key, ...)`
 
 
-It gets when one arg passed, sets when two args passed.
-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.
+Creates an accessor function allowing
+to get specified key from any object.
 
 
-Specify keys by passing a list of keys to `cowValueModel`.
+Specify keys by passing a list of keys to `deepGetOrNil`.
 Key can be either:
 Key can be either:
  - number
  - number
  - array of Keys
  - array of Keys
  - anything else, which is `toString()`ed and dot-split.
  - anything else, which is `toString()`ed and dot-split.
 
 
-Set-usage (two-arg) returns a copy with specified (sub-)property changed;
-in case no change actually happens, returns the original object.
-
 ```js
 ```js
-const name = cowValueModel("name");
+const name = deget("name");
   
   
 name({name: "Tom"});
 name({name: "Tom"});
 // => "Tom"
 // => "Tom"
-name({name: "Tom"}, "Jerry");
-// => {name: "Jerry"}
   
   
-const city = cowValueModel("address.city");
-const city2 = cowValueModel("address", "city");
+const city = deget("address.city");
+const city2 = deget("address", "city");
 // and other forms, like:
 // and other forms, like:
-// const city3 = cowValueModel(["address", "city"]);
-// const city4 = cowValueModel("address", [[], "city"]);
-// const city5 = cowValueModel([[], "address.city"]);
+// const city3 = deget(["address", "city"]);
+// const city4 = deget("address", [[], "city"]);
+// const city5 = deget([[], "address.city"]);
 // etc.
 // etc.
 const object = {address: {city: "New York"}};
 const object = {address: {city: "New York"}};
   
   
@@ -171,16 +160,6 @@ city(object);
 // => "New York"
 // => "New York"
 city2(object);
 city2(object);
 // => "New York"
 // => "New York"
-city(object, "London");
-// => {address: {city: "London"}}
-city2(object, "London");
-// => {address: {city: "London"}}
-object;
-// => {address: {city: "New York"}}
-city(object, "New York") === object;
-// => true
-city2(object, "New York") === object;
-// => true
   
   
 city(undefined);
 city(undefined);
 // => undefined
 // => undefined
@@ -194,18 +173,73 @@ city({address: {}});
 // => undefined
 // => undefined
 city({address: {city: null}});
 city({address: {city: null}});
 // => null
 // => null
+```
+
+If you put a number in a list of keys to use,
+an object will be treated as an array.
+
+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
+ - 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
   
   
-city(undefined, "London");
+const setCityLondon = setCity("London");
+  
+setCityLondon(undefined);
 // => {address: {city: "London"}}
 // => {address: {city: "London"}}
-city(null, "London");
+setCityLondon(null);
 // => {address: {city: "London"}}
 // => {address: {city: "London"}}
-city({}, "London");
+setCityLondon({});
 // => {address: {city: "London"}}
 // => {address: {city: "London"}}
-city({address: null}, "London");
+setCityLondon({address: null});
 // => {address: {city: "London"}}
 // => {address: {city: "London"}}
-city({address: {}}, "London");
+setCityLondon({address: {}});
 // => {address: {city: "London"}}
 // => {address: {city: "London"}}
-city({address: {city: null}}, "London");
+setCityLondon({address: {city: null}});
 // => {address: {city: "London"}}
 // => {address: {city: "London"}}
 ```
 ```
 
 
@@ -214,8 +248,23 @@ 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
 where it is treated as an object), so copy wil be created
 using `[...obj]`, not using `{...obj}`.
 using `[...obj]`, not using `{...obj}`.
 
 
-That way you can create eg. `const c = cowValueObject("person", 34, "name")`
-to access `obj.person[34].name` with `c(obj)` / `c(obj, val)`.
+That way you can create eg. `const c = decow("person", 34, "name")`
+to "set" `obj.person[34].name` with `c(val)(obj)`.
+
+### `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])`
 ### `typedAction(type, [fn])`
 
 

+ 1 - 1
package.json

@@ -1,6 +1,6 @@
 {
 {
   "name": "redux-sac",
   "name": "redux-sac",
-  "version": "0.6.0",
+  "version": "0.6.1",
   "description": "Slice and compose redux-type reducers.",
   "description": "Slice and compose redux-type reducers.",
   "main": "index.js",
   "main": "index.js",
   "scripts": {
   "scripts": {

+ 35 - 9
src/cow-value-model.js

@@ -4,7 +4,7 @@ function copyWith (obj, key, value) {
     return result;
     return result;
 }
 }
 
 
-export const cowValueModel = (...keyDescriptions) => {
+const constructKeys = function (keyDescriptions) {
     const keys = [];
     const keys = [];
 
 
     function fillKeys (keyDescriptions) {
     function fillKeys (keyDescriptions) {
@@ -17,31 +17,57 @@ export const cowValueModel = (...keyDescriptions) => {
 
 
     fillKeys(keyDescriptions);
     fillKeys(keyDescriptions);
 
 
-    function setField (x, index, val) {
+    return keys;
+};
+
+const deepGet = (keys, obj) => keys.reduce((x, key) => x == null ? undefined : x[key], obj);
+
+const deepPut = (keys, obj, val) => {
+    function setVal (x, index) {
         if (index >= keys.length) return val;
         if (index >= keys.length) return val;
         const key = keys[index],
         const key = keys[index],
             value = x == null ? undefined : x[key],
             value = x == null ? undefined : x[key],
-            modified = setField(value, index + 1, val);
+            modified = setVal(value, index + 1);
         return value === modified ? x : copyWith(x, key, modified);
         return value === modified ? x : copyWith(x, key, modified);
     }
     }
 
 
+    return setVal(obj, 0);
+};
+
+export const deepGetOrNil = (...keyDescriptions) => {
+    const keys = constructKeys(keyDescriptions);
+    return obj => deepGet(keys, obj);
+};
+
+export const deget = deepGetOrNil;
+
+export const deepCopyOnWrite = (...keyDescriptions) => {
+    const keys = constructKeys(keyDescriptions);
+    return val => obj => deepPut(keys, obj, val);
+};
+
+export const decow = deepCopyOnWrite;
+
+export const cowValueModel = (...keyDescriptions) => {
+    const keys = constructKeys(keyDescriptions);
+
     const GET_SENTINEL = {};
     const GET_SENTINEL = {};
 
 
     return (obj, val = GET_SENTINEL) =>
     return (obj, val = GET_SENTINEL) =>
         val === GET_SENTINEL ?
         val === GET_SENTINEL ?
-            keys.reduce((x, key) => x == null ? undefined : x[key], obj) :
-            setField(obj, 0, val);
+            deepGet(keys, obj) :
+            deepPut(keys, obj, val);
 };
 };
 
 
 export const cowWorkshop = (keys, fn = x => x) => (obj, {result = obj, resultKeys = keys, diff} = {}) => {
 export const cowWorkshop = (keys, fn = x => x) => (obj, {result = obj, resultKeys = keys, diff} = {}) => {
     keys.forEach((key, index) => {
     keys.forEach((key, index) => {
-        const value = fn(cowValueModel(key)(obj));
+        const value = fn(deget(key)(obj));
         if (typeof value === "undefined") return;
         if (typeof value === "undefined") return;
-        const modifier = cowValueModel(resultKeys[index]);
+        const modifier = decow(resultKeys[index])(value);
         const oldResult = result;
         const oldResult = result;
-        result = modifier(oldResult, value);
+        result = modifier(oldResult);
         if (result !== oldResult) {
         if (result !== oldResult) {
-            diff = modifier(diff, value);
+            diff = modifier(diff);
         }
         }
     });
     });
     return {result, diff};
     return {result, diff};