Comparing version 0.7.1 to 0.8.1
@@ -10,11 +10,16 @@ 'use strict'; | ||
var _utils = require('./utils'); | ||
function Element(func, props, children) { | ||
return { | ||
func: func, | ||
props: props, | ||
children: children, | ||
pipeline: func.pipeline || (0, _Pipeline.createDefaultPipeline)(), | ||
scopedVars: (0, _utils.getScopedVars)(props), | ||
name: (0, _utils.getFuncName)(func), | ||
scope: {}, | ||
pipeline: undefined, | ||
result: undefined, | ||
context: undefined, | ||
parent: undefined, | ||
@@ -24,12 +29,31 @@ mergeToProps: function mergeToProps(additionalProps) { | ||
}, | ||
run: async function run(context) { | ||
this.context = context; | ||
dispatch: function dispatch(type, value) { | ||
if (this.scopedVars.indexOf(type) >= 0 || this.scopedVars[0] === '*') { | ||
this.scope[type] = value; | ||
} else { | ||
this.parent.dispatch(type, value); | ||
} | ||
}, | ||
readFromScope: function readFromScope(key) { | ||
var scope = this.scope, | ||
parent = this.parent; | ||
var value = scope[key]; | ||
if (typeof value !== 'undefined') return value; | ||
return parent.readFromScope(key); | ||
}, | ||
run: async function run(parent) { | ||
if (!parent) { | ||
throw new Error('The Element can not be run without a parent.'); | ||
} | ||
this.parent = parent; | ||
this.context = parent.context; | ||
this.pipeline = this.func.pipeline || (0, _Pipeline.createDefaultPipeline)(this); | ||
if (typeof func === 'string') { | ||
this.func = this.context.get(func); | ||
this.func = this.context[func]; | ||
} | ||
this.pipeline.setScope(this); | ||
await this.pipeline.run(); | ||
return this.result; | ||
return await this.pipeline.process(); | ||
} | ||
@@ -39,6 +63,24 @@ }; | ||
// Static props | ||
// Static | ||
Element.isItAnElement = function (element) { | ||
return element && !!element.run; | ||
}; | ||
Element.createRootElement = function (context) { | ||
return { | ||
context: context, | ||
scope: {}, | ||
dispatch: function dispatch(type, value) { | ||
this.scope[type] = value; | ||
}, | ||
readFromScope: function readFromScope(key) { | ||
var value = this.scope[key]; | ||
if (typeof value !== 'undefined') return value; | ||
value = this.context[key]; | ||
if (typeof value !== 'undefined') return value; | ||
throw new Error('"' + key + '" is not defined in the global scope neither in the context.'); | ||
} | ||
}; | ||
}; | ||
Element.errors = { | ||
@@ -45,0 +87,0 @@ STOP_PROCESSING: 'STOP_PROCESSING', |
@@ -19,3 +19,3 @@ 'use strict'; | ||
if (props && _Element2.default.isItAnElement(props.selector)) { | ||
var s = await props.selector.run(this.context); | ||
var s = await props.selector.run(this); | ||
return s(_Integration2.default.getState()); | ||
@@ -22,0 +22,0 @@ } |
@@ -26,4 +26,2 @@ 'use strict'; | ||
var _Context = require('./Context'); | ||
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } | ||
@@ -38,19 +36,26 @@ | ||
// using D as a dymmy component | ||
if (func === create) return (0, _Element2.default)(function () { | ||
return this.context.dump(); | ||
}, props, children); | ||
// using A as a dymmy component | ||
if (func === create) { | ||
return (0, _Element2.default)(function A() { | ||
return { | ||
scope: this.scope, | ||
context: this.context | ||
}; | ||
}, props, children); | ||
} | ||
return (0, _Element2.default)(func, props, children); | ||
} | ||
async function run(element, contextData) { | ||
var context = (0, _Context.createContext)(contextData); | ||
async function run(element) { | ||
var context = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; | ||
var rootElement = _Element2.default.createRootElement(context); | ||
if (_Element2.default.isItAnElement(element)) { | ||
if (_Element2.default.isItAnElement(element.func)) { | ||
element.func.mergeToProps(element.props); | ||
return await element.func.run(context); | ||
return await element.func.run(rootElement); | ||
} | ||
return await element.run(context); | ||
return await element.run(rootElement); | ||
} | ||
return await create(element, null).run(context); | ||
return await create(element, null).run(rootElement); | ||
} | ||
@@ -57,0 +62,0 @@ |
@@ -19,7 +19,7 @@ 'use strict'; | ||
// helpers | ||
var handleElementError = async function handleElementError(error, props, context) { | ||
var handleElementError = async function handleElementError(error, props, element) { | ||
if (props && props.onError) { | ||
props.onError.mergeToProps({ error: error }); | ||
var onErrorStrategy = await props.onError.run(context); | ||
var onErrorStrategy = await props.onError.run(element); | ||
@@ -39,9 +39,10 @@ if (onErrorStrategy === false) { | ||
// middlewares | ||
async function execute(element) { | ||
var func = element.func, | ||
props = element.props, | ||
context = element.context; | ||
props = element.props; | ||
var normalizedProps = _extends({}, props); | ||
// normalizing props | ||
if (props) { | ||
@@ -52,3 +53,3 @@ normalizedProps = _extends({}, props); | ||
var prop = propName.substr(1, propName.length); | ||
var value = context.get(prop); | ||
var value = element.readFromScope(prop); | ||
@@ -69,12 +70,14 @@ if (typeof value !== 'undefined') { | ||
// actual running of the function | ||
try { | ||
element.result = await func.call(element, normalizedProps); | ||
} catch (error) { | ||
await handleElementError(error, normalizedProps, context); | ||
await handleElementError(error, normalizedProps, element); | ||
} | ||
} | ||
async function processResult(element) { | ||
var result = element.result, | ||
context = element.context, | ||
props = element.props; | ||
props = element.props, | ||
scope = element.scope; | ||
@@ -87,6 +90,8 @@ | ||
Object.keys(exportedProps).forEach(function (key) { | ||
return context.set(key, exportedProps[key]); | ||
scope[key] = exportedProps[key]; | ||
element.dispatch(key, exportedProps[key]); | ||
}); | ||
} else { | ||
context.set(props.exports, result); | ||
scope[props.exports] = result; | ||
element.dispatch(props.exports, result); | ||
} | ||
@@ -97,3 +102,3 @@ } | ||
if (_Element2.default.isItAnElement(result)) { | ||
await result.run(context); | ||
await result.run(element); | ||
} | ||
@@ -108,3 +113,3 @@ // Generator | ||
if (_Element2.default.isItAnElement(genRes.value)) { | ||
genRes.value = await genRes.value.run(context); | ||
genRes.value = await genRes.value.run(element); | ||
} | ||
@@ -116,7 +121,7 @@ } | ||
} | ||
async function processChildren(element) { | ||
var func = element.func, | ||
children = element.children, | ||
result = element.result, | ||
context = element.context; | ||
result = element.result; | ||
@@ -128,3 +133,3 @@ // FACC pattern | ||
if (_Element2.default.isItAnElement(resultOfFACC)) { | ||
await resultOfFACC.run(context); | ||
await resultOfFACC.run(element); | ||
} | ||
@@ -142,5 +147,5 @@ | ||
if (parallelProcessing) { | ||
w.run(context); | ||
w.run(element); | ||
} else { | ||
await w.run(context); | ||
await w.run(element); | ||
} | ||
@@ -159,16 +164,19 @@ } catch (error) { | ||
function Pipeline() { | ||
var entries = []; | ||
var API = function API(entryName, result) { | ||
var entry = API.find(entryName); | ||
function Pipeline(element) { | ||
if (!element) { | ||
throw new Error('A Pipeline can not be created with no Element.'); | ||
} | ||
var middlewares = []; | ||
var pipeline = function pipeline(middleware, result) { | ||
var entry = pipeline.find(middleware); | ||
entry.enabled = false; | ||
return entry.func(_extends({}, API.scopeElement, { result: result })); | ||
return entry.func(_extends({}, element, { result: result })); | ||
}; | ||
API.add = function add(func, name) { | ||
entries.push({ name: name, func: func, enabled: true }); | ||
pipeline.add = function add(func, name) { | ||
middlewares.push({ name: name, func: func, enabled: true }); | ||
}; | ||
API.find = function (n) { | ||
var entry = entries.find(function (_ref) { | ||
pipeline.find = function (n) { | ||
var entry = middlewares.find(function (_ref) { | ||
var name = _ref.name; | ||
@@ -184,24 +192,22 @@ return name === n; | ||
}; | ||
API.disable = function (name) { | ||
pipeline.disable = function (name) { | ||
this.find(name).enabled = false; | ||
}; | ||
API.enable = function (name) { | ||
pipeline.enable = function (name) { | ||
this.find(name).enabled = true; | ||
}; | ||
API.run = async function () { | ||
pipeline.process = async function () { | ||
var entry = void 0; | ||
var pointer = 0; | ||
while (entry = entries[pointer]) { | ||
while (entry = middlewares[pointer]) { | ||
if (entry.enabled) { | ||
await entry.func(API.scopeElement); | ||
await entry.func(element); | ||
} | ||
pointer += 1; | ||
} | ||
return element.result; | ||
}; | ||
API.setScope = function (scopeElement) { | ||
API.scopeElement = scopeElement; | ||
}; | ||
return API; | ||
return pipeline; | ||
} | ||
@@ -213,4 +219,4 @@ | ||
function createDefaultPipeline() { | ||
var pipeline = Pipeline(); | ||
function createDefaultPipeline(element) { | ||
var pipeline = Pipeline(element); | ||
@@ -217,0 +223,0 @@ pipeline.add(execute); |
{ | ||
"name": "actml", | ||
"version": "0.7.1", | ||
"version": "0.8.1", | ||
"description": "Like jsx but for your business logic", | ||
@@ -5,0 +5,0 @@ "main": "lib", |
119
README.md
@@ -11,2 +11,7 @@ # <ActML /> :rocket: <!-- omit in toc --> | ||
- [Setting in and getting out from the context](#setting-in-and-getting-out-from-the-context) | ||
- [Setting initial context value](#setting-initial-context-value) | ||
- [Using the context API as a dependency management tool](#using-the-context-api-as-a-dependency-management-tool) | ||
- [Predefined elements](#predefined-elements) | ||
- [Running elements in parallel](#running-elements-in-parallel) | ||
- [Examples](#examples) | ||
@@ -64,3 +69,3 @@ ## Concept | ||
Or if we use ActML's context API: | ||
Or if we use [ActML's context API](#context-api): | ||
@@ -217,4 +222,116 @@ ```js | ||
```js | ||
const IKnowTheAnswer = function() { | ||
return 42; | ||
}; | ||
const Print = function({ message }) { | ||
console.log(`The answer is ${message}`); | ||
}; | ||
const formatMessage = a => ({ | ||
message: a < 50 ? 'less then 50' : 'more then 50' | ||
}); | ||
run( | ||
<A> | ||
<IKnowTheAnswer exports="answer" /> | ||
<Print $answer={ formatMessage } /> | ||
</A> | ||
); | ||
// Prints out: The answer is less then 50 | ||
``` | ||
### Setting initial context value | ||
The `run` function accepts a second argument which is the initial state of the context. We can pass an object in the format of key-value pairs. | ||
```js | ||
const Print = function({ name }) { | ||
console.log(`Hello ${name}`); | ||
}; | ||
const initialContext = { | ||
name: 'David' | ||
}; | ||
run(<Print $name />, initialContext); | ||
// Prints out: Hello David | ||
``` | ||
### Using the context API as a dependency management tool | ||
Because the context is available in every element we may use it to deliver dependencies. It works not only with variables but also with other elements. For example: | ||
```js | ||
// context.js | ||
const initialContext = { | ||
async getSeason({ endpoint }) { | ||
const result = await fetch(endpoint); | ||
const { season } = await result.json(); | ||
return season; | ||
}, | ||
print({ season }) { | ||
console.log(`The season is ${season}`); | ||
} | ||
}; | ||
export default initialContext; | ||
// App.js | ||
import initialContext from './context.js'; | ||
run( | ||
<A> | ||
<getSeason endpoint="https://www.mocky.io/v2/5ba2a2b52f00006a008d2e0d"> | ||
{ season => <print season={season} /> } | ||
</getSeason> | ||
</A>, | ||
initialContext | ||
); | ||
// Prints out: The season is not summer | ||
``` | ||
Notice how `getSeason` and `print` are only defined in the context and they don't exist in `App.js`. And here we have to mention that this is only possible because of the JSX transpiler. They both should start with a lowercase letter. That is really important because: | ||
```js | ||
<getSeason /> | ||
``` | ||
gets transpiled to | ||
```js | ||
A("getSeason", null); | ||
``` | ||
while | ||
```js | ||
<GetSeason /> | ||
``` | ||
to | ||
```js | ||
A(GetSeason, null); | ||
``` | ||
In the second case there **must** be a function `GetSeason` while in the first case there's just a string `getSeason` passed to ActML runner. | ||
## Predefined elements | ||
There are some predefined elements that come with ActML core package. | ||
### Running elements in parallel | ||
```js | ||
import { A, run, Parallel } from 'actml'; | ||
const Z = await function () { ... } | ||
const M = function () { ... } | ||
run(<Parallel><Z /><M /></Parallel>); | ||
``` | ||
`Z` and `M` run in parallel which means that `M` is not waiting for `Z` to finish. | ||
## Examples | ||
* [Codesandbox](https://codesandbox.io/s/qx667yqvj9) | ||
* [React+Redux+ActML app](https://github.com/krasimir/actml/tree/master/examples/react-redux-app) |
Sorry, the diff of this file is not supported yet
380124
41
998
335