ember-pipeline
ember-pipeline copied to clipboard
Railway oriented programming in Ember
ember-pipeline

Railway oriented programming in Ember. To install:
ember install ember-pipeline
Philosophy
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:
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);
}
}
});
Usage
First, create a pipeline using pipeline and step. You can also define a cancel handler:
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:
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:
[step('step1'), step(x => x * x)];
In a step function, return CANCEL() to abort the pipeline:
{
step1() {
return CANCEL('optional reason, can be any type');
}
}
Then, to run the pipeline, get the reference to it and perform it:
get(this, 'myPipeline').perform(...args);
pipelineInstance.perform(...args);
You can compose new pipelines at runtime. For example:
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:
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
API
Because features are still in flux, detailed API docs are coming soon!
Roadmap
- [ ] Support
ember-concurrencytasks
Installation
git clone <repository-url>this repositorycd ember-pipelinenpm installbower install
Running
ember serve- Visit your app at http://localhost:4200.
Running Tests
npm test(Runsember try:eachto test your addon against multiple Ember versions)ember testember test --server
Building
ember build
For more information on using ember-cli, visit https://ember-cli.com/.