Skip to content

Commit

Permalink
Check if every unit is a function (#24)
Browse files Browse the repository at this point in the history
Check if every unit is a function
  • Loading branch information
paulocoghi authored Jan 19, 2024
1 parent 71640b4 commit a1f2c5c
Show file tree
Hide file tree
Showing 7 changed files with 51 additions and 13 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Unitflow changelog

## 1.0.3 - 2024-01-19

* Gracefully stop a flow when at least one unit inside it isn't a function, informing on console

## 1.0.2 - 2024-01-19

* Updated how ESM and CommonJS versions are distributed
Expand Down
2 changes: 1 addition & 1 deletion dist/unitflow.cjs
Original file line number Diff line number Diff line change
@@ -1 +1 @@
"use strict";exports.Unitflow=class{constructor(e={}){this.unit={},this.flow={},this.state=e}async run(...e){const t=[],n=this.prepare(e);n.flows.reverse();for(let e=n.flows.length-1;e>=0;e--){const s=new Promise((t=>this.link_and_start(n.flows[e],n,t)));t.push(s)}return Promise.all(t)}prepare(e){let t=e.pop();const n={};return"string"==typeof t?(e.push(t),t={}):this.dependents(t,n),{flows:e,dependencies:t,dependents:n,execution:{}}}dependents(e,t){for(let[n,s]of Object.entries(e))for(let e=s.length-1;e>=0;e--)t[s[e]]||(t[s[e]]=[]),t[s[e]].push(n)}link_and_start(e,t,n){const s=this.flow[e],i=s.length,o={},r=this.state;for(let c=i-1;c>=0;c--){const d=this.unit[s[c]].bind(this),p=c==i-1?n:o[c+1],l=e+":"+s[c],h=async()=>d(r,p,n);o[c]=()=>this.wait_or_run(l,h,t)}o[0]()}wait_or_run(e,t,n,s=!1){if(!s&&!this.dependencies_completed(e,n))return n.execution[e]=()=>this.wait_or_run(e,t,n,!0);n.execution[e]=!0,t(),this.check_dependents(e,n)}dependencies_completed(e,t){const n=t.dependencies[e];return!n||n.every((e=>!0===t.execution[e]))}check_dependents(e,t){const n=t.dependents[e];if(n)for(let e=n.length-1;e>=0;e--){const s=n[e];"function"==typeof t.execution[s]&&(this.dependencies_completed(s,t)&&t.execution[s]())}}};
"use strict";exports.Unitflow=class{constructor(e={}){this.unit={},this.flow={},this.state=e}async run(...e){const t=[],n=this.prepare(e);n.flows.reverse();for(let e=n.flows.length-1;e>=0;e--){const s=new Promise((t=>this.link_and_start(n.flows[e],n,t)));t.push(s)}return Promise.all(t)}prepare(e){let t=e.pop();const n={};return"string"==typeof t?(e.push(t),t={}):this.dependents(t,n),{flows:e,dependencies:t,dependents:n,execution:{}}}dependents(e,t){for(let[n,s]of Object.entries(e))for(let e=s.length-1;e>=0;e--)t[s[e]]||(t[s[e]]=[]),t[s[e]].push(n)}link_and_start(e,t,n){const s=this.flow[e],o=s.length,i={},r=this.state;for(let c=o-1;c>=0;c--){if("function"!=typeof this.unit[s[c]])return console.log(`Error: Unit "${s[c]}" on flow "${e}" is not a function. This flow execution is stopped.`)&&n();const d=this.unit[s[c]].bind(this),l=c==o-1?n:i[c+1],p=e+":"+s[c],u=async()=>d(r,l,n);i[c]=()=>this.wait_or_run(p,u,t)}i[0]()}wait_or_run(e,t,n,s=!1){if(!s&&!this.dependencies_completed(e,n))return n.execution[e]=()=>this.wait_or_run(e,t,n,!0);n.execution[e]=!0,t(),this.check_dependents(e,n)}dependencies_completed(e,t){const n=t.dependencies[e];return!n||n.every((e=>!0===t.execution[e]))}check_dependents(e,t){const n=t.dependents[e];if(n)for(let e=n.length-1;e>=0;e--){const s=n[e];"function"==typeof t.execution[s]&&(this.dependencies_completed(s,t)&&t.execution[s]())}}};
2 changes: 1 addition & 1 deletion dist/unitflow.mjs
Original file line number Diff line number Diff line change
@@ -1 +1 @@
class e{constructor(e={}){this.unit={},this.flow={},this.state=e}async run(...e){const t=[],n=this.prepare(e);n.flows.reverse();for(let e=n.flows.length-1;e>=0;e--){const s=new Promise((t=>this.link_and_start(n.flows[e],n,t)));t.push(s)}return Promise.all(t)}prepare(e){let t=e.pop();const n={};return"string"==typeof t?(e.push(t),t={}):this.dependents(t,n),{flows:e,dependencies:t,dependents:n,execution:{}}}dependents(e,t){for(let[n,s]of Object.entries(e))for(let e=s.length-1;e>=0;e--)t[s[e]]||(t[s[e]]=[]),t[s[e]].push(n)}link_and_start(e,t,n){const s=this.flow[e],i=s.length,o={},r=this.state;for(let c=i-1;c>=0;c--){const d=this.unit[s[c]].bind(this),p=c==i-1?n:o[c+1],h=e+":"+s[c],l=async()=>d(r,p,n);o[c]=()=>this.wait_or_run(h,l,t)}o[0]()}wait_or_run(e,t,n,s=!1){if(!s&&!this.dependencies_completed(e,n))return n.execution[e]=()=>this.wait_or_run(e,t,n,!0);n.execution[e]=!0,t(),this.check_dependents(e,n)}dependencies_completed(e,t){const n=t.dependencies[e];return!n||n.every((e=>!0===t.execution[e]))}check_dependents(e,t){const n=t.dependents[e];if(n)for(let e=n.length-1;e>=0;e--){const s=n[e];"function"==typeof t.execution[s]&&(this.dependencies_completed(s,t)&&t.execution[s]())}}}export{e as Unitflow};
class e{constructor(e={}){this.unit={},this.flow={},this.state=e}async run(...e){const t=[],n=this.prepare(e);n.flows.reverse();for(let e=n.flows.length-1;e>=0;e--){const s=new Promise((t=>this.link_and_start(n.flows[e],n,t)));t.push(s)}return Promise.all(t)}prepare(e){let t=e.pop();const n={};return"string"==typeof t?(e.push(t),t={}):this.dependents(t,n),{flows:e,dependencies:t,dependents:n,execution:{}}}dependents(e,t){for(let[n,s]of Object.entries(e))for(let e=s.length-1;e>=0;e--)t[s[e]]||(t[s[e]]=[]),t[s[e]].push(n)}link_and_start(e,t,n){const s=this.flow[e],o=s.length,i={},r=this.state;for(let c=o-1;c>=0;c--){if("function"!=typeof this.unit[s[c]])return console.log(`Error: Unit "${s[c]}" on flow "${e}" is not a function. This flow execution is stopped.`)&&n();const d=this.unit[s[c]].bind(this),p=c==o-1?n:i[c+1],l=e+":"+s[c],h=async()=>d(r,p,n);i[c]=()=>this.wait_or_run(l,h,t)}i[0]()}wait_or_run(e,t,n,s=!1){if(!s&&!this.dependencies_completed(e,n))return n.execution[e]=()=>this.wait_or_run(e,t,n,!0);n.execution[e]=!0,t(),this.check_dependents(e,n)}dependencies_completed(e,t){const n=t.dependencies[e];return!n||n.every((e=>!0===t.execution[e]))}check_dependents(e,t){const n=t.dependents[e];if(n)for(let e=n.length-1;e>=0;e--){const s=n[e];"function"==typeof t.execution[s]&&(this.dependencies_completed(s,t)&&t.execution[s]())}}}export{e as Unitflow};
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@alumna/unitflow",
"version": "1.0.2",
"version": "1.0.3",
"description": "Organize your library or project, defining flows composable by a sequence of units",
"exports": {
"import": "./dist/unitflow.mjs",
Expand Down
20 changes: 10 additions & 10 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions src/unitflow.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,10 @@ export class Unitflow {

for ( let i = length - 1; i >= 0; i-- ) {

// ensure it's a function
if ( typeof this.unit[ units[i] ] !== 'function' )
return console.log( `Error: Unit "${units[i]}" on flow "${flow}" is not a function. This flow execution is stopped.` ) && resolve();

const unit = this.unit[ units[i] ].bind( this ),
next = i == ( length - 1 ) ? resolve : sequence[ i + 1 ],
name = flow + ':' + units[i],
Expand Down
30 changes: 30 additions & 0 deletions test/without-dependencies.test.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Unitflow } from './../src/unitflow';
import { jest } from '@jest/globals';


describe( 'Flows without dependencies', () => {
Expand Down Expand Up @@ -73,4 +74,33 @@ describe( 'Flows without dependencies', () => {

});

test('3. Error when an unit isn\'t a function', async () => {

// console.log mock
let log_message;
const log = jest.spyOn(console, "log").mockImplementation( message => log_message = message );

const lib = new Unitflow();

expect( lib.state ).toEqual( {} );

lib.unit[ 'task 1' ] = function ( state, next ) {
state[ 'task 1' ] = 'done'
next();
}

lib.unit[ 'task 2' ] = 'This string is not a function';

lib.flow[ 'tasks' ] = [ 'task 1', 'task 2' ]

await lib.run( 'tasks' )

expect( lib.state ).toEqual({});
expect( log_message ).toBe( 'Error: Unit "task 2" on flow "tasks" is not a function. This flow execution is stopped.' )

// undo console.log mock
log.mockReset();

});

});

0 comments on commit a1f2c5c

Please sign in to comment.