Railway oriented programming in Ember
npm install ember-pipelineRailway oriented programming in Ember. To install:
```
ember install ember-pipeline
ember-pipeline allows you to compose a pipeline of (promise aware) methods on an object using "railway oriented programming". That is, if any of the methods in the pipeline returns a CANCEL token, the entire pipeline exits and can be optionally handled by another method. If the host Ember.Object is destroyed, the pipeline is aborted as well.
For example:
`js
import Ember from 'ember';
import { pipeline, step, CANCEL } from 'ember-pipeline';
const { computed, get } = Ember;
export default Component.extend({
fetchStoreLocations: computed(function() {
return pipeline(this, [
step('requestGeolocation'),
step('fetchStoresInProximity'),
step('sortStoresByDistance'),
step('alwaysCancels')
]).onCancel((cancellation) => this.handleCancel(cancellation));
}),
requestGeolocation() { / ... / },
fetchStoresInProximity() { / ... / },
sortStoresByDistance() { / ... / },
alwaysCancels() {
return CANCEL();
},
handleCancel(cancellation) {
switch (cancellation.fnName) {
case 'requestGeolocation':
// show error message saying you didn't allow us to use geo api
break;
case 'fetchStoresInProximity':
// no stores around you, sorry!
break;
case 'sortStoresByDistance':
// we used bubble sort
break;
default:
// no cancel handler
console.log(last value: ${cancellation.result}, reason: ${cancellation.reason});
break;
}
}),
actions: {
fetchStoreLocations(...args) {
return get(this, 'fetchStoreLocations').perform(...args);
}
}
});
`
First, create a pipeline using pipeline and step. You can also define a cancel handler:
`js`
return pipeline(this, [
step('step1'),
step('step2'),
step('step3')
]).onCancel((cancellation) => this.handleCancel(cancellation));
If using inside of an Ember.Object, you could make this a computed property:
`js`
export default Component.extend({
myPipeline: computed(function() {
return pipeline(this, [
step('step1'),
step('step2'),
step('step3')
]).onCancel((cancellation) => this.handleCancel(cancellation));
})
});
step receives either a method name as a string, or a function:
`js`
[step('step1'), step(x => x * x)];
In a step function, return CANCEL() to abort the pipeline:
`js`
{
step1() {
return CANCEL('optional reason, can be any type');
}
}
Then, to run the pipeline, get the reference to it and perform it:
`js`
get(this, 'myPipeline').perform(...args);
pipelineInstance.perform(...args);
You can compose new pipelines at runtime. For example:
`js
export default Component.extend({
makePipeline(steps) {
return pipeline(this, steps)
.onCancel((cancellation) => this.handleCancel(cancellation));
},
// ...
actions: {
normal(...args) {
return this.makePipeline([step('step1'), step('step2')]).perform(...args);
},
reverse(...args) {
return this.makePipeline([step('step2'), step('step1')]).perform(...args);
}
}
});
`
After a pipeline has been performed, you can get derived state:
`js`
get(this, 'myPipeline').perform(1, 2, 3);
get(this, 'myPipeline.successfulSteps.length'); // 2
get(this, 'myPipeline.cancelledSteps.length'); // 1
get(this, 'myPipeline.successfulSteps'); // array of successful Steps
get(this, 'myPipeline.cancelledSteps'); // array of cancelled Steps
Because features are still in flux, detailed API docs are coming soon!
- [ ] Support ember-concurrency tasks
* git clone this repositorycd ember-pipeline
* npm install
* bower install
*
* ember serve
* Visit your app at http://localhost:4200.
* npm test (Runs ember try:each to test your addon against multiple Ember versions)ember test
* ember test --server
*
* ember build`
For more information on using ember-cli, visit https://ember-cli.com/.