Skip to content

Commit

Permalink
feat(async): add support for async expressions and modify expression …
Browse files Browse the repository at this point in the history
…interpreter definition api

Previously the expression interpreter structure allowed only for evaluation of synchronous
expressions. This commit modifies the way interpreters are prepared in order to support both
synchronous and asynchronous execution.

BREAKING CHANGE: remove interpreter method, add syncInterpreter, syncInterpreterList,
asyncInterpreter and asyncInterpreterList methods
  • Loading branch information
simonfan committed Mar 3, 2021
1 parent 20d0165 commit 8bd0db2
Show file tree
Hide file tree
Showing 29 changed files with 700 additions and 446 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
"@babel/core": "^7.11.5",
"@babel/preset-env": "^7.11.5",
"@babel/preset-typescript": "^7.10.4",
"@orioro/jest-util": "^1.2.0",
"@orioro/jest-util": "^1.3.0",
"@orioro/readme": "^1.0.1",
"@rollup/plugin-babel": "^5.2.0",
"@rollup/plugin-commonjs": "^15.0.0",
Expand Down
6 changes: 5 additions & 1 deletion src/__snapshots__/index.spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,10 @@ Array [
"isExpression",
"evaluate",
"evaluateTyped",
"interpreter",
"evaluateTypedAsync",
"syncInterpreter",
"syncInterpreterList",
"asyncInterpreter",
"asyncInterpreterList",
]
`;
82 changes: 82 additions & 0 deletions src/async.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { testCases, asyncResult } from '@orioro/jest-util'
// import { validateType } from '@orioro/typing'

import { ALL_EXPRESSIONS } from './'

import { asyncInterpreterList } from './interpreter'

import { evaluate } from './evaluate'

const wait = (ms, result) =>
new Promise((resolve) => setTimeout(resolve.bind(null, result), ms))

const $asyncLoadStr = [() => wait(100, 'async-str'), []]
const $asyncLoadNum = [() => wait(100, 9), []]
const $asyncLoadArr = [() => wait(100, ['str-1', 'str-2', 'str-3']), []]
const $asyncLoadObj = [
() =>
wait(100, {
key1: 'value1',
key2: 'value2',
}),
[],
]
const $asyncLoadTrue = [() => wait(100, true), []]
const $asyncLoadFalse = [() => wait(100, false), []]

const interpreters = asyncInterpreterList({
...ALL_EXPRESSIONS,
$asyncLoadStr,
$asyncLoadNum,
$asyncLoadArr,
$asyncLoadObj,
$asyncLoadTrue,
$asyncLoadFalse,
})

describe('async - immediate async expression', () => {
testCases(
[
['$asyncLoadStr', asyncResult('async-str')],
['$asyncLoadNum', asyncResult(9)],
['$asyncLoadArr', asyncResult(['str-1', 'str-2', 'str-3'])],
['$asyncLoadObj', asyncResult({ key1: 'value1', key2: 'value2' })],
['$asyncLoadTrue', asyncResult(true)],
['$asyncLoadFalse', asyncResult(false)],
],
(expression) =>
evaluate({ interpreters, scope: { $$VALUE: null } }, [expression])
)
})

describe('async - nested async expression', () => {
test('simple scenario - string concat', () => {
return expect(
evaluate(
{
interpreters,
scope: {
$$VALUE: 'value-',
},
},
['$stringConcat', ['$asyncLoadStr']]
)
).resolves.toEqual('value-async-str')
})
})

describe('async - syncronous expressions only get converted to async as well', () => {
test('simple scenario - string concat', () => {
return expect(
evaluate(
{
interpreters,
scope: {
$$VALUE: 'value-',
},
},
['$stringConcat', 'sync-value']
)
).resolves.toEqual('value-sync-value')
})
})
49 changes: 33 additions & 16 deletions src/evaluate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,6 @@ const _maybeExpression = (value) =>
typeof value[0] === 'string' &&
value[0].startsWith('$')

const _evaluateProd = (
context: EvaluationContext,
expOrValue: Expression | any
): any => {
if (!isExpression(context.interpreters, expOrValue)) {
return expOrValue
}

const [interpreterId, ...interpreterArgs] = expOrValue
const interpreter = context.interpreters[interpreterId]

return interpreter(context, ...interpreterArgs)
}

const _ellipsis = (str, maxlen = 50) =>
str.length > maxlen ? str.substr(0, maxlen - 1).concat('...') : str

Expand All @@ -54,7 +40,21 @@ const _evaluateDev = (
)
}

return _evaluateProd(context, expOrValue)
return _evaluate(context, expOrValue)
}

const _evaluate = (
context: EvaluationContext,
expOrValue: Expression | any
): any => {
if (!isExpression(context.interpreters, expOrValue)) {
return expOrValue
}

const [interpreterId, ...interpreterArgs] = expOrValue
const interpreter = context.interpreters[interpreterId]

return interpreter(context, ...interpreterArgs)
}

/**
Expand All @@ -66,7 +66,7 @@ const _evaluateDev = (
export const evaluate =
process && process.env && process.env.NODE_ENV !== 'production'
? _evaluateDev
: _evaluateProd
: _evaluate

/**
* @function evaluateTyped
Expand All @@ -84,3 +84,20 @@ export const evaluateTyped = (
validateType(expectedTypes, value)
return value
}

/**
* @function evaluateTypedAsync
* @param {String | string[]} expectedTypes
* @param {EvaluationContext} context
* @param {Expression | any} expOrValue
* @returns {Promise<*>}
*/
export const evaluateTypedAsync = (
expectedTypes: string | string[],
context: EvaluationContext,
expOrValue: Expression | any
): Promise<any> =>
Promise.resolve(evaluate(context, expOrValue)).then((value) => {
validateType(expectedTypes, value)
return value
})
5 changes: 3 additions & 2 deletions src/expressions/array.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { evaluate } from '../evaluate'
import { syncInterpreterList } from '../interpreter'
import { VALUE_EXPRESSIONS } from './value'
import { COMPARISON_EXPRESSIONS } from './comparison'
import { LOGICAL_EXPRESSIONS } from './logical'
Expand All @@ -8,7 +9,7 @@ import { STRING_EXPRESSIONS } from './string'
import { MATH_EXPRESSIONS } from './math'
import { NUMBER_EXPRESSIONS } from './number'

const interpreters = {
const interpreters = syncInterpreterList({
...VALUE_EXPRESSIONS,
...LOGICAL_EXPRESSIONS,
...NUMBER_EXPRESSIONS,
Expand All @@ -17,7 +18,7 @@ const interpreters = {
...OBJECT_EXPRESSIONS,
...STRING_EXPRESSIONS,
...ARRAY_EXPRESSIONS,
}
})

describe('$arrayIncludes', () => {
test('basic usage', () => {
Expand Down
Loading

0 comments on commit 8bd0db2

Please sign in to comment.