Explorar el Código

Rename to atpath, only keep atpath code.

Herby Vojčík hace 6 años
padre
commit
d1b2288029
Se han modificado 6 ficheros con 110 adiciones y 327 borrados
  1. 2 169
      README.md
  2. 101 3
      index.js
  3. 7 8
      package.json
  4. 0 111
      src/cow-value-model.js
  5. 0 29
      src/sac.js
  6. 0 7
      src/typed-action.js

+ 2 - 169
README.md

@@ -1,133 +1,6 @@
-# redux-sac
+# atpath
 
-## Slice redux objects (reducers, middleware, ...)
-
-### `subReducer(key, reducer, additionalKey, ...)`
-
-Creates a wrapper reducer that calls `reducer`
-on the sub-state specified by `key`.
-Key can go in different formats, see `deepGetOrNil` below.
-Rest of the state is left untouched.
-
-```js
-const r = subReducer("persons", personReducer);
-  
-r({persons: ["John", "Jill"], cars: ["Honda"]}, action);
-// => {
-//   persons: personReducer(["John", "Jill"], action), 
-//   cars: ["Honda"]
-// }
-```
-
-Respects redux convention that if no change was made,
-the identical object should be returned. So in previous case,
-if `personReducer` would return the identical array,
-`r` would return the state object it was passed in.
-
-If persons were deeper in hierarchy, it could have been created as
-`const r = subReducer("files.persons", personReducer);` for example.
-
-You may pass additional keys
-as addition arguments. In that case, additional parts of state
-will be fetched and passed to a sub-reducer:
-
-```js
-const r = subReducer("persons", personReducer, "assets.cars");
-  
-r({persons: ["John", "Jill"], assets: {cars: ["Honda"]}}, action);
-// => {
-//   persons: personReducer(["John", "Jill"], action, ["Honda"]), 
-//   assets: {cars: ["Honda"]}
-// }
-```
-
-This technique is mentioned in Redux docs, in "Beyond combineReducers" page.
-
-### `subMiddleware(key | selectorFn, middleware)`
-
-Creates a wrapper middleware
-on the sub-state specified by `key`
-or the selector function `selectorFn`
-by decorating `getState` part of the passed arg.
-The `key` can have different format, see `deepGetOrNil` below.
-Rest of the passed arg is left untouched.
-
-```js
-const r = subMiddleware("persons", ({getState}) => next => action => {
-  console.log(JSON.stringify(getState()));
-  return next(action);
-});
-  
-const altR = subMiddleware(state => state.persons, ({getState}) => next => action => {
-  console.log(JSON.stringify(getState()));
-  return next(action);
-});
-  
-// use r or altR in creation of store
-  
-store.getState();
-// => {persons: ["John", "Jill"], cars: ["Honda"]}
-store.dispatch({type: "any"});
-// console => ["John","Jill"] 
-```
-
-If persons were deeper in hierarchy, it could have been created as
-`const r = subMiddleware("files.persons", ...);` for example.
-
-You can use `subMiddleware` to sub-state anything
-getting one parameter in which
-one of the properties passed is `getState` function;
-it is not special-case for middleware.
-For example, it is usable for wrapping `redux-effex` effect function.
-
-### `subEffex(key | selectorFn, effects)`
-
-Creates a wrapper around each element
-of the effects array
-on the sub-state specified by `key`
-or by selector function `selectorFn`
-by decorating `effect` function with `subMiddleware`
-and returns the array of the wrapped effects.
-
-```js
-const effects = [{
-  action: "foo",
-  effect: ({getState}) => {
-    console.log(JSON.stringify(getState()));
-  }
-}];
-  
-const e = subEffex("persons", effects);
-const altE = subEffex(state => state.persons, effects);
-  
-// use e or altE in creation of store
-  
-store.getState();
-// => {persons: ["John", "Jill"], cars: ["Honda"]}
-store.dispatch({type: "foo"});
-// console => ["John","Jill"] 
-```
-
-## Compose
-
-### `composeReducers(reducer1, reducer2, ...)`
-
-Creates a wrapper reducer that calls passed reducers
-one after another, passing intermediate states.
-and returning the result of the last one.
-
-Useful to "concatenate" a few `subReducer`s. like:
-
-```js
-composeReducers(
-  subReducer("files.persons", personReducer, "assets.swag"),
-  subReducer("files.clients", clientReducer, "news"),
-  subReducer("assets", assetReducer),
-  baseReducer
-)
-```
-
-## Redux helpers
+Access and copy-on-modify JavaScript objects, including maps, using deep paths.
 
 ### `deepGetOrNil(key, ...)`
 ### `deget(key, ...)`
