-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
c381856
commit 04b6a2a
Showing
11 changed files
with
633 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import { memoize, makeCallable } from './tools'; | ||
|
||
import { buildReducer, buildCollectionReducers } from './generators/reducers'; | ||
import { COLLECTION, generateActionTypes, generateActionCreators } from './generators/actions'; | ||
|
||
const generateCollectionStore = (entityName, { idKey = 'id', defaultValue = [] } = {}) => { | ||
const actionTypes = generateActionTypes(COLLECTION, entityName); | ||
const actionCreators = generateActionCreators(COLLECTION, entityName); | ||
|
||
const reducer = buildReducer(buildCollectionReducers(actionTypes, idKey), defaultValue); | ||
|
||
return makeCallable(reducer, actionCreators); | ||
}; | ||
export default memoize(generateCollectionStore); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
export const SIMPLE = 'SIMPLE'; | ||
export const COLLECTION = 'COLLECTION'; | ||
export const VERBS = { | ||
[SIMPLE]: { | ||
SET: 'set' | ||
}, | ||
[COLLECTION]: { | ||
SET: 'set', | ||
UPDATE: 'update', | ||
DELETE: 'delete', | ||
MERGE: 'merge', | ||
MERGEDEEP: 'mergeDeep', | ||
DELETEALL: 'deleteAll', | ||
CLEAR: 'clear' | ||
} | ||
}; | ||
|
||
const generateActionType = (storeType, entityName, verb) => | ||
`REDEUCE:${storeType}@@${entityName}@@${verb}`; | ||
export const generateActionTypes = (storeType, entityId) => | ||
Object.keys(VERBS[storeType]).reduce( | ||
(actionTypes, verb) => ({ | ||
...actionTypes, | ||
[verb]: generateActionType(storeType, entityId, verb) | ||
}), | ||
{} | ||
); | ||
|
||
const generateActionCreator = type => payload => ({ type, payload }); | ||
export const generateActionCreators = (storeType, entityId) => { | ||
const actionTypes = generateActionTypes(storeType, entityId); | ||
return Object.keys(VERBS[storeType]).reduce( | ||
(actionCreators, verb) => ({ | ||
...actionCreators, | ||
[VERBS[storeType][verb]]: generateActionCreator(actionTypes[verb]) | ||
}), | ||
{} | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
const sortByKeyName = keyName => (a, b) => a[keyName] > b[keyName]; | ||
const getKeyNameValues = (entities, keyName) => | ||
entities.map(({ [keyName]: id }) => id); | ||
|
||
const filterByKeyname = (entities, keyName) => | ||
entities.filter(({ [keyName]: id }) => id !== undefined); | ||
|
||
const filterWithoutIndexes = (entities, indexes, keyName) => | ||
entities.filter(({ [keyName]: id }) => indexes.indexOf(id) === -1); | ||
|
||
const filterByIndexes = (entities, indexes, keyName) => | ||
entities.filter(({ [keyName]: id }) => indexes.indexOf(id) > -1); | ||
|
||
const findOneByIndex = (entities, keyName, uniqueId) => | ||
entities.find(({ [keyName]: id }) => id === uniqueId); | ||
|
||
export const buildReducer = (reducers, defaultValue) => ( | ||
state = defaultValue, | ||
{ type, payload } | ||
) => (reducers[type] ? reducers[type](state, payload) : state); | ||
|
||
const asArray = o => [].concat(o); | ||
|
||
export const buildCollectionReducers = (actionTypes, keyName) => { | ||
const { | ||
SET, | ||
UPDATE, | ||
DELETE, | ||
MERGE, | ||
MERGEDEEP, | ||
DELETEALL, | ||
CLEAR | ||
} = actionTypes; | ||
|
||
// Reducer to SET a list of objects in the store | ||
const setReducer = (state, payload) => { | ||
const filteredPayload = filterByKeyname(asArray(payload), keyName); | ||
const setIndexes = getKeyNameValues(filteredPayload, keyName); | ||
|
||
return [ | ||
// the list of objects from the previous state that are not to be replaced | ||
...filterWithoutIndexes(state, setIndexes, keyName), | ||
// the list of new objects | ||
...filteredPayload | ||
].sort(sortByKeyName(keyName)); | ||
}; | ||
|
||
// Reducer to UPDATE a list of objects in the store | ||
const updateReducer = (state, payload) => { | ||
const filteredPayload = filterByKeyname(asArray(payload), keyName); | ||
const updateIndexes = getKeyNameValues(filteredPayload, keyName); | ||
const existingIndexes = getKeyNameValues(state, keyName); | ||
|
||
return [ | ||
// the list of objects from the previous state that are not to be updated | ||
...filterWithoutIndexes(state, updateIndexes, keyName), | ||
// the list of objects from the previous state that have to be updated | ||
// mapped to be merged with the new version | ||
...filterByIndexes(state, updateIndexes, keyName).map(obj => ({ | ||
...obj, | ||
...findOneByIndex(filteredPayload, keyName, obj[keyName]) | ||
})), | ||
// the list new objects that are not existing in the previous state | ||
...filterWithoutIndexes(filteredPayload, existingIndexes, keyName) | ||
].sort(sortByKeyName(keyName)); | ||
}; | ||
|
||
// Reducer to DELETE a list of objects in the store | ||
const deleteReducer = (state, payload) => { | ||
const filteredPayload = filterByKeyname(asArray(payload), keyName); | ||
const setIndexes = getKeyNameValues(filteredPayload, keyName); | ||
|
||
return [ | ||
// the list of objects from the previous state that are not to be deleted | ||
...filterWithoutIndexes(state, setIndexes, keyName) | ||
].sort(sortByKeyName(keyName)); | ||
}; | ||
|
||
const clearReducer = () => []; | ||
|
||
return { | ||
[SET]: setReducer, | ||
[UPDATE]: updateReducer, | ||
[DELETE]: deleteReducer, | ||
[MERGE]: setReducer, | ||
[MERGEDEEP]: updateReducer, | ||
[DELETEALL]: deleteReducer, | ||
[CLEAR]: clearReducer | ||
}; | ||
}; | ||
|
||
export const buildSimpleReducer = actionTypes => { | ||
const { SET } = actionTypes; | ||
|
||
// Reducer to SET a list of objects in the store | ||
const setReducer = (_, payload) => payload; | ||
|
||
return { | ||
[SET]: setReducer | ||
}; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export { default as generateCollectionStore } from './collectionStore'; | ||
export { default as generateSimpleStore } from './simpleStore'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import { memoize, makeCallable } from './tools'; | ||
|
||
import { buildReducer, buildSimpleReducer } from './generators/reducers'; | ||
import { SIMPLE, generateActionTypes, generateActionCreators } from './generators/actions'; | ||
|
||
const generateSimpleStore = (entityName, { defaultValue = undefined } = {}) => { | ||
const actionTypes = generateActionTypes(SIMPLE, entityName); | ||
const actionCreators = generateActionCreators(SIMPLE, entityName); | ||
|
||
const reducer = buildReducer(buildSimpleReducer(actionTypes), defaultValue); | ||
|
||
return makeCallable(reducer, actionCreators); | ||
}; | ||
export default memoize(generateSimpleStore); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export { default as makeCallable } from './makeCallable'; | ||
export { default as memoize } from './memoize'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
export default (reducer, actionCreators) => { | ||
const fn = () => ({ reducer, ...actionCreators }); | ||
fn.getReducer = () => reducer; | ||
fn.getActionCreators = () => actionCreators; | ||
|
||
return fn; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
export default function memoize(fn) { | ||
memoize.cache = {}; | ||
return (...args) => { | ||
const key = JSON.stringify(args[0]); | ||
if (memoize.cache[key]) { | ||
if (JSON.stringify(args) !== JSON.stringify(memoize.cache[key].args)) { | ||
throw new Error('boo'); | ||
} | ||
return memoize.cache[key].val; | ||
} else { | ||
const val = fn(...args); | ||
memoize.cache[key] = { val, args }; | ||
return val; | ||
} | ||
}; | ||
} |
Oops, something went wrong.