Skip to content

Commit

Permalink
feat(Computed): simplify use of computed without props
Browse files Browse the repository at this point in the history
The Computed returns an instance that uses no props. To add props use "computed.props({foo: 'bar'})"

BREAKING CHANGE:

Use "myComputed" instead of "myComputed()"
  • Loading branch information
gaspard authored and Guria committed Oct 29, 2016
1 parent 8a45aa9 commit 9ad3e96
Show file tree
Hide file tree
Showing 12 changed files with 61 additions and 76 deletions.
2 changes: 1 addition & 1 deletion packages/cerebral-demo/src/components/UpperTitle/index.js
Expand Up @@ -3,7 +3,7 @@ import {connect} from 'cerebral/react'
import upperCaseTitleComputed from '../../computeds/upperCaseTitle'

export default connect({
title: upperCaseTitleComputed()
title: upperCaseTitleComputed
},
function UpperTitle (props) {
return (
Expand Down
2 changes: 1 addition & 1 deletion packages/cerebral-todomvc/src/components/App/index.js
Expand Up @@ -8,7 +8,7 @@ import visibleTodosRefs from '../../computed/visibleTodosRefs'
export default connect({
todos: 'app.todos',
isSaving: 'app.isSaving',
visibleTodosRefs: visibleTodosRefs()
visibleTodosRefs: visibleTodosRefs
},
function App (props) {
return (
Expand Down
2 changes: 1 addition & 1 deletion packages/cerebral-todomvc/src/components/Footer/index.js
Expand Up @@ -5,7 +5,7 @@ import cn from 'classnames'

export default connect({
filter: 'app.filter',
counts: counts()
counts: counts
}, {
filterClicked: 'app.filterClicked',
clearCompletedClicked: 'app.clearCompletedClicked'
Expand Down
4 changes: 2 additions & 2 deletions packages/cerebral-todomvc/src/components/List/index.js
Expand Up @@ -6,8 +6,8 @@ import isAllChecked from '../../computed/isAllChecked'
import visibleTodosRefs from '../../computed/visibleTodosRefs'

export default connect({
isAllChecked: isAllChecked(),
todoRefs: visibleTodosRefs()
isAllChecked: isAllChecked,
todoRefs: visibleTodosRefs
}, {
toggleAllChanged: 'app.toggleAllChanged'
},
Expand Down
2 changes: 1 addition & 1 deletion packages/cerebral-todomvc/src/computed/isAllChecked.js
Expand Up @@ -2,7 +2,7 @@ import {Computed} from 'cerebral'
import visibleTodosRefs from './visibleTodosRefs'

export default Computed({
visibleTodosRefs: visibleTodosRefs(),
visibleTodosRefs: visibleTodosRefs,
todos: 'app.todos.**'
}, props => {
return props.visibleTodosRefs.filter((ref) => {
Expand Down
Expand Up @@ -2,8 +2,8 @@ import isAllChecked from '../../../computed/isAllChecked'
import visibleTodosRefs from '../../../computed/visibleTodosRefs'

function toggleAllChecked ({state}) {
const isCompleted = !state.compute(isAllChecked())
const currentTodosKeys = state.compute(visibleTodosRefs())
const isCompleted = !state.compute(isAllChecked)
const currentTodosKeys = state.compute(visibleTodosRefs)

currentTodosKeys.forEach((ref) => {
state.set(`app.todos.${ref}.completed`, isCompleted)
Expand Down
2 changes: 1 addition & 1 deletion packages/cerebral/README.md
Expand Up @@ -81,7 +81,7 @@ In component:
import someComputed from './someComputed'

export default connect({
myComputed: someComputed()
myComputed: someComputed
})
```

Expand Down
22 changes: 13 additions & 9 deletions packages/cerebral/src/Computed.js
Expand Up @@ -16,10 +16,7 @@ export const dependencyStore = new DependencyStore()
*/
export class Computed {
constructor (props, paths, func, depsMap, factory) {
if (!isObject(props)) {
throwError('You are not passing valid props to a computed')
}
this.props = props
this.setProps = props
this.func = func
this.value = null
this.paths = paths
Expand All @@ -30,6 +27,13 @@ export class Computed {

dependencyStore.addEntity(this, this.depsMap)
}
/*
Uses the factory to return a computed for the given props. This returns
the same computed given the same props (cache in factory).
*/
props (...args) {
return this.factory.create(...args)
}
/*
This method is called by the controller when a flush happens and
the dependency store returns affected computeds
Expand All @@ -45,7 +49,7 @@ export class Computed {
if (this.isDirty) {
const computedProps = Object.assign(
{},
this.props,
this.setProps,
Object.keys(this.paths).reduce((currentProps, depsMapKey) => {
currentProps[depsMapKey] = (
this.paths[depsMapKey] instanceof Computed
Expand Down Expand Up @@ -98,16 +102,16 @@ class ComputedFactory {
this.func = func
this.cache = []

const create = this.create.bind(this)
create.cache = this.cache

return create
return this.create()
}
/*
This is what runs when you create an instance of a computed, passing
any optional props. It checks the cache or creates a new computed
*/
create (props = {}) {
if (!isObject(props)) {
throwError('You are not passing valid props to a computed')
}
for (let x = 0; x < this.cache.length; x++) {
if (!propsDiffer(props, this.cache[x].props)) {
return this.cache[x]
Expand Down
75 changes: 31 additions & 44 deletions packages/cerebral/src/Computed.test.js
Expand Up @@ -6,7 +6,7 @@ import assert from 'assert'
describe('Computed', () => {
describe('factory', () => {
it('should create a computed factory', () => {
const computedFactory = Computed({}, () => {})
const computedFactory = Computed({}, () => {}).factory.create
assert.ok(typeof computedFactory === 'function')
})
it('should throw when not passing correct arguments', () => {
Expand All @@ -15,45 +15,40 @@ describe('Computed', () => {
})
})
it('should cache computed', () => {
const computedFactory = Computed({}, () => {})
computedFactory()
assert.equal(computedFactory.cache.length, 1)
computedFactory()
assert.equal(computedFactory.cache.length, 1)
const computed = Computed({}, () => {})
assert.equal(computed.factory.cache.length, 1)
computed.props()
assert.equal(computed.factory.cache.length, 1)
})
it('should not use cache when props differ', () => {
const computedFactory = Computed({}, () => {})
computedFactory()
assert.equal(computedFactory.cache.length, 1)
computedFactory({foo: 'bar'})
assert.equal(computedFactory.cache.length, 2)
const computed = Computed({}, () => {})
assert.equal(computed.factory.cache.length, 1)
computed.props({foo: 'bar'})
assert.equal(computed.factory.cache.length, 2)
})
it('should remove from cache when computed is removed', () => {
const computedFactory = Computed({}, () => {})
const computed = computedFactory()
assert.equal(computedFactory.cache.length, 1)
const computed = Computed({}, () => {})
assert.equal(computed.factory.cache.length, 1)
computed.remove()
assert.equal(computedFactory.cache.length, 0)
assert.equal(computed.factory.cache.length, 0)
})
})
describe('instance', () => {
it('should create a computed', () => {
const computedFactory = Computed({}, () => {})
const computed = computedFactory()
const computed = Computed({}, () => {})
assert.ok(typeof computed.getValue === 'function')
})
it('should throw when passing wrong argument', () => {
const computedFactory = Computed({}, () => {})
const computed = Computed({}, () => {})
assert.throws(() => {
computedFactory([])
computed.props([])
})
})
it('should return value', () => {
const model = Controller({}).model
const computedFactory = Computed({}, () => {
const computed = Computed({}, () => {
return 'foo'
})
const computed = computedFactory()
assert.equal(computed.getValue(model), 'foo')
})
it('should extract state', () => {
Expand All @@ -62,12 +57,11 @@ describe('Computed', () => {
foo: 'bar'
}
}).model
const computedFactory = Computed({
const computed = Computed({
foo: 'foo'
}, ({foo}) => {
return foo
})
const computed = computedFactory()
assert.equal(computed.getValue(model), 'bar')
})
it('should cache values', () => {
Expand All @@ -76,14 +70,13 @@ describe('Computed', () => {
foo: 'bar'
}
}).model
const computedFactory = Computed({
const computed = Computed({
foo: 'foo'
}, ({foo}) => {
return {
foo
}
})
const computed = computedFactory()
const value = computed.getValue(model)
assert.deepEqual(value, {foo: 'bar'})
assert.equal(computed.getValue(model), value)
Expand All @@ -95,14 +88,13 @@ describe('Computed', () => {
}
})
const model = controller.model
const computedFactory = Computed({
const computed = Computed({
foo: 'foo'
}, ({foo}) => {
return {
foo
}
})
const computed = computedFactory()
assert.deepEqual(computed.getValue(model), {foo: 'bar'})
assert.deepEqual(computed.getValue(model), {foo: 'bar'})
model.set(['foo'], 'bar2')
Expand All @@ -115,19 +107,18 @@ describe('Computed', () => {
foo: 'bar'
}
}).model
const computedFactoryA = Computed({
const computedA = Computed({
foo: 'foo'
}, ({foo}) => {
return foo
})
const computedFactoryB = Computed({
const computedB = Computed({
foo: 'foo',
foo2: computedFactoryA()
foo2: computedA
}, ({foo, foo2}) => {
return foo + foo2
})
const computed = computedFactoryB()
assert.equal(computed.getValue(model), 'barbar')
assert.equal(computedB.getValue(model), 'barbar')
})
it('should bust cache when nested computed updates', () => {
const controller = Controller({
Expand All @@ -137,22 +128,21 @@ describe('Computed', () => {
}
})
const model = controller.model
const computedFactoryA = Computed({
const computedA = Computed({
foo: 'foo'
}, ({foo}) => {
return foo
})
const computedFactoryB = Computed({
foo: computedFactoryA(),
const computedB = Computed({
foo: computedA,
bar: 'bar'
}, ({foo, bar}) => {
return foo + bar
})
const computed = computedFactoryB()
assert.equal(computed.getValue(model), 'barfoo')
assert.equal(computedB.getValue(model), 'barfoo')
model.set(['foo'], 'bar2')
controller.flush()
assert.equal(computed.getValue(model), 'bar2foo')
assert.equal(computedB.getValue(model), 'bar2foo')
})
it('should handle strict path updates', () => {
const controller = Controller({
Expand All @@ -164,18 +154,16 @@ describe('Computed', () => {
}
})
const model = controller.model
const computedFactoryA = Computed({
const computedA = Computed({
foo: 'foo'
}, ({foo}) => {
return foo.bar
})
const computedFactoryB = Computed({
const computedB = Computed({
foo: 'foo.*'
}, ({foo}) => {
return foo.bar
})
const computedA = computedFactoryA()
const computedB = computedFactoryB()
assert.equal(computedA.getValue(model), 'woop')
assert.equal(computedB.getValue(model), 'woop')
model.set(['foo', 'bar'], 'woop2')
Expand All @@ -190,12 +178,11 @@ describe('Computed', () => {
}
})
const model = controller.model
const computedFactory = Computed({
const computed = Computed({
foo: 'foo'
}, ({foo}) => {
return foo
})
const computed = computedFactory()
assert.equal(computed.getValue(model), 'bar')
model.set(['foo'], 'bar2')
controller.flush()
Expand Down
6 changes: 2 additions & 4 deletions packages/cerebral/src/Model.test.js
Expand Up @@ -126,13 +126,12 @@ describe('Model', () => {
})
describe('COMPUTE', () => {
it('should compute value from Computed', () => {
const fullNameFactory = Computed({
const fullName = Computed({
firstName: 'user.firstName',
lastName: 'user.lastName'
}, ({firstName, lastName}) => {
return `${firstName} ${lastName}`
})
const fullName = fullNameFactory()
const model = new Model({
user: {
firstName: 'John',
Expand All @@ -142,13 +141,12 @@ describe('Model', () => {
assert.deepEqual(model.compute(fullName), 'John Difool')
})
it('should force recompute value from Computed', () => {
const fullNameFactory = Computed({
const fullName = Computed({
firstName: 'user.firstName',
lastName: 'user.lastName'
}, ({firstName, lastName}) => {
return `${firstName} ${lastName}`
})
const fullName = fullNameFactory()
const model = new Model({
user: {
firstName: 'John',
Expand Down

0 comments on commit 9ad3e96

Please sign in to comment.