Comparing version 8.2.1 to 8.3.0
122
index.js
@@ -19,4 +19,9 @@ const mutate = require('xtend/mutable') | ||
useHooks(hooks) | ||
const subscriptionWraps = [] | ||
const initialStateWraps = [] | ||
const reducerWraps = [] | ||
const effectWraps = [] | ||
use(hooks) | ||
var reducersCalled = false | ||
@@ -36,3 +41,3 @@ var effectsCalled = false | ||
start.start = start | ||
start.use = useHooks | ||
start.use = use | ||
return start | ||
@@ -42,3 +47,3 @@ | ||
// obj -> null | ||
function useHooks (hooks) { | ||
function use (hooks) { | ||
assert.equal(typeof hooks, 'object', 'barracks.use: hooks should be an object') | ||
@@ -49,5 +54,9 @@ assert.ok(!hooks.onError || typeof hooks.onError === 'function', 'barracks.use: onError should be undefined or a function') | ||
if (hooks.onStateChange) onStateChangeHooks.push(hooks.onStateChange) | ||
if (hooks.onError) onErrorHooks.push(wrapOnError(hooks.onError)) | ||
if (hooks.onAction) onActionHooks.push(hooks.onAction) | ||
if (hooks.onStateChange) onStateChangeHooks.push(hooks.onStateChange) | ||
if (hooks.wrapSubscriptions) subscriptionWraps.push(hooks.wrapSubscriptions) | ||
if (hooks.wrapInitialState) initialStateWraps.push(hooks.wrapInitialState) | ||
if (hooks.wrapReducers) reducerWraps.push(hooks.wrapReducers) | ||
if (hooks.wrapEffects) effectWraps.push(hooks.wrapEffects) | ||
} | ||
@@ -67,22 +76,28 @@ | ||
assert.equal(typeof opts, 'object', 'barracks.store.state: opts should be an object') | ||
if (opts.state) { | ||
const initialState = {} | ||
const nsState = {} | ||
models.forEach(function (model) { | ||
const ns = model.namespace | ||
const modelState = model.state || {} | ||
if (ns) { | ||
nsState[ns] = {} | ||
apply(ns, modelState, nsState) | ||
nsState[ns] = xtend(nsState[ns], opts.state[ns]) | ||
} else { | ||
apply(model.namespace, modelState, initialState) | ||
} | ||
}) | ||
return xtend(_state, xtend(opts.state, nsState)) | ||
} else if (opts.freeze === false) { | ||
return xtend(_state) | ||
} else { | ||
return Object.freeze(xtend(_state)) | ||
} | ||
const state = opts.state | ||
if (!opts.state && opts.freeze === false) return xtend(_state) | ||
else if (!opts.state) return Object.freeze(xtend(_state)) | ||
assert.equal(typeof state, 'object', 'barracks.store.state: state should be an object') | ||
const nsState = {} | ||
models.forEach(function (model) { | ||
const ns = model.namespace | ||
const modelState = model.state || {} | ||
if (ns) { | ||
nsState[ns] = {} | ||
apply(ns, modelState, nsState) | ||
nsState[ns] = xtend(nsState[ns], state[ns]) | ||
} else { | ||
mutate(nsState, modelState) | ||
} | ||
}) | ||
const tmpState = xtend(_state, xtend(state, nsState)) | ||
const wrappedState = wrapHook(tmpState, initialStateWraps) | ||
return (opts.freeze === false) | ||
? wrappedState | ||
: Object.freeze(wrappedState) | ||
} | ||
@@ -100,13 +115,28 @@ | ||
if (!stateCalled && model.state && opts.state !== false) { | ||
apply(ns, model.state, _state) | ||
const modelState = model.state || {} | ||
if (ns) { | ||
_state[ns] = _state[ns] || {} | ||
apply(ns, modelState, _state) | ||
} else { | ||
mutate(_state, modelState) | ||
} | ||
} | ||
if (!reducersCalled && model.reducers && opts.reducers !== false) { | ||
apply(ns, model.reducers, reducers) | ||
apply(ns, model.reducers, reducers, function (cb) { | ||
return wrapHook(cb, reducerWraps) | ||
}) | ||
} | ||
if (!effectsCalled && model.effects && opts.effects !== false) { | ||
apply(ns, model.effects, effects) | ||
apply(ns, model.effects, effects, function (cb) { | ||
return wrapHook(cb, effectWraps) | ||
}) | ||
} | ||
if (!subsCalled && model.subscriptions && opts.subscriptions !== false) { | ||
apply(ns, model.subscriptions, subscriptions, createSend, function (err) { | ||
applyHook(onErrorHooks, err) | ||
apply(ns, model.subscriptions, subscriptions, function (cb, key) { | ||
const send = createSend('subscription: ' + (ns ? ns + ':' + key : key)) | ||
cb = wrapHook(cb, subscriptionWraps) | ||
cb(send, function (err) { | ||
applyHook(onErrorHooks, err) | ||
}) | ||
return cb | ||
}) | ||
@@ -116,6 +146,12 @@ } | ||
if (!opts.noState) stateCalled = true | ||
// the state wrap is special because we want to operate on the full | ||
// state rather than indvidual chunks, so we apply it outside the loop | ||
if (!stateCalled && opts.state !== false) { | ||
_state = wrapHook(_state, initialStateWraps) | ||
} | ||
if (!opts.noSubscriptions) subsCalled = true | ||
if (!opts.noReducers) reducersCalled = true | ||
if (!opts.noEffects) effectsCalled = true | ||
if (!opts.noSubscriptions) subsCalled = true | ||
if (!opts.noState) stateCalled = true | ||
@@ -220,14 +256,8 @@ if (!onErrorHooks.length) onErrorHooks.push(wrapOnError(defaultOnError)) | ||
// (str, obj, obj, fn?) -> null | ||
function apply (ns, source, target, createSend, done) { | ||
function apply (ns, source, target, wrap) { | ||
if (ns && !target[ns]) target[ns] = {} | ||
Object.keys(source).forEach(function (key) { | ||
if (ns) { | ||
target[ns][key] = source[key] | ||
} else { | ||
target[key] = source[key] | ||
} | ||
if (createSend && done) { | ||
const send = createSend('subscription: ' + (ns ? ns + ':' + key : key)) | ||
source[key](send, done) | ||
} | ||
const cb = wrap ? wrap(source[key], key) : source[key] | ||
if (ns) target[ns][key] = cb | ||
else target[key] = cb | ||
}) | ||
@@ -247,1 +277,11 @@ } | ||
} | ||
// take a apply an array of transforms onto a value. The new value | ||
// must be returned synchronously from the transform | ||
// (any, [fn]) -> any | ||
function wrapHook (value, transforms) { | ||
transforms.forEach(function (transform) { | ||
value = transform(value) | ||
}) | ||
return value | ||
} |
{ | ||
"name": "barracks", | ||
"version": "8.2.1", | ||
"version": "8.3.0", | ||
"description": "Action dispatcher for unidirectional data flows", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
@@ -8,3 +8,3 @@ # barracks | ||
Action dispatcher for unidirectional data flows. Creates tiny models of data | ||
that can be accessed through actions through a small API. | ||
that can be accessed with actions through a small API. | ||
@@ -53,11 +53,16 @@ ## Usage | ||
Register new hooks on the store. Hooks are little plugins that can extend | ||
behavior or perform actions at specific points in the lifecycle. The following | ||
behavior or perform actions at specific points in the life cycle. The following | ||
hooks are possible: | ||
- __onError(err, state, createSend):__ called when an `effect` or | ||
`subscription` emit an error. If no hook is passed, the default hook will | ||
`throw` on each error. | ||
`subscription` emit an error; if no hook is passed, the default hook will | ||
`throw` on each error | ||
- __onAction(data, state, name, caller, createSend):__ called when an `action` | ||
is fired. | ||
is fired | ||
- __onStateChange(data, state, prev, caller, createSend):__ called after a | ||
reducer changes the `state`. | ||
- __wrapSubscriptions(fn):__ wraps a `subscription` to add custom behavior | ||
- __wrapReducers(fn):__ wraps a `reducer` to add custom behavior | ||
- __wrapEffects(fn):__ wraps an `effect` to add custom behavior | ||
- __wrapInitialState(fn):__ mutate the initial `state` to add custom | ||
behavior - useful to mutate the state before starting up | ||
@@ -67,7 +72,7 @@ `createSend()` is a special function that allows the creation of a new named | ||
second argument is a boolean `callOnError` which can be set to `true` to call | ||
the `onError` hook istead of a provided callback. It then returns a | ||
the `onError` hook instead of a provided callback. It then returns a | ||
`send(actionName, data?)` function. | ||
Hooks should be used with care, as they're the most powerful interface into | ||
the state. For application level code it's generally recommended to delegate to | ||
the state. For application level code, it's generally recommended to delegate to | ||
actions inside models using the `send()` call, and only shape the actions | ||
@@ -78,11 +83,11 @@ inside the hooks. | ||
Register a new model on the store. Models are optionally namespaced objects | ||
with an initial `state`, and handlers for dealing with data: | ||
with an initial `state` and handlers for dealing with data: | ||
- __namespace:__ namespace the model so that it cannot access any properties | ||
and handlers in other models | ||
- __state:__ initial values of `state` inside the model | ||
- __reducers:__ synchronous operations that modify state. Triggered by `actions` | ||
- __effects:__ asynchronous operations that don't modify state directly. | ||
Triggered by `actions`, can call `actions` | ||
- __reducers:__ synchronous operations that modify state; triggered by `actions` | ||
- __effects:__ asynchronous operations that don't modify state directly; | ||
triggered by `actions`, can call `actions` | ||
- __subscriptions:__ asynchronous read-only operations that don't modify state | ||
directly. Can call `actions` | ||
directly; can call `actions` | ||
@@ -93,3 +98,3 @@ `state` within handlers is immutable through `Object.freeze()` and thus cannot | ||
For debugging purposes internal references to values can be inspected through a | ||
For debugging purposes, internal references to values can be inspected through a | ||
series of private accessors: | ||
@@ -103,7 +108,7 @@ - `store._subscriptions` | ||
Get the current state from the store. Opts can take the following values: | ||
- __freeze:__ default: true. Set to false to not freeze state in handlers | ||
using `Object.freeze()`. Useful for optimizing performance in production | ||
builds. | ||
- __freeze:__ default: true; set to false to not freeze state in handlers | ||
using `Object.freeze()`; useful for optimizing performance in production | ||
builds | ||
- __state:__ pass in a state object that will be merged with the state returned | ||
from the store. Useful for rendering in Node. | ||
from the store; useful for rendering in Node | ||
@@ -113,13 +118,13 @@ ### send = createSend(name) = store.start(opts) | ||
`createSend()` to get a `send()` function. Opts can take the following values: | ||
- __subscriptions:__ default: true. Set to false to not register | ||
`subscriptions` when starting the application. Useful to delay `init` | ||
functions until the DOM has loaded. | ||
- __effects:__ default: true. Set to `false` to not register `effects` when | ||
starting the application. Useful when only wanting the initial `state` | ||
- __reducers:__ default: true. Set to false to not register `reducers` when | ||
starting the application. Useful when only wanting the initial `state` | ||
- __subscriptions:__ default: true; set to false to not register | ||
`subscriptions` when starting the application; useful to delay `init` | ||
functions until the DOM has loaded | ||
- __effects:__ default: true; set to `false` to not register `effects` when | ||
starting the application; useful when only wanting the initial `state` | ||
- __reducers:__ default: true; set to false to not register `reducers` when | ||
starting the application; useful when only wanting the initial `state` | ||
If the store has disabled any of the handlers (e.g. `{ reducers: false }`), | ||
calling `store.start()` a second time will register the remaining values. This | ||
is a useful if not everything can be started at the same time (e.g. have | ||
is useful if not everything can be started at the same time (e.g. have | ||
`subscriptions` wait for the `DOMContentLoaded` event). | ||
@@ -165,3 +170,3 @@ | ||
### reducers | ||
Reducers are synchronous functions that return a value syncrhonously. No | ||
Reducers are synchronous functions that return a value synchronously. No | ||
eventual values, just values that are relevant for the state. It takes two | ||
@@ -199,5 +204,5 @@ arguments of `data` and `state`. `data` is the data that was emitted, and | ||
`send()`. They never update the state directly, but can instead do thing | ||
asyncrhonously, and then call `send()` again to trigger a `reducer` that can | ||
asynchronously, and then call `send()` again to trigger a `reducer` that can | ||
update the state. `effects` can also trigger other `effects`, making them fully | ||
composable. Generalyy it's recommended to only have `effects` without a | ||
composable. Generally, it's recommended to only have `effects` without a | ||
`namespace` call other `effects`, as to keep namespaced models as isolated as | ||
@@ -208,3 +213,3 @@ possible. | ||
final `done(err)` callback. If the `effect` was called by another `effect` it | ||
will call the callback of the caller. When an error propegates all the way to | ||
will call the callback of the caller. When an error propagates all the way to | ||
the top, the `onError` handler will be called, registered in | ||
@@ -287,7 +292,7 @@ `barracks(handlers)`. If no callback is registered, errors will `throw`. | ||
An action dispatcher gets data from one place to another without tightly | ||
coupling code. The best known use case for this is in the `flux` pattern. Say | ||
coupling code. The best known use case for this is in the `flux` pattern. Say | ||
you want to update a piece of data (for example a user's name), instead of | ||
directly calling the update logic inside the view the action calls a function | ||
directly calling the update logic inside the view, the action calls a function | ||
that updates the user's name for you. Now all the views that need to update a | ||
user's name can call the same action and pass in the relevant data. This | ||
user's name can call the same action and pass in the relevant data. This | ||
pattern tends to make views more robust and easier to maintain. | ||
@@ -298,6 +303,6 @@ | ||
casually throw restrictions at users without having a clear architecture. I | ||
don't like that. `barracks` is a package creates a clear flow of data within an | ||
don't like that. `barracks` is a package that creates a clear flow of data within an | ||
application, concerning itself with state, code separation, and data flow. I | ||
believe that having strong opinions and being transparant in them makes for | ||
architectures than sprinkles of opinions left and right, without a cohesive | ||
believe that having strong opinions and being transparent in them makes for | ||
better architectures than sprinkles of opinions left and right, without a cohesive | ||
story as to _why_. | ||
@@ -308,3 +313,3 @@ | ||
that. This is a package that only concerns itself with data flow, without being | ||
explicitely tied to the DOM. | ||
explicitly tied to the DOM. | ||
@@ -311,0 +316,0 @@ ### This looks like more than five functions! |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
24336
246
337