@ibm-functions/composer
Advanced tools
Comparing version 0.2.2 to 0.3.0
@@ -286,3 +286,3 @@ /* | ||
if (typeof count !== 'number') throw new ComposerError('Invalid argument', count) | ||
return this.let({ count }, this.while(this.function(() => count-- > 0, { helper: 'repeat_1' }), this.seq(...Array.prototype.slice.call(arguments, 1)))) | ||
return this.let({ count }, this.while(this.function(() => count-- > 0, { helper: 'repeat_1' }), this.mask(this.seq(...Array.prototype.slice.call(arguments, 1))))) | ||
} | ||
@@ -296,6 +296,11 @@ | ||
this.dowhile( | ||
this.finally(this.function(({ params }) => params, { helper: 'retry_2' }), attempt), | ||
this.finally(this.function(({ params }) => params, { helper: 'retry_2' }), this.mask(attempt)), | ||
this.function(({ result }) => typeof result.error !== 'undefined' && count-- > 0, { helper: 'retry_3' })), | ||
this.function(({ result }) => result, { helper: 'retry_4' })) | ||
} | ||
mask(body, options) { | ||
if (arguments.length > 2) throw new ComposerError('Too many arguments') | ||
return new Composition({ type: 'mask', body: this.task(body) }, options) | ||
} | ||
} | ||
@@ -338,2 +343,5 @@ | ||
return [[{ type: 'let', let: json.declarations, path }], body, [{ type: 'exit', path }]].reduce(chain) | ||
case 'mask': | ||
var body = compile(json.body, path + '.body') | ||
return [[{ type: 'mask', path }], body, [{ type: 'exit', path }]].reduce(chain) | ||
case 'retain': | ||
@@ -439,5 +447,20 @@ var body = compile(json.body, path + '.body') | ||
function run(f) { | ||
// handle let/mask pairs | ||
const view = [] | ||
let n = 0 | ||
for (let i in stack) { | ||
if (typeof stack[i].mask !== 'undefined') { | ||
n++ | ||
} else if (typeof stack[i].let !== 'undefined') { | ||
if (n === 0) { | ||
view.push(stack[i]) | ||
} else { | ||
n-- | ||
} | ||
} | ||
} | ||
// update value of topmost matching symbol on stack if any | ||
function set(symbol, value) { | ||
const element = stack.find(element => typeof element.let !== 'undefined' && typeof element.let[symbol] !== 'undefined') | ||
const element = view.find(element => typeof element.let !== 'undefined' && typeof element.let[symbol] !== 'undefined') | ||
if (typeof element !== 'undefined') element.let[symbol] = JSON.parse(JSON.stringify(value)) | ||
@@ -447,3 +470,3 @@ } | ||
// collapse stack for invocation | ||
const env = stack.reduceRight((acc, cur) => typeof cur.let === 'object' ? Object.assign(acc, cur.let) : acc, {}) | ||
const env = view.reduceRight((acc, cur) => typeof cur.let === 'object' ? Object.assign(acc, cur.let) : acc, {}) | ||
let main = '(function(){try{' | ||
@@ -484,2 +507,5 @@ for (const name in env) main += `var ${name}=arguments[1]['${name}'];` | ||
break | ||
case 'mask': | ||
stack.unshift({ mask: true }) | ||
break | ||
case 'exit': | ||
@@ -486,0 +512,0 @@ if (stack.length === 0) return internalError(`State ${current} attempted to pop from an empty stack`) |
@@ -128,2 +128,6 @@ # Combinators | ||
JSON values cannot represent functions. Applying `composer.literal` to a value of type `'function'` will result in an error. Functions embedded in a `value` of type `'object'`, e.g., `{ f: p => p, n: 42 }` will be silently omitted from the JSON dictionary. In other words, `composer.literal({ f: p => p, n: 42 })` will output `{ n: 42 }`. | ||
In general, a function can be embedded in a composition either by using the `composer.function` combinator, or by embedding the source code for the function as a string and later using `eval` to evaluate the function code. | ||
## Sequence | ||
@@ -143,2 +147,4 @@ | ||
The initial values must be valid JSON values. In particular, `composer.let({ foo: undefined })` is incorrect as `undefined` is not representable by a JSON value. On the other hand, `composer.let({ foo: null })` is correct. For the same reason, initial values cannot be functions, e.g., `composer.let({ foo: params => params })` is incorrect. | ||
Variables declared with `composer.let` may be accessed and mutated by functions __running__ as part of the following sequence (irrespective of their place of definition). In other words, name resolution is [dynamic](https://en.wikipedia.org/wiki/Name_resolution_(programming_languages)#Static_versus_dynamic). If a variable declaration is nested inside a declaration of a variable with the same name, the innermost declaration masks the earlier declarations. | ||
@@ -145,0 +151,0 @@ |
{ | ||
"name": "@ibm-functions/composer", | ||
"version": "0.2.2", | ||
"version": "0.3.0", | ||
"description": "Composer is an IBM Cloud Functions programming model for composing individual functions into larger applications.", | ||
@@ -5,0 +5,0 @@ "homepage": "https://github.com/ibm-functions/composer", |
@@ -520,2 +520,38 @@ const assert = require('assert') | ||
describe('mask', function () { | ||
it('let/let/mask', function () { | ||
return invoke(composer.let({ x: 42 }, composer.let({ x: 69 }, composer.mask(() => x)))) | ||
.then(activation => assert.deepEqual(activation.response.result, { value: 42 })) | ||
}) | ||
it('let/mask/let', function () { | ||
return invoke(composer.let({ x: 42 }, composer.mask(composer.let({ x: 69 }, () => x)))) | ||
.then(activation => assert.deepEqual(activation.response.result, { value: 69 })) | ||
}) | ||
it('let/let/try/mask', function () { | ||
return invoke(composer.let({ x: 42 }, composer.let({ x: 69 }, | ||
composer.try(composer.mask(() => x), () => { })))) | ||
.then(activation => assert.deepEqual(activation.response.result, { value: 42 })) | ||
}) | ||
it('let/let/let/mask', function () { | ||
return invoke(composer.let({ x: 42 }, composer.let({ x: 69 }, | ||
composer.let({ x: -1 }, composer.mask(() => x))))) | ||
.then(activation => assert.deepEqual(activation.response.result, { value: 69 })) | ||
}) | ||
it('let/let/let/mask/mask', function () { | ||
return invoke(composer.let({ x: 42 }, composer.let({ x: 69 }, | ||
composer.let({ x: -1 }, composer.mask(composer.mask(() => x)))))) | ||
.then(activation => assert.deepEqual(activation.response.result, { value: 42 })) | ||
}) | ||
it('let/let/mask/let/mask', function () { | ||
return invoke(composer.let({ x: 42 }, composer.let({ x: 69 }, | ||
composer.mask(composer.let({ x: -1 }, composer.mask(() => x)))))) | ||
.then(activation => assert.deepEqual(activation.response.result, { value: 42 })) | ||
}) | ||
}) | ||
describe('retain', function () { | ||
@@ -522,0 +558,0 @@ it('base case', function () { |
123827
1182