models: sturdify namespaces

This commit is contained in:
Yoshua Wuyts
2016-05-23 03:32:14 +09:00
parent 0be2337605
commit 24b9be7c8d
4 changed files with 77 additions and 47 deletions
+10
View File
@@ -0,0 +1,10 @@
## `2.0.0`
### breaking changes
- namespaces are now enforced more strictly
- models now only accept a single argument
- the `namespace` key was introduced inside of models (was prior the leading
string in models)
- namespaced models can now only operate within themselves
## `1.0.0`
- first version of choo
+4 -3
View File
@@ -66,9 +66,10 @@ app.model({
} }
}) })
const mainView = (params, state, send) => choo.view` const mainView = (params, state, send) => {
return choo.view`
<main class="app"> <main class="app">
<h1>${state.title}</h1> <h1>${state.input.title}</h1>
<label>Set the title</label> <label>Set the title</label>
<input <input
type="text" type="text"
@@ -76,6 +77,7 @@ const mainView = (params, state, send) => choo.view`
oninput=${(e) => send('input:update', { payload: e.target.value })}> oninput=${(e) => send('input:update', { payload: e.target.value })}>
</main> </main>
` `
}
app.router((route) => [ app.router((route) => [
route('/', mainView) route('/', mainView)
@@ -240,7 +242,6 @@ choo.model({
}) })
``` ```
### Server Sent Events (SSE) ### Server Sent Events (SSE)
[Server Sent Events (SSE)][sse] allow servers to push data to the browser. [Server Sent Events (SSE)][sse] allow servers to push data to the browser.
They're the unidirectional cousin of `websockets` and compliment `HTTP` They're the unidirectional cousin of `websockets` and compliment `HTTP`
+14 -9
View File
@@ -1,26 +1,31 @@
const choo = require('../') const choo = require('../../')
const app = choo() const app = choo()
app.model('title', { app.model({
state: { title: 'my-demo-app' }, namespace: 'input',
state: {
title: 'my demo app'
},
reducers: { reducers: {
'update': (action, state) => ({ title: action.payload }) update: (action, state) => ({ title: action.payload })
}, },
effects: { effects: {
'update': (action, state, send) => (document.title = action.payload) update: (action, state, send) => (document.title = action.payload)
} }
}) })
const mainView = (params, state, send) => choo.view` const mainView = (params, state, send) => {
return choo.view`
<main class="app"> <main class="app">
<h1>${state.title}</h1> <h1>${state.input.title}</h1>
<label>Set the title</label> <label>Set the title</label>
<input <input
type="text" type="text"
placeholder=${state.title} placeholder=${state.input.title}
oninput=${(e) => send('title:update', { payload: e.target.value })}> oninput=${(e) => send('input:update', { payload: e.target.value })}>
</main> </main>
` `
}
app.router((route) => [ app.router((route) => [
route('/', mainView) route('/', mainView)
+34 -20
View File
@@ -11,9 +11,7 @@ module.exports = choo
// A framework for creating sturdy web applications // A framework for creating sturdy web applications
// null -> fn // null -> fn
function choo (opts) { function choo () {
opts = opts || {}
const name = opts.name || 'choo'
const _models = [] const _models = []
var _router = null var _router = null
@@ -40,17 +38,19 @@ function choo (opts) {
} }
// start the application // start the application
// null -> DOMNode // obj -> DOMNode
function start () { function start (opts) {
opts = opts || {}
const name = opts.name || 'choo'
const initialState = {} const initialState = {}
const reducers = {} const reducers = {}
const effects = {} const effects = {}
_models.push(appInit(opts)) _models.push(appInit(opts))
_models.forEach(function (model) { _models.forEach(function (model) {
if (model.state) apply(model.name, model.state, initialState) if (model.state) apply(model.namespace, model.state, initialState)
if (model.reducers) apply(model.name, model.reducers, reducers) if (model.reducers) apply(model.namespace, model.reducers, reducers)
if (model.effects) apply(model.name, model.effects, effects) if (model.effects) apply(model.namespace, model.effects, effects)
}) })
const send = sendAction({ const send = sendAction({
@@ -68,7 +68,7 @@ function choo (opts) {
}) })
const rootId = name + '-root' const rootId = name + '-root'
const tree = _router(send.state().location, send.state(), send) const tree = _router(send.state().app.location, send.state(), send)
tree.setAttribute('id', rootId) tree.setAttribute('id', rootId)
return tree return tree
@@ -77,14 +77,26 @@ function choo (opts) {
var _effects = false var _effects = false
var newState = null var newState = null
if (reducers[action.type]) { if (/:/.test(action.type)) {
const arr = action.type.split(':')
var ns = arr[0]
action.type = arr[1]
}
const nsReducers = ns ? reducers[ns] : reducers
if (nsReducers[action.type]) {
if (ns) {
state[ns] = reducers[ns][action.type](action, state[ns])
newState = state
} else {
newState = xtend(state, reducers[action.type](action, state)) newState = xtend(state, reducers[action.type](action, state))
}
_reducers = true _reducers = true
} }
if (effects[action.type]) { const nsEffects = ns ? effects[ns] : effects
effects[action.type](action, newState || state, send) if (nsEffects) {
newState = newState || state nsEffects[action.type](action, newState || state)
_effects = true _effects = true
} }
@@ -92,13 +104,13 @@ function choo (opts) {
throw new Error('Could not find action ' + action.type) throw new Error('Could not find action ' + action.type)
} }
return newState return newState || state
} }
// update on every change // update on every change
function onchange (action, state) { function onchange (action, state) {
const oldTree = document.querySelector('#' + rootId) const oldTree = document.querySelector('#' + rootId)
const newTree = _router(state.location, state, send) const newTree = _router(state.app.location, state, send)
newTree.setAttribute('id', rootId) newTree.setAttribute('id', rootId)
yo.update(oldTree, newTree) yo.update(oldTree, newTree)
} }
@@ -113,9 +125,7 @@ function choo (opts) {
// create a new model // create a new model
// (str?, obj) -> null // (str?, obj) -> null
function model (name, model) { function model (model) {
if (!model) model = name
if (typeof name === 'string') model.name = name
_models.push(model) _models.push(model)
} }
} }
@@ -124,6 +134,7 @@ function choo (opts) {
// obj -> obj // obj -> obj
function appInit (opts) { function appInit (opts) {
const model = { const model = {
namespace: 'app',
state: { location: document.location.href }, state: { location: document.location.href },
reducers: { location: setLocation }, reducers: { location: setLocation },
subscriptions: [] subscriptions: []
@@ -158,7 +169,10 @@ function appInit (opts) {
// (str, obj, obj) -> null // (str, obj, obj) -> null
function apply (name, source, target) { function apply (name, source, target) {
Object.keys(source).forEach(function (key) { Object.keys(source).forEach(function (key) {
if (name) target[name + ':' + key] = source[key] if (name) {
else target[key] = source[key] if (!target[name]) target[name] = {}
target[name][key] = source[key]
target[name][key].namespace = name
} else target[key] = source[key]
}) })
} }