@@ -344,46 +217,6 @@ using `new Map(obj)`, not using `{...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:
-  - adding a `type` property to the result, as well as
-  - adding a `TYPE` property to action creator itself.
-
-So for example:
-
-```js
-export const answerQuestion =
-  typedAction("answer question", (index, answer) => ({
-    payload: {index, answer}
-  }));
-```
-
-allows you to call `answerQuestion(2, "Albert Einstein")` to create
-`{type: "answer question", payload: {index: 2, answer: "Albert Einstein"}}`;
-but it also allows you to use `case answerQuestion.TYPE:` in your reducer,
-since `answerQuestion.TYPE === "answer question"`.
-IOW, this removes the two-space problem of having `const FOO_BAR_TYPE`
-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

+ 101 - 3
index.js

@@ -1,3 +1,101 @@
-export * from './src/sac';
-export * from './src/cow-value-model';
-export * from './src/typed-action';
+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 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};
+};

+ 7 - 8
package.json

@@ -1,20 +1,19 @@
 {
-  "name": "redux-sac",
-  "version": "0.6.7",
-  "description": "Slice and compose redux-type reducers.",
+  "name": "atpath",
+  "version": "0.0.1",
+  "description": "Access and copy-on-modify JavaScript objects, including maps, using deep paths.",
   "main": "index.js",
   "scripts": {
-    "build": "babel src --presets es2015,stage-2 --out-dir lib",
     "test": "echo \"Error: no test specified\" && exit 1"
   },
   "repository": {
     "type": "git",
-    "url": "https://lolg.it/herby/redux-sac.git"
+    "url": "https://lolg.it/herby/atpath.git"
   },
   "keywords": [
-    "redux",
-    "slice",
-    "compose"
+    "immutable",
+    "deep",
+    "copy on write"
   ],
   "author": "Herby Vojčík <herby@mailbox.sk>",
   "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};
-};

+ 0 - 29
src/sac.js

@@ -1,29 +0,0 @@
-import {decow, deget} from './cow-value-model';
-
-export const subReducer = (key, reducer, ...otherKeys) => {
-    const selector = deget(key),
-        modifier = decow(key),
-        otherParts = otherKeys.map(deget);
-
-    return (state, action) => {
-        let newSubState = reducer(selector(state), action, ...otherParts.map(eachSelector => eachSelector(state)));
-        if (typeof newSubState === "undefined") {
-            throw new Error(`The '${key}' reducer must not return undefined.`);
-        }
-        return modifier(newSubState)(state);
-    };
-};
-
-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);
-    return (store, ...rest) => {
-        const {getState} = store;
-        return middleware({...store, getState: () => selector(getState())}, ...rest);
-    };
-};
-
-export const subEffex = (keyOrSelectorFn, effects) =>
-    effects.map(each => ({...each, effect: subMiddleware(keyOrSelectorFn, each.effect)}));

+ 0 - 7
src/typed-action.js

@@ -1,7 +0,0 @@
-export const typedAction = (type, fn) => {
-    const result = fn ?
-        (...args) => ({...fn(...args), type}) :
-        () => ({type});
-    result.TYPE = type;
-    return result;
-};