Skip to content

Commit

Permalink
feat(Operators): implement template tag based operators
Browse files Browse the repository at this point in the history
  • Loading branch information
christianalfoni committed Oct 18, 2016
1 parent 321ae82 commit 77a6ae3
Show file tree
Hide file tree
Showing 9 changed files with 110 additions and 104 deletions.
27 changes: 5 additions & 22 deletions packages/cerebral/src/operators/filter.js
Original file line number Diff line number Diff line change
@@ -1,32 +1,15 @@
import parseScheme from 'cerebral-scheme-parser'
import populateInputAndStateSchemes from './helpers/populateInputAndStateSchemes'

export default function (passedPath, filterFunc) {
const pathScheme = parseScheme(passedPath)

export default function (template, filterFunc) {
const filterValue = typeof filterFunc === 'function'
? filterFunc
: (value) => value === filterFunc

if (pathScheme.target !== 'state' && pathScheme.target !== 'input') {
throw new Error('Cerebral operator FILTER - The path: "' + passedPath + '" does not target "state" or "input"')
}

// define the action
const filter = function ({input, state, path}) {
const pathValue = pathScheme.getValue(populateInputAndStateSchemes(input, state))
let value

if (pathScheme.target === 'input') {
value = input[pathValue]
} else if (pathScheme.target === 'state') {
value = state.get(pathValue)
}
function filter (context) {
const value = template(context).toValue()

return filterValue(value) ? path.accepted() : path.discarded()
return filterValue(value) ? context.path.accepted() : context.path.discarded()
}

filter.displayName = 'operator FILTER'
filter.displayName = 'operator.filter'

return filter
}

This file was deleted.

9 changes: 9 additions & 0 deletions packages/cerebral/src/operators/helpers/populatePath.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export default function populatePath (context, strings, values) {
return strings.reduce((currentPath, string) => {
if (!string) {
return currentPath + values.shift()(context).toValue()
}

return currentPath + string
}, '')
}
18 changes: 18 additions & 0 deletions packages/cerebral/src/operators/input.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import populatePath from './helpers/populatePath'

export default function input (strings, ...values) {
return (context) => {
const target = 'input'
const path = populatePath(context, strings, values)

return {
target,
path,
toValue () {
return path.split('.').reduce((currentValue, key) => {
return currentValue[key]
}, context.input)
}
}
}
}
36 changes: 10 additions & 26 deletions packages/cerebral/src/operators/set.js
Original file line number Diff line number Diff line change
@@ -1,36 +1,20 @@
import parseScheme from 'cerebral-scheme-parser'
import populateInputAndStateSchemes from './helpers/populateInputAndStateSchemes'

export default function (path, value) {
const pathScheme = parseScheme(path)
const valueScheme = typeof value === 'string' ? parseScheme(value) : value

if (pathScheme.target !== 'state') {
throw new Error('Cerebral operator SET - The path: "' + path + '" does not target "state"')
export default function (fromTemplate, valueTemplate) {
if (typeof fromTemplate !== 'function') {
throw new Error('Cerebral operator.set: You have to use a state template tag as first argument')
}

if (
valueScheme &&
valueScheme.target &&
valueScheme.target !== 'input' &&
valueScheme.target !== 'state'
) {
throw new Error('Cerebral operator SET - The value: "' + path + '" does not target "input" or "state"')
}
const set = function set ({input, state}) {
const pathSchemeValue = pathScheme.getValue(populateInputAndStateSchemes(input, state))
let valueSchemeValue = value
function set (context) {
const from = fromTemplate(context)
const value = typeof valueTemplate === 'function' ? valueTemplate(context).toValue() : valueTemplate

if (valueScheme && valueScheme.target && valueScheme.target === 'input') {
valueSchemeValue = input[valueScheme.getValue(populateInputAndStateSchemes(input, state))]
} else if (valueScheme && valueScheme.target && valueScheme.target === 'state') {
valueSchemeValue = state.get(valueScheme.getValue(populateInputAndStateSchemes(input, state)))
if (from.target !== 'state') {
throw new Error('Cerebral operator.set: You have to use a state template tag as first argument')
}

state.set(pathSchemeValue, valueSchemeValue)
context.state.set(from.path, value)
}

set.displayName = 'operator SET'
set.displayName = 'operator.set'

return set
}
16 changes: 16 additions & 0 deletions packages/cerebral/src/operators/state.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import populatePath from './helpers/populatePath'

export default function state (strings, ...values) {
return (context) => {
const target = 'state'
const path = populatePath(context, strings, values)

return {
target,
path,
toValue () {
return context.state.get(path)
}
}
}
}
22 changes: 8 additions & 14 deletions packages/cerebral/src/operators/toggle.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,15 @@
import parseScheme from 'cerebral-scheme-parser'
import populateInputAndStateSchemes from './helpers/populateInputAndStateSchemes'
export default function (template) {
function toggle ({input, state}) {
const {target, path} = template()

export default function (path, onValue = true, offValue = false) {
const pathScheme = parseScheme(path)
if (target !== 'state') {
throw new Error('Cerebral operator.toggle: You have to use a state template tag as first argument')
}

if (pathScheme.target !== 'state') {
throw new Error('Cerebral operator TOGGLE - The path: "' + path + '" does not target "state"')
state.set(path, !state.get(path))
}

const toggle = function toggleRead ({input, state}) {
const pathValue = pathScheme.getValue(populateInputAndStateSchemes(input, state))
const currentValue = state.get(pathValue)

state.set(pathValue, currentValue === onValue ? offValue : onValue)
}

toggle.displayName = 'operator TOGGLE'
toggle.displayName = 'operator.toggle'

return toggle
}
27 changes: 5 additions & 22 deletions packages/cerebral/src/operators/when.js
Original file line number Diff line number Diff line change
@@ -1,28 +1,11 @@
import parseScheme from 'cerebral-scheme-parser'
import populateInputAndStateSchemes from './helpers/populateInputAndStateSchemes'
function whenFactory (template) {
function when (context) {
const exists = Boolean(template(context).toValue())

function whenFactory (passedPath) {
const pathScheme = parseScheme(passedPath)

if (pathScheme.target !== 'state' && pathScheme.target !== 'input') {
throw new Error('Cerebral operator WHEN - The path: "' + passedPath + '" does not target "input" or "state"')
}

// define the action
const when = function ({input, state, path}) {
const pathValue = pathScheme.getValue(populateInputAndStateSchemes(input, state))
let value

if (pathScheme.target === 'input') {
value = input[pathValue]
} else if (pathScheme.target === 'state') {
value = state.get(pathValue)
}

return value ? path.true() : path.false()
return exists ? context.path.true() : context.path.false()
}

when.displayName = `operator WHEN (${passedPath})`
when.displayName = 'operator.when'

return when
}
Expand Down
48 changes: 39 additions & 9 deletions packages/cerebral/tests/operators.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,26 @@ import Controller from '../src/Controller'
import assert from 'assert'

describe('Operators', () => {
it('should be able to nest template tags', () => {
const set = require('../src/operators/set').default
const state = require('../src/operators/state').default
const input = require('../src/operators/input').default
const controller = new Controller({
state: {
foo: 'bar',
grabValue: {
bar: 'baz'
}
},
signals: {
test: [
set(state`foo`, state`grabValue.${input`foo`}`)
]
}
})
controller.getSignal('test')({foo: 'bar'})
assert.equal(controller.getState().foo, 'baz')
})
describe('debounce', () => {
it('should debounce execution', (done) => {
const debounce = require('../src/operators/debounce').default
Expand All @@ -29,12 +49,13 @@ describe('Operators', () => {
describe('filter', () => {
it('should filter input', () => {
const filter = require('../src/operators/filter').default
const input = require('../src/operators/input').default
let accepted = 0
let discarded = 0
const controller = new Controller({
signals: {
test: [
filter('input:value', (value) => Boolean(value.length)), {
filter(input`value`, (value) => Boolean(value.length)), {
accepted: [
() => { accepted++ }
],
Expand All @@ -52,14 +73,15 @@ describe('Operators', () => {
})
it('should filter state', () => {
const filter = require('../src/operators/filter').default
const state = require('../src/operators/state').default
let discarded = 0
const controller = new Controller({
state: {
foo: 'bar'
},
signals: {
test: [
filter('state:foo', (value) => value === 'bar'), {
filter(state`foo`, (value) => value === 'bar'), {
accepted: [
({state}) => { state.set('foo', 'bar2') }
],
Expand All @@ -78,13 +100,14 @@ describe('Operators', () => {
describe('toggle', () => {
it('should toggle state', () => {
const toggle = require('../src/operators/toggle').default
const state = require('../src/operators/state').default
const controller = new Controller({
state: {
foo: true
},
signals: {
test: [
toggle('state:foo')
toggle(state`foo`)
]
}
})
Expand All @@ -95,11 +118,12 @@ describe('Operators', () => {
describe('when', () => {
it('should check truthy value of input', () => {
const when = require('../src/operators/when').default
const input = require('../src/operators/input').default
let count = 0
const controller = new Controller({
signals: {
test: [
when('input:foo'), {
when(input`foo`), {
true: [() => { count++ }],
false: []
}
Expand All @@ -111,14 +135,15 @@ describe('Operators', () => {
})
it('should check truthy value of state', () => {
const when = require('../src/operators/when').default
const state = require('../src/operators/state').default
let count = 0
const controller = new Controller({
state: {
foo: false
},
signals: {
test: [
when('state:foo'), {
when(state`foo`), {
true: [],
false: [() => { count++ }]
}
Expand Down Expand Up @@ -150,13 +175,14 @@ describe('Operators', () => {
describe('set', () => {
it('should set value to model', () => {
const set = require('../src/operators/set').default
const state = require('../src/operators/state').default
const controller = new Controller({
state: {
foo: 'bar'
},
signals: {
test: [
set('state:foo', 'bar2')
set(state`foo`, 'bar2')
]
}
})
Expand All @@ -165,13 +191,14 @@ describe('Operators', () => {
})
it('should set non string value to model', () => {
const set = require('../src/operators/set').default
const state = require('../src/operators/state').default
const controller = new Controller({
state: {
foo: 'bar'
},
signals: {
test: [
set('state:foo', {bar: 'baz'})
set(state`foo`, {bar: 'baz'})
]
}
})
Expand All @@ -180,13 +207,15 @@ describe('Operators', () => {
})
it('should set value to model from input', () => {
const set = require('../src/operators/set').default
const state = require('../src/operators/state').default
const input = require('../src/operators/input').default
const controller = new Controller({
state: {
foo: 'bar'
},
signals: {
test: [
set('state:foo', 'input:value')
set(state`foo`, input`value`)
]
}
})
Expand All @@ -197,14 +226,15 @@ describe('Operators', () => {
})
it('should set value to model from model', () => {
const set = require('../src/operators/set').default
const state = require('../src/operators/state').default
const controller = new Controller({
state: {
foo: 'bar',
grabValue: 'bar2'
},
signals: {
test: [
set('state:foo', 'state:grabValue')
set(state`foo`, state`grabValue`)
]
}
})
Expand Down

0 comments on commit 77a6ae3

Please sign in to comment.