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">
<h1>${state.title}</h1>
<h1>${state.input.title}</h1>
<label>Set the title</label>
<input
type="text"
@@ -76,6 +77,7 @@ const mainView = (params, state, send) => choo.view`
oninput=${(e) => send('input:update', { payload: e.target.value })}>
</main>
`
}
app.router((route) => [
route('/', mainView)
@@ -240,7 +242,6 @@ choo.model({
})
```
### Server Sent Events (SSE)
[Server Sent Events (SSE)][sse] allow servers to push data to the browser.
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()
app.model('title', {
state: { title: 'my-demo-app' },
app.model({
namespace: 'input',
state: {
title: 'my demo app'
},
reducers: {
'update': (action, state) => ({ title: action.payload })
update: (action, state) => ({ title: action.payload })
},
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">
<h1>${state.title}</h1>
<h1>${state.input.title}</h1>
<label>Set the title</label>
<input
type="text"
placeholder=${state.title}
oninput=${(e) => send('title:update', { payload: e.target.value })}>
placeholder=${state.input.title}
oninput=${(e) => send('input:update', { payload: e.target.value })}>
</main>
`
}
app.router((route) => [
route('/', mainView)
+34 -20
View File
@@ -11,9 +11,7 @@ module.exports = choo
// A framework for creating sturdy web applications
// null -> fn
function choo (opts) {
opts = opts || {}
const name = opts.name || 'choo'
function choo () {
const _models = []
var _router = null
@@ -40,17 +38,19 @@ function choo (opts) {
}
// start the application
// null -> DOMNode
function start () {
// obj -> DOMNode
function start (opts) {
opts = opts || {}
const name = opts.name || 'choo'
const initialState = {}
const reducers = {}
const effects = {}
_models.push(appInit(opts))
_models.forEach(function (model) {
if (model.state) apply(model.name, model.state, initialState)
if (model.reducers) apply(model.name, model.reducers, reducers)
if (model.effects) apply(model.name, model.effects, effects)
if (model.state) apply(model.namespace, model.state, initialState)
if (model.reducers) apply(model.namespace, model.reducers, reducers)
if (model.effects) apply(model.namespace, model.effects, effects)
})
const send = sendAction({
@@ -68,7 +68,7 @@ function choo (opts) {
})
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)
return tree
@@ -77,14 +77,26 @@ function choo (opts) {
var _effects = false
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))
}
_reducers = true
}
if (effects[action.type]) {
effects[action.type](action, newState || state, send)
newState = newState || state
const nsEffects = ns ? effects[ns] : effects
if (nsEffects) {
nsEffects[action.type](action, newState || state)
_effects = true
}
@@ -92,13 +104,13 @@ function choo (opts) {
throw new Error('Could not find action ' + action.type)
}
return newState
return newState || state
}
// update on every change
function onchange (action, state) {
const oldTree = document.querySelector('#' + rootId)
const newTree = _router(state.location, state, send)
const newTree = _router(state.app.location, state, send)
newTree.setAttribute('id', rootId)
yo.update(oldTree, newTree)
}
@@ -113,9 +125,7 @@ function choo (opts) {
// create a new model
// (str?, obj) -> null
function model (name, model) {
if (!model) model = name
if (typeof name === 'string') model.name = name
function model (model) {
_models.push(model)
}
}
@@ -124,6 +134,7 @@ function choo (opts) {
// obj -> obj
function appInit (opts) {
const model = {
namespace: 'app',
state: { location: document.location.href },
reducers: { location: setLocation },
subscriptions: []
@@ -158,7 +169,10 @@ function appInit (opts) {
// (str, obj, obj) -> null
function apply (name, source, target) {
Object.keys(source).forEach(function (key) {
if (name) target[name + ':' + key] = source[key]
else target[key] = source[key]
if (name) {
if (!target[name]) target[name] = {}
target[name][key] = source[key]
target[name][key].namespace = name
} else target[key] = source[key]
})
}