diff --git a/packages/cerebral/src/operators/filter.js b/packages/cerebral/src/operators/filter.js index 4ca897899..1052324e8 100644 --- a/packages/cerebral/src/operators/filter.js +++ b/packages/cerebral/src/operators/filter.js @@ -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 } diff --git a/packages/cerebral/src/operators/helpers/populateInputAndStateSchemes.js b/packages/cerebral/src/operators/helpers/populateInputAndStateSchemes.js deleted file mode 100644 index 22f5d43de..000000000 --- a/packages/cerebral/src/operators/helpers/populateInputAndStateSchemes.js +++ /dev/null @@ -1,11 +0,0 @@ -export default (input, state) => { - return (scheme) => { - if (scheme.target === 'state') { - return state.get(scheme.value) - } - if (scheme.target === 'input') { - return input[scheme.value] - } - throw new Error('Cerebral operators - The inline scheme: "' + (JSON.stringify(scheme)) + '" is not valid for the SET operator') - } -} diff --git a/packages/cerebral/src/operators/helpers/populatePath.js b/packages/cerebral/src/operators/helpers/populatePath.js new file mode 100644 index 000000000..d47ca6834 --- /dev/null +++ b/packages/cerebral/src/operators/helpers/populatePath.js @@ -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 + }, '') +} diff --git a/packages/cerebral/src/operators/input.js b/packages/cerebral/src/operators/input.js new file mode 100644 index 000000000..00e75f679 --- /dev/null +++ b/packages/cerebral/src/operators/input.js @@ -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) + } + } + } +} diff --git a/packages/cerebral/src/operators/set.js b/packages/cerebral/src/operators/set.js index e05b3a618..447df77c3 100644 --- a/packages/cerebral/src/operators/set.js +++ b/packages/cerebral/src/operators/set.js @@ -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 } diff --git a/packages/cerebral/src/operators/state.js b/packages/cerebral/src/operators/state.js new file mode 100644 index 000000000..301b339f1 --- /dev/null +++ b/packages/cerebral/src/operators/state.js @@ -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) + } + } + } +} diff --git a/packages/cerebral/src/operators/toggle.js b/packages/cerebral/src/operators/toggle.js index 77f8b7359..b9ab74391 100644 --- a/packages/cerebral/src/operators/toggle.js +++ b/packages/cerebral/src/operators/toggle.js @@ -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 } diff --git a/packages/cerebral/src/operators/when.js b/packages/cerebral/src/operators/when.js index 151312451..0ae7fb076 100644 --- a/packages/cerebral/src/operators/when.js +++ b/packages/cerebral/src/operators/when.js @@ -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 } diff --git a/packages/cerebral/tests/operators.js b/packages/cerebral/tests/operators.js index e84adfda3..dbd03ce11 100644 --- a/packages/cerebral/tests/operators.js +++ b/packages/cerebral/tests/operators.js @@ -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 @@ -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++ } ], @@ -52,6 +73,7 @@ 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: { @@ -59,7 +81,7 @@ describe('Operators', () => { }, signals: { test: [ - filter('state:foo', (value) => value === 'bar'), { + filter(state`foo`, (value) => value === 'bar'), { accepted: [ ({state}) => { state.set('foo', 'bar2') } ], @@ -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`) ] } }) @@ -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: [] } @@ -111,6 +135,7 @@ 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: { @@ -118,7 +143,7 @@ describe('Operators', () => { }, signals: { test: [ - when('state:foo'), { + when(state`foo`), { true: [], false: [() => { count++ }] } @@ -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') ] } }) @@ -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'}) ] } }) @@ -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`) ] } }) @@ -197,6 +226,7 @@ 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', @@ -204,7 +234,7 @@ describe('Operators', () => { }, signals: { test: [ - set('state:foo', 'state:grabValue') + set(state`foo`, state`grabValue`) ] } })