├── index.js
├── src
├── NotDefinedAsyncTreeNode.js
├── Event.js
├── As.js
├── SimpleTreeNode.js
├── TreeNode.js
├── AsyncTree.js
├── AsyncObject.js
└── AsyncTreeNode.js
├── test
├── BrokenEvent.js
├── BrokenAsyncObject.js
├── SyncMaxNum.js
├── InvokedEvent.js
├── EventOfAsyncObjectWithEvent.js
├── SafeAsyncObject.js
├── BrokenTreeNode.js
├── AsyncObjectWithoutError.js
├── AsyncObjectWithArgs.js
├── StrictEqualAssertion.js
├── DeepStrictEqualAssertion.js
├── AsyncMaxNum.js
├── AsyncObjectWithAssertedErrorInDefinedCall.js
├── AsyncObjectWithAssertedErrorInCallback.js
├── AsyncObjectWithAssertedResultInCallback.js
├── AsyncObjectWithAssertedSafeErrorInDefinedCall.js
├── AsyncObjectWithAssertedSafeResultInCallback.js
├── AsyncObjectWithAssertedSafeErrorAndResultInCallback.js
├── AsyncObjectWithAssertedSafeErrorInCallback.js
└── test.js
├── .eslintrc.json
├── json
└── ParsedJSON.js
├── wall
├── ExecutedLint.js
├── ExecutedTestCoverage.js
├── ExecutedTestCoverageCheck.js
├── ExecutedTestCoverageReport.js
├── custom-calls
│ ├── executedTestCoverage.js
│ ├── executedTestCoverageReport.js
│ ├── executedLint.js
│ └── executedTestCoverageCheck.js
└── LoggedTotalCoverageByJsonSummary.js
├── fs
└── ReadDataByPath.js
├── LICENSE
├── .gitignore
├── package.json
├── .travis.yml
└── README.md
/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 |
3 | AsyncObject: require('./src/AsyncObject'),
4 | Event: require('./src/Event'),
5 | as: require('./src/As')
6 |
7 | }
8 |
--------------------------------------------------------------------------------
/src/NotDefinedAsyncTreeNode.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | class NotDefinedAsyncTreeNode {
4 | constructor () {}
5 | }
6 |
7 | module.exports = NotDefinedAsyncTreeNode
8 |
--------------------------------------------------------------------------------
/test/BrokenEvent.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const Event = require('./../src/Event')
4 |
5 | class BrokenEvent extends Event {
6 | constructor () {
7 | super()
8 | }
9 | }
10 |
11 | module.exports = BrokenEvent
12 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "standard",
3 | "rules": {
4 | "no-useless-constructor": [
5 | "off", "always"
6 | ],
7 | "standard/no-callback-literal": [
8 | "off", "always"
9 | ]
10 | }
11 | }
--------------------------------------------------------------------------------
/test/BrokenAsyncObject.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const AsyncObject = require('./../src/AsyncObject')
4 |
5 | class BrokenAsyncObject extends AsyncObject {
6 | constructor () {
7 | super()
8 | }
9 | }
10 |
11 | module.exports = BrokenAsyncObject
12 |
--------------------------------------------------------------------------------
/src/Event.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | class Event {
4 | constructor () {}
5 |
6 | // TO BE OVERRIDDEN
7 |
8 | body (...args) {
9 | throw new Error(`Method body must be overriden with arguments ${args} of the event/eventListener you call`)
10 | }
11 | }
12 |
13 | module.exports = Event
14 |
--------------------------------------------------------------------------------
/json/ParsedJSON.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const { AsyncObject } = require('./../index')
4 |
5 | // Represented result is json
6 | class ParsedJSON extends AsyncObject {
7 | constructor (string) {
8 | super(string)
9 | }
10 |
11 | syncCall () {
12 | return JSON.parse
13 | }
14 | }
15 |
16 | module.exports = ParsedJSON
17 |
--------------------------------------------------------------------------------
/test/SyncMaxNum.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const AsyncObject = require('./../src/AsyncObject')
4 |
5 | class SyncMaxNum extends AsyncObject {
6 | constructor (a, b, c) {
7 | super(a, b, c)
8 | }
9 |
10 | syncCall () {
11 | return (a, b, c) => {
12 | return Math.max(a, b, c)
13 | }
14 | }
15 | }
16 |
17 | module.exports = SyncMaxNum
18 |
--------------------------------------------------------------------------------
/test/InvokedEvent.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const AsyncObject = require('./../src/AsyncObject')
4 |
5 | class InvokedEvent extends AsyncObject {
6 | constructor (event) {
7 | super(event)
8 | }
9 |
10 | syncCall () {
11 | return (event) => {
12 | event()
13 | return event
14 | }
15 | }
16 | }
17 |
18 | module.exports = InvokedEvent
19 |
--------------------------------------------------------------------------------
/test/EventOfAsyncObjectWithEvent.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const AsyncObject = require('./../src/AsyncObject')
4 |
5 | class EventOfAsyncObjectWithEvent extends AsyncObject {
6 | constructor (event) {
7 | super(event)
8 | }
9 |
10 | syncCall () {
11 | return (event) => {
12 | return event
13 | }
14 | }
15 | }
16 |
17 | module.exports = EventOfAsyncObjectWithEvent
18 |
--------------------------------------------------------------------------------
/test/SafeAsyncObject.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const AsyncObject = require('./../src/AsyncObject')
4 |
5 | class SafeAsyncObject extends AsyncObject {
6 | constructor () {
7 | super()
8 | }
9 |
10 | syncCall () {
11 | return () => {
12 | throw new Error('safe error')
13 | }
14 | }
15 |
16 | continueAfterFail () {
17 | return true
18 | }
19 | }
20 |
21 | module.exports = SafeAsyncObject
22 |
--------------------------------------------------------------------------------
/wall/ExecutedLint.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const { AsyncObject } = require('./../index')
4 | const executedLint = require('./custom-calls/executedLint')
5 |
6 | // Represented result is process
7 | class ExecutedLint extends AsyncObject {
8 | constructor (process, ...files) {
9 | super(process, files)
10 | }
11 |
12 | asyncCall () {
13 | return executedLint
14 | }
15 | }
16 |
17 | module.exports = ExecutedLint
18 |
--------------------------------------------------------------------------------
/test/BrokenTreeNode.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const TreeNode = require('./../src/TreeNode')
4 |
5 | class BrokenTreeNode extends TreeNode {
6 | /*
7 | field: simple argument (not AsyncObject, can be Event)
8 | parent: AsyncTreeNode or NotDefinedAsyncTree
9 | position: int
10 | */
11 | constructor (field, parent, position) {
12 | super(field, parent, position)
13 | }
14 | }
15 |
16 | module.exports = BrokenTreeNode
17 |
--------------------------------------------------------------------------------
/test/AsyncObjectWithoutError.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const AsyncObject = require('./../src/AsyncObject')
4 |
5 | class AsyncObjectWithoutError extends AsyncObject {
6 | constructor () {
7 | super()
8 | }
9 |
10 | asyncCall () {
11 | return (callback) => {
12 | callback('value')
13 | }
14 | }
15 |
16 | callbackWithError () {
17 | return false
18 | }
19 | }
20 |
21 | module.exports = AsyncObjectWithoutError
22 |
--------------------------------------------------------------------------------
/fs/ReadDataByPath.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const { AsyncObject } = require('./../index')
4 | const fs = require('fs')
5 |
6 | // Represented result is buffer or string
7 | class ReadDataByPath extends AsyncObject {
8 | constructor (path, options) {
9 | super(path, options || {
10 | encoding: null,
11 | flag: 'r'
12 | });
13 | }
14 |
15 | asyncCall () {
16 | return fs.readFile
17 | }
18 | }
19 |
20 | module.exports = ReadDataByPath
21 |
--------------------------------------------------------------------------------
/test/AsyncObjectWithArgs.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const AsyncObject = require('./../src/AsyncObject')
4 |
5 | class AsyncObjectWithArgs extends AsyncObject {
6 | constructor (...args) {
7 | super(...args)
8 | }
9 |
10 | asyncCall () {
11 | return (...args) => {
12 | let callback = args[args.length - 1]
13 | return callback(null, ...args.slice(0, args.length - 1))
14 | }
15 | }
16 | }
17 |
18 | module.exports = AsyncObjectWithArgs
19 |
--------------------------------------------------------------------------------
/wall/ExecutedTestCoverage.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const { AsyncObject } = require('./../index')
4 | const executedTestCoverage = require('./custom-calls/executedTestCoverage')
5 |
6 | // Represented result is process
7 | class ExecutedTestCoverage extends AsyncObject {
8 | constructor (process, file) {
9 | super(process, file)
10 | }
11 |
12 | asyncCall () {
13 | return executedTestCoverage
14 | }
15 | }
16 |
17 | module.exports = ExecutedTestCoverage
18 |
--------------------------------------------------------------------------------
/test/StrictEqualAssertion.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const AsyncObject = require('./../src/AsyncObject')
4 | const assert = require('assert')
5 |
6 | class StrictEqualAssertion extends AsyncObject {
7 | constructor (actual, expected) {
8 | super(actual, expected)
9 | }
10 |
11 | syncCall () {
12 | return (actual, expected) => {
13 | assert.strictEqual(actual, expected)
14 | return actual
15 | }
16 | }
17 | }
18 |
19 | module.exports = StrictEqualAssertion
20 |
--------------------------------------------------------------------------------
/test/DeepStrictEqualAssertion.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const AsyncObject = require('./../src/AsyncObject')
4 | const assert = require('assert')
5 |
6 | class DeepStrictEqualAssertion extends AsyncObject {
7 | constructor (actual, expected) {
8 | super(actual, expected)
9 | }
10 |
11 | syncCall () {
12 | return (actual, expected) => {
13 | assert.deepStrictEqual(actual, expected)
14 | return actual
15 | }
16 | }
17 | }
18 |
19 | module.exports = DeepStrictEqualAssertion
20 |
--------------------------------------------------------------------------------
/wall/ExecutedTestCoverageCheck.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const { AsyncObject } = require('./../index')
4 | const executedTestCoverageCheck = require('./custom-calls/executedTestCoverageCheck')
5 |
6 | // Represented result is process
7 | class ExecutedTestCoverageCheck extends AsyncObject {
8 | constructor (process, options) {
9 | super(process, options)
10 | }
11 |
12 | asyncCall () {
13 | return executedTestCoverageCheck
14 | }
15 | }
16 |
17 | module.exports = ExecutedTestCoverageCheck
18 |
--------------------------------------------------------------------------------
/src/As.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const AsyncObject = require('./AsyncObject')
4 |
5 | class As extends AsyncObject {
6 | constructor (key) {
7 | super(key)
8 | }
9 |
10 | syncCall () {
11 | return (key) => {
12 | let result = this.cache[key]
13 | if (result === undefined) {
14 | throw new Error(`There is no value that is cached with key ${key}`)
15 | }
16 | return result
17 | }
18 | }
19 | }
20 |
21 | module.exports = (key) => {
22 | return new As(key)
23 | }
24 |
--------------------------------------------------------------------------------
/wall/ExecutedTestCoverageReport.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const { AsyncObject } = require('./../index')
4 | const executedTestCoverageReport = require('./custom-calls/executedTestCoverageReport')
5 |
6 | // Represented result is process
7 | class ExecutedTestCoverageReport extends AsyncObject {
8 | constructor (process, format) {
9 | super(process, format || 'text')
10 | }
11 |
12 | asyncCall () {
13 | return executedTestCoverageReport
14 | }
15 | }
16 |
17 | module.exports = ExecutedTestCoverageReport
18 |
--------------------------------------------------------------------------------
/test/AsyncMaxNum.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const AsyncObject = require('./../src/AsyncObject')
4 |
5 | class AsyncMaxNum extends AsyncObject {
6 | constructor (a, b, c) {
7 | super(a, b, c)
8 | }
9 |
10 | /**
11 | It's not an operation with I/O, it's just silly example
12 | **/
13 | asyncCall () {
14 | return (a, b, c, callback) => {
15 | callback(Math.max(a, b, c))
16 | }
17 | }
18 |
19 | callbackWithError () {
20 | return false
21 | }
22 | }
23 |
24 | module.exports = AsyncMaxNum
25 |
--------------------------------------------------------------------------------
/wall/custom-calls/executedTestCoverage.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const spawn = require('child_process').spawn
4 |
5 | module.exports = (process, testFile, callback) => {
6 | const testCoverage = spawn('./node_modules/.bin/nyc', ['node', testFile], {
7 | stdio: [process.stdin, process.stdout, process.stderr]
8 | })
9 | testCoverage.on('close', (code) => {
10 | if (code === 0) {
11 | callback(null, process)
12 | } else {
13 | callback(new Error(`test coverage failed with code ${code}`))
14 | }
15 | })
16 | }
17 |
--------------------------------------------------------------------------------
/wall/custom-calls/executedTestCoverageReport.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const spawn = require('child_process').spawn
4 |
5 | module.exports = (process, format, callback) => {
6 | const testCoverage = spawn('./node_modules/.bin/nyc', ['report', '--reporter', format], {
7 | stdio: [process.stdin, process.stdout, process.stderr]
8 | })
9 | testCoverage.on('close', (code) => {
10 | if (code === 0) {
11 | callback(null, process)
12 | } else {
13 | callback(new Error(`test coverage report failed with code ${code}`))
14 | }
15 | })
16 | }
17 |
--------------------------------------------------------------------------------
/src/SimpleTreeNode.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const TreeNode = require('./TreeNode')
4 |
5 | class SimpleTreeNode extends TreeNode {
6 | /*
7 | field: simple argument (not AsyncObject, can be Event)
8 | parent: AsyncTreeNode or NotDefinedAsyncTree
9 | position: int
10 | */
11 | constructor (field, parent, position) {
12 | super(field, parent, position)
13 | }
14 |
15 | // PUBLIC
16 |
17 | call () {
18 | super.callParent(this.field)
19 | }
20 |
21 | isLeaf () {
22 | return true
23 | }
24 | }
25 |
26 | module.exports = SimpleTreeNode
27 |
--------------------------------------------------------------------------------
/test/AsyncObjectWithAssertedErrorInDefinedCall.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const AsyncObject = require('./../src/AsyncObject')
4 | const assert = require('assert')
5 |
6 | class AsyncObjectWithAssertedErrorInDefinedCall extends AsyncObject {
7 | constructor () {
8 | super()
9 | }
10 |
11 | asyncCall () {
12 | return () => {
13 | throw new Error('async error')
14 | }
15 | }
16 |
17 | onError (error) {
18 | assert.deepStrictEqual(error, new Error('async error'))
19 | }
20 | }
21 |
22 | module.exports = AsyncObjectWithAssertedErrorInDefinedCall
23 |
--------------------------------------------------------------------------------
/test/AsyncObjectWithAssertedErrorInCallback.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const AsyncObject = require('./../src/AsyncObject')
4 | const assert = require('assert')
5 |
6 | class AsyncObjectWithAssertedErrorInCallback extends AsyncObject {
7 | constructor () {
8 | super()
9 | }
10 |
11 | asyncCall () {
12 | return (callback) => {
13 | return callback(new Error('async error'))
14 | }
15 | }
16 |
17 | onError (error) {
18 | assert.deepStrictEqual(error, new Error('async error'))
19 | }
20 | }
21 |
22 | module.exports = AsyncObjectWithAssertedErrorInCallback
23 |
--------------------------------------------------------------------------------
/test/AsyncObjectWithAssertedResultInCallback.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const AsyncObject = require('./../src/AsyncObject')
4 | const assert = require('assert')
5 |
6 | class AsyncObjectWithAssertedResultInCallback extends AsyncObject {
7 | constructor () {
8 | super()
9 | }
10 |
11 | asyncCall () {
12 | return (callback) => {
13 | return callback(null, 'result')
14 | }
15 | }
16 |
17 | onResult (result) {
18 | assert.strictEqual(result, 'result')
19 | return result
20 | }
21 | }
22 |
23 | module.exports = AsyncObjectWithAssertedResultInCallback
24 |
--------------------------------------------------------------------------------
/wall/custom-calls/executedLint.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const spawn = require('child_process').spawn
4 |
5 | module.exports = (process, files, callback) => {
6 | const lint = spawn('./node_modules/.bin/eslint', files, {
7 | stdio: [process.stdin, process.stdout, process.stderr]
8 | })
9 | lint.on('close', (code) => {
10 | if (code === 0) {
11 | console.log('\x1b[32m%s\x1b[0m', `lint has executed successfully: everything is ok for ${files.join(', ')}`)
12 | callback(null, process)
13 | } else {
14 | callback(new Error(`lint failed for ${files.join(', ')} with code ${code}`))
15 | }
16 | })
17 | }
18 |
--------------------------------------------------------------------------------
/test/AsyncObjectWithAssertedSafeErrorInDefinedCall.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const AsyncObject = require('./../src/AsyncObject')
4 | const assert = require('assert')
5 |
6 | class AsyncObjectWithAssertedSafeErrorInDefinedCall extends AsyncObject {
7 | constructor () {
8 | super()
9 | }
10 |
11 | asyncCall () {
12 | return () => {
13 | throw new Error('safe async error')
14 | }
15 | }
16 |
17 | onErrorAndResult (error) {
18 | assert.deepStrictEqual(error, new Error('safe async error'))
19 | }
20 |
21 | continueAfterFail () {
22 | return true
23 | }
24 | }
25 |
26 | module.exports = AsyncObjectWithAssertedSafeErrorInDefinedCall
27 |
--------------------------------------------------------------------------------
/wall/custom-calls/executedTestCoverageCheck.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const spawn = require('child_process').spawn
4 |
5 | module.exports = (process, { lines, functions, branches }, callback) => {
6 | const testCoverage = spawn(
7 | './node_modules/.bin/nyc',
8 | ['check-coverage', '--lines', lines || 90, '--functions', functions || 90, '--branches', branches || 90],
9 | { stdio: [process.stdin, process.stdout, process.stderr] }
10 | )
11 | testCoverage.on('close', (code) => {
12 | if (code === 0) {
13 | callback(null, process)
14 | } else {
15 | callback(new Error(`test coverage configuration failed with code ${code}`))
16 | }
17 | })
18 | }
19 |
--------------------------------------------------------------------------------
/wall/LoggedTotalCoverageByJsonSummary.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const { AsyncObject } = require('./../index')
4 |
5 | // Represented result is logged string
6 | class LoggedTotalCoverageByJsonSummary extends AsyncObject {
7 | constructor (json, formula) {
8 | super(json, formula)
9 | }
10 |
11 | syncCall () {
12 | return (json, formula) => {
13 | let totalValue = formula(json.total.lines.pct, json.total.statements.pct, json.total.functions.pct, json.total.branches.pct)
14 | let report = `Total coverage: ${totalValue}%`
15 | console.log(report)
16 | return report
17 | }
18 | }
19 | }
20 |
21 | module.exports = LoggedTotalCoverageByJsonSummary
22 |
--------------------------------------------------------------------------------
/test/AsyncObjectWithAssertedSafeResultInCallback.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const AsyncObject = require('./../src/AsyncObject')
4 | const assert = require('assert')
5 |
6 | class AsyncObjectWithAssertedSafeResultInCallback extends AsyncObject {
7 | constructor () {
8 | super()
9 | }
10 |
11 | asyncCall () {
12 | return (callback) => {
13 | return callback(null, 'result')
14 | }
15 | }
16 |
17 | onErrorAndResult (error, result) {
18 | assert.strictEqual(error, null)
19 | assert.strictEqual(result, 'result')
20 | return result
21 | }
22 |
23 | continueAfterFail () {
24 | return true
25 | }
26 | }
27 |
28 | module.exports = AsyncObjectWithAssertedSafeResultInCallback
29 |
--------------------------------------------------------------------------------
/test/AsyncObjectWithAssertedSafeErrorAndResultInCallback.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const AsyncObject = require('./../src/AsyncObject')
4 | const assert = require('assert')
5 |
6 | class AsyncObjectWithAssertedSafeErrorAndResultInCallback extends AsyncObject {
7 | constructor () {
8 | super()
9 | }
10 |
11 | asyncCall () {
12 | return (callback) => {
13 | return callback(null, 'result')
14 | }
15 | }
16 |
17 | onErrorAndResult (error, result) {
18 | assert.strictEqual(super.onErrorAndResult(error, result), 'result')
19 | return result
20 | }
21 |
22 | continueAfterFail () {
23 | return true
24 | }
25 | }
26 |
27 | module.exports = AsyncObjectWithAssertedSafeErrorAndResultInCallback
28 |
--------------------------------------------------------------------------------
/test/AsyncObjectWithAssertedSafeErrorInCallback.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const AsyncObject = require('./../src/AsyncObject')
4 | const assert = require('assert')
5 |
6 | class AsyncObjectWithAssertedSafeErrorInCallback extends AsyncObject {
7 | constructor () {
8 | super()
9 | }
10 |
11 | asyncCall () {
12 | return (callback) => {
13 | return callback(new Error('safe async error'), 'result')
14 | }
15 | }
16 |
17 | onErrorAndResult (error, result) {
18 | assert.deepStrictEqual(error, new Error('safe async error'))
19 | assert.strictEqual(result, 'result')
20 | return error
21 | }
22 |
23 | continueAfterFail () {
24 | return true
25 | }
26 | }
27 |
28 | module.exports = AsyncObjectWithAssertedSafeErrorInCallback
29 |
--------------------------------------------------------------------------------
/src/TreeNode.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | /* abstract class */
4 |
5 | class TreeNode {
6 | /*
7 | field: just some value (argument), also can be Event
8 | parent: AsyncTreeNode
9 | position: int
10 | */
11 | constructor (field, parent, position) {
12 | this.field = field
13 | this.parent = parent
14 | this.position = position
15 | }
16 |
17 | // TO BE OVERRIDEN
18 |
19 | call (result) {
20 | result = result || ''
21 | throw new Error(`call must be overridden and insert result ${result} into parent node`)
22 | }
23 |
24 | isLeaf () {
25 | throw new Error('isLeaf must be overridden')
26 | }
27 |
28 | // NOT ALLOWED TO BE OVERRIDDEN
29 |
30 | callParent (result) {
31 | this.parent.insertArgumentResult(this.position, result)
32 | if (this.parent.readyToBeInvoked()) {
33 | this.parent.call()
34 | }
35 | }
36 | }
37 |
38 | module.exports = TreeNode
39 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Guseyn Ismayylov
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | .DS_Store
8 |
9 | # Runtime data
10 | pids
11 | *.pid
12 | *.seed
13 | *.pid.lock
14 |
15 | # Directory for instrumented libs generated by jscoverage/JSCover
16 | lib-cov
17 |
18 | # Coverage directory used by tools like istanbul
19 | coverage
20 |
21 | # nyc test coverage
22 | .nyc_output
23 |
24 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
25 | .grunt
26 |
27 | # Bower dependency directory (https://bower.io/)
28 | bower_components
29 |
30 | # node-waf configuration
31 | .lock-wscript
32 |
33 | # Compiled binary addons (http://nodejs.org/api/addons.html)
34 | build/Release
35 |
36 | # Dependency directories
37 | node_modules/
38 | jspm_packages/
39 |
40 | # Typescript v1 declaration files
41 | typings/
42 |
43 | # Optional npm cache directory
44 | .npm
45 |
46 | # Optional eslint cache
47 | .eslintcache
48 |
49 | # Optional REPL history
50 | .node_repl_history
51 |
52 | # Output of 'npm pack'
53 | *.tgz
54 |
55 | # Yarn Integrity file
56 | .yarn-integrity
57 |
58 | # dotenv environment variables file
59 | .env
60 |
61 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@cuties/cutie",
3 | "version": "1.7.6",
4 | "description": "Cutie is a library with beautiful abstractions and primitives that make your asynchronous code in Node simple and declarative.",
5 | "main": "index.js",
6 | "directories": {
7 | "test": "test"
8 | },
9 | "scripts": {
10 | "test": "node test/test",
11 | "build": "node build"
12 | },
13 | "repository": {
14 | "type": "git",
15 | "url": "git+https://github.com/Guseyn/cutie.git"
16 | },
17 | "keywords": [
18 | "cutie",
19 | "async",
20 | "object",
21 | "tree",
22 | "abstractions",
23 | "oop",
24 | "desing",
25 | "guseyn"
26 | ],
27 | "author": "guseyn",
28 | "license": "MIT",
29 | "bugs": {
30 | "url": "https://github.com/Guseyn/cutie/issues"
31 | },
32 | "homepage": "https://github.com/Guseyn/cutie#readme",
33 | "devDependencies": {
34 | "eslint": "^5.13.0",
35 | "eslint-config-standard": "^12.0.0",
36 | "eslint-plugin-import": "^2.16.0",
37 | "eslint-plugin-node": "^8.0.1",
38 | "eslint-plugin-promise": "^4.0.1",
39 | "eslint-plugin-standard": "^4.0.0",
40 | "nyc": "^14.1.1"
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/AsyncTree.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const SimpleTreeNode = require('./SimpleTreeNode')
4 | const AsyncTreeNode = require('./AsyncTreeNode')
5 | const NotDefinedAsyncTreeNode = require('./NotDefinedAsyncTreeNode')
6 |
7 | class AsyncTree {
8 | /*
9 | rootField: AsyncObject
10 | */
11 | constructor (rootField) {
12 | this.rootField = rootField
13 | this.nodes = []
14 | }
15 |
16 | // PUBLIC
17 |
18 | create () {
19 | this.createAsyncTreeNode(this.rootField, new NotDefinedAsyncTreeNode(), 0)
20 | return this
21 | }
22 |
23 | call () {
24 | let leaves = this.nodes.filter(node => {
25 | return node.isLeaf()
26 | })
27 | leaves.forEach(leaf => {
28 | leaf.call()
29 | })
30 | }
31 |
32 | // PRIVATE
33 |
34 | createChildNodes (field, parent) {
35 | field.iterateArgs((argAsField, index, isAsyncObject, isEvent) => {
36 | if (isAsyncObject) {
37 | this.createAsyncTreeNode(argAsField, parent, index)
38 | } else if (isEvent) {
39 | this.createSimpleTreeNode((...eventArgs) => {
40 | argAsField.body(...eventArgs)
41 | }, parent, index)
42 | } else {
43 | this.createSimpleTreeNode(argAsField, parent, index)
44 | }
45 | })
46 | }
47 |
48 | createAsyncTreeNode (field, parent, index) {
49 | let asyncTreeNode = new AsyncTreeNode(field, parent, index)
50 | this.nodes.push(asyncTreeNode)
51 | this.createChildNodes(field, asyncTreeNode)
52 | }
53 |
54 | createSimpleTreeNode (field, parent, index) {
55 | let treeNode = new SimpleTreeNode(field, parent, index)
56 | this.nodes.push(treeNode)
57 | }
58 | }
59 |
60 | module.exports = AsyncTree
61 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - lts/*
4 | install: |-
5 | npm ci
6 | npm install -g codecov
7 | script: |-
8 | npm run build || travis_terminate 1
9 | ./node_modules/.bin/nyc report --reporter=lcov
10 | codecov
11 | log=$(git log $TRAVIS_COMMIT_RANGE --oneline)
12 | echo $'**Change log:**
'${log//$'\n'/
}
13 | branches:
14 | only:
15 | - master
16 | before_deploy: |-
17 | log=$(git log $TRAVIS_COMMIT_RANGE --oneline)
18 | export TAG=$'v'$TRAVIS_COMMIT_MESSAGE
19 | export BODY=$'**Change log:**
'${log//$'\n'/
}
20 | deploy:
21 | - provider: releases
22 | skip_cleanup: true
23 | api_key:
24 | secure: NyxY7T4REcxEhpAj/VMD4hfv/0Id3yVpo/dTFiVtQhu1JX3ruJ6C6KlGa3k+aHV3Z5Zbwmfm5+azMq+l7Kone/BI7V2m1PbwMRUcEJdBVPfkZKfsIH7rN/7cvJas2pKXCCC/QFooMsFjUebRTZM8KTGxxLwcH3iNwKd2H8fAsCQak0uvjJSbsWmau5L/SLJFoOUpMLDhpH94I/wzZt2vA69A1ziUaMQpYZYV9PM1DwJbnCqtlncwQbmodFsANu7ZlExEEfvPbaLoR5F3zJLd+9aSpRAjfdFNCqwXK3nwyWmMt1ban9EtBjW+OL9ZnBA44Lqm3iZc2b9GCBUX/35XaWBCfhueOA//Eo0QFIV8q3g6iUk/M/PoickBEtax956t/Dkg8vGd914TZ1eYlUa+VNsGIk4iMbtpPXDlFDithplxUgZEY4/m3yMPA1v7KXtjfJ933RqG1Rvym42JsmEDW57i9zfcEhS45ZnxkrJl1hPwT8rEAhFtpkXEBPCQXiHnopXDe2e9WzibkhinIT4wPydFtV62gsAhPJ8YW52v8ZlCCK+XDNi66SYGxXtwm6gpKOsafyTtUOOeDeq1NDRf0RCO3lFYC6z/k/8xrKRVqmYxc/i7efokqBAtMzebGuXEyZL4SMRIXM6ZpDmcGPdxVQKEpoN74oKP39Ji+rhMRbE=
25 | file: README.md
26 | name: Release $TRAVIS_COMMIT_MESSAGE
27 | tag_name: $TAG
28 | body: $BODY
29 | on:
30 | repo: Guseyn/cutie
31 | branch:
32 | - master
33 | condition: $TRAVIS_COMMIT_MESSAGE =~ ^([0-9]{1,3}).([0-9]{1,3}).([0-9]{1,3})$
34 | - provider: npm
35 | email: guseynism@gmail.com
36 | api_key:
37 | secure: QAIyol5sH60ZcPCWQAF+g6IrCE9xbxnTmZ+3Q+UecFPsZGS3rffL/toMbVptunYGPsavyiIq4mtZ8+8RLWBMdL89EaRiBpKYCK+Tmo7coaGhwh5sSWXFIF63mfrxcnBC7B6+yKawmXAQm99z6Kd4ShdbANSqt4UzQOwFUHHQFWc2QyvGzdY/ZJiRAZeNguAh3J705IA7fohzeGycseBoFf4fB24md4JS/XaqgsZ+Rf0Eio+xV1bMFT4Aibpx+gD0r8wY1X/aLzfsRXAPnokz7RJowzg4ksSDNoIJNrh0RRRdbu2XP9MiocwxdkrmbQPvZI496mJowMTXSbv2L3OPTK7g4VJbfSNfQyujhyVfa2mEgE0vrY4UMavvsBgKM2Y5FpnXzNCG7bjGfXzxgu9gYriiVNJGjFQav1Ia39ndsbWtiLLMTAyyxgL7/A42YmzwQKwqmc19+WJFEg2aD63vRnHIwCGsLbUevq26gR+f6376LxWJthjkaLfiOrl+KBKSh7yRGhc4tmJ0pZ2zLkQPKZvArfJMClIWJMb2L9v3gAX56Lw7LHkPOyrbPBO3kjSFzAStJtisBrZx1RY+HUia9TtNTGc/1pPXk9Z7STCiEiuC/7/ZiiuznrJTvdPBgZBD+lwoKmzFKRG2nYc0j2Ea0DBaZzu2b9pHhJ94rteATe0=
38 | on:
39 | repo: Guseyn/cutie
40 | branch:
41 | - master
42 | condition: $TRAVIS_COMMIT_MESSAGE =~ ^([0-9]{1,3}).([0-9]{1,3}).([0-9]{1,3})$
43 |
--------------------------------------------------------------------------------
/src/AsyncObject.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const AsyncTree = require('./AsyncTree')
4 |
5 | /* abstract class */
6 |
7 | class AsyncObject {
8 | /*
9 | args: any type (including AsyncObject)
10 | */
11 | constructor (...args) {
12 | this.args = args
13 | this.cache = {}
14 | this.next = undefined
15 | this.asKey = undefined
16 | }
17 |
18 | // TO BE OVERRIDDEN
19 |
20 | asyncCall () {
21 | throw new Error('asyncCall or syncCall must be defined')
22 | }
23 |
24 | syncCall () {
25 | throw new Error('asyncCall or syncCall must be defined')
26 | }
27 |
28 | onError (error) {
29 | throw error
30 | }
31 |
32 | onResult (result) {
33 | return result
34 | }
35 |
36 | /*
37 | Works only if this.continueAfterFail returns true
38 | (in that case this.onError and this.onResult will be ignored),
39 | */
40 | onErrorAndResult (error, result) {
41 | return error || result
42 | }
43 |
44 | /*
45 | If it returns true, then this.onError and this.onResult will be ignored,
46 | and the represented result of this object
47 | will be returned by this.onErrorAndResult.
48 | */
49 | continueAfterFail () {
50 | return false
51 | }
52 |
53 | callbackWithError () {
54 | return true
55 | }
56 |
57 | // PUBLIC API
58 |
59 | call () {
60 | this.propagateCache(this)
61 | new AsyncTree(this).create().call()
62 | }
63 |
64 | after (asyncObject) {
65 | this.next = asyncObject
66 | return this
67 | }
68 |
69 | as (key) {
70 | this.asKey = key
71 | return this
72 | }
73 |
74 | // NOT ALLOWED TO BE OVERRIDDEN
75 |
76 | iterateArgs (func) {
77 | this.args.forEach((arg, index) => {
78 | func(arg, index, this.isAsyncObject(arg), this.isEvent(arg))
79 | })
80 | }
81 |
82 | hasNoArgs () {
83 | return this.args.length === 0
84 | }
85 |
86 | readyToBeInvoked (readyResultsNum) {
87 | return this.args.length === readyResultsNum
88 | }
89 |
90 | callNextTreeIfExists () {
91 | if (this.next) {
92 | this.propagateCache(this.next)
93 | new AsyncTree(this.next).create().call()
94 | }
95 | }
96 |
97 | propagateCache (arg) {
98 | if (this.isAsyncObject(arg)) {
99 | arg.withCache(this.cache)
100 | arg.iterateArgs(arg => this.propagateCache(arg))
101 | }
102 | }
103 |
104 | withCache (cache) {
105 | this.cache = cache
106 | return this
107 | }
108 |
109 | saveValueIntoCacheIfNeeded (value) {
110 | if (this.asKey) {
111 | if (this.cache[this.asKey]) {
112 | throw new Error(`There is already value that is cached with key ${this.asKey}`)
113 | }
114 | this.cache[this.asKey] = value
115 | }
116 | return this
117 | }
118 |
119 | isAsyncObject (arg) {
120 | return this.classChain(arg).indexOf('AsyncObject') !== -1
121 | }
122 |
123 | isEvent (arg) {
124 | return this.classChain(arg).indexOf('Event') !== -1
125 | }
126 |
127 | classChain (obj, chain) {
128 | if (!chain) {
129 | chain = []
130 | }
131 | if (typeof obj === 'function') {
132 | if (!obj.name || obj === Object) {
133 | return chain
134 | }
135 | return this.classChain(Object.getPrototypeOf(obj), chain.concat(obj.name))
136 | }
137 | if (typeof obj === 'object' && obj !== null) {
138 | return this.classChain(obj.constructor, chain)
139 | }
140 | return chain
141 | }
142 | }
143 |
144 | module.exports = AsyncObject
145 |
--------------------------------------------------------------------------------
/src/AsyncTreeNode.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const TreeNode = require('./TreeNode')
4 |
5 | class AsyncTreeNode extends TreeNode {
6 | /*
7 | field: AsyncObject
8 | parent: AsyncTreeNode or NotDefinedAsyncTree
9 | position: int
10 | */
11 | constructor (field, parent, position) {
12 | super(field, parent, position)
13 | this.argResults = []
14 | this.readyResultsNum = 0
15 | }
16 |
17 | // PUBLIC
18 |
19 | call () {
20 | let args = this.argResults
21 | try {
22 | let asyncCall = this.field.asyncCall()
23 | if (this.field.callbackWithError()) {
24 | this.invokeAsyncCallWithError(asyncCall, ...args)
25 | } else {
26 | this.invokeAsyncCallWithoutError(asyncCall, ...args)
27 | }
28 | } catch (error) {
29 | if (error.message !== 'asyncCall or syncCall must be defined') {
30 | if (this.field.continueAfterFail()) {
31 | this.field.onErrorAndResult(error)
32 | } else {
33 | this.field.onError(error)
34 | }
35 | } else {
36 | let syncCall = this.field.syncCall()
37 | this.invokeSyncCall(syncCall, ...args)
38 | }
39 | }
40 | }
41 |
42 | isLeaf () {
43 | return this.field.hasNoArgs()
44 | }
45 |
46 | readyToBeInvoked () {
47 | return this.field.readyToBeInvoked(this.readyResultsNum)
48 | }
49 |
50 | hasParent () {
51 | return this.parent instanceof AsyncTreeNode
52 | }
53 |
54 | insertArgumentResult (position, result) {
55 | this.argResults[position] = result
56 | this.readyResultsNum += 1
57 | }
58 |
59 | // PRIVATE
60 |
61 | invokeAsyncCallWithError (asyncCall, ...args) {
62 | asyncCall(...args, (error, ...results) => {
63 | if (!this.processedError(error, ...results)) {
64 | this.processedResult(...results)
65 | }
66 | })
67 | }
68 |
69 | invokeAsyncCallWithoutError (asyncCall, ...args) {
70 | asyncCall(...args, (...results) => {
71 | this.processedResult(...results)
72 | })
73 | }
74 |
75 | invokeSyncCall (syncCall, ...args) {
76 | try {
77 | let syncCallResult = syncCall(...args)
78 | this.processedResult(syncCallResult)
79 | } catch (error) {
80 | this.processedError(error)
81 | }
82 | }
83 |
84 | processedError (error, ...results) {
85 | let isProcessed = false
86 | // It's not possible to get rid of null here :(
87 | if (error != null) {
88 | if (this.field.continueAfterFail()) {
89 | let totalResult = this.field.onErrorAndResult(error, ...results)
90 | this.field.saveValueIntoCacheIfNeeded(totalResult)
91 | if (this.hasParent()) {
92 | super.callParent(totalResult)
93 | } else {
94 | this.field.callNextTreeIfExists()
95 | }
96 | } else {
97 | this.field.onError(error)
98 | }
99 | isProcessed = true
100 | }
101 | return isProcessed
102 | }
103 |
104 | processedResult (...results) {
105 | let totalResult
106 | if (this.field.continueAfterFail()) {
107 | totalResult = this.field.onErrorAndResult(null, ...results)
108 | } else {
109 | totalResult = this.field.onResult(...results)
110 | }
111 | this.field.saveValueIntoCacheIfNeeded(totalResult)
112 | if (this.hasParent()) {
113 | super.callParent(totalResult)
114 | } else {
115 | this.field.callNextTreeIfExists()
116 | }
117 | return true
118 | }
119 | }
120 |
121 | module.exports = AsyncTreeNode
122 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | [](https://npmjs.org/package/@cuties/cutie)
4 | [](https://travis-ci.org/Guseyn/cutie)
5 | [](https://codecov.io/gh/Guseyn/cutie)
6 |
7 | **Cutie** is a lightweight library that implements [Async Tree Pattern](https://guseyn.com/pdf/Async_Tree_Pattern.pdf).
8 |
9 | # Motivation
10 | Let's say we want to read content from a file and write it to another one. And all these operations are asynchronous, of course. So, instead of writing something like this:
11 | ```js
12 | fs.readFile('./../file1.txt', 'utf8', (err, result) => {
13 | if (err != null) {
14 | throw err
15 | }
16 |
17 | fs.writeFile('./../file2.txt', result, (err) => {
18 | if (err != null) {
19 | throw err
20 | }
21 | })
22 | })
23 | ```
24 | we can design our code in the following style:
25 | ```js
26 | new WrittenFile(
27 | './../file2.txt',
28 | new ReadDataByPath('./../file1.txt', 'utf8')
29 | ).call()
30 | ```
31 | # How to use
32 | You can use Cutie as a dependency via npm:
33 | `npm install @cuties/cutie`
34 | ```js
35 | const AsyncObject = require('@cuties/cutie').AsyncObject
36 | const fs = require('fs')
37 |
38 | // Represents file as path
39 | class WrittenFile extends AsyncObject {
40 | constructor (path, content) {
41 | super(path, content)
42 | }
43 |
44 | asyncCall () {
45 | return (path, content, callback) => {
46 | this.path = path
47 | fs.writeFile(path, content, callback)
48 | }
49 | }
50 |
51 | onResult() {
52 | return this.path
53 | }
54 | }
55 | ```
56 | ```js
57 | const AsyncObject = require('@cuties/cutie').AsyncObject
58 | const fs = require('fs')
59 |
60 | // Represents buffer or string
61 | class ReadDataByPath extends AsyncObject {
62 | constructor (path, encoding) {
63 | super(path, encoding);
64 | }
65 |
66 | asyncCall () {
67 | return fs.readFile
68 | }
69 | }
70 | ```
71 | AsyncObject also provides methods `OnResult` and `OnError`, so that you can process the result (it's provided by callback by default) from async call and handle an error in the specific way (error is being thrown by default).
72 |
73 | Let's say we want to read a json file and parse all information from there. Cutie provides two ways. First of them is just to create `ParsedJSON` async object like this:
74 | ```js
75 | const AsyncObject = require('@cuties/cutie').AsyncObject;
76 | const fs = require('fs');
77 |
78 | class ParsedJSON extends AsyncObject {
79 | constructor (path, encoding) {
80 | super(path, encoding)
81 | }
82 |
83 | asyncCall () {
84 | return fs.readFile
85 | }
86 |
87 | onResult (result) {
88 | return JSON.parse(result)
89 | }
90 | }
91 |
92 | // usage
93 | new ParsedJSON('./../file.txt', 'utf8').call()
94 | ```
95 | `ParsedJSON` also could be designed like this:
96 | ```js
97 | const fs = require('fs')
98 | const ReadDataByPath = require('./ReadDataByPath')
99 |
100 | class ParsedJSON extends ReadDataByPath {
101 | constructor (path, encoding) {
102 | super(path, encoding)
103 | }
104 |
105 | onResult (result) {
106 | return JSON.parse(result)
107 | }
108 | }
109 |
110 | // usage
111 | new ParsedJSON('./../file.txt', 'utf8').call();
112 | ```
113 | Or you can use `ReadDataByPath` with `ParsedJSON` that looks like this:
114 | ```js
115 | const AsyncObject = require('@cuties/cutie').AsyncObject
116 | const fs = require('fs')
117 | const ReadDataByPath = require('./ReadDataByPath')
118 |
119 | class ParsedJSON extends AsyncObject {
120 | constructor (text) {
121 | super(text)
122 | }
123 |
124 | /*
125 | you can't call here async operations with I/O
126 | */
127 | syncCall () {
128 | return JSON.parse
129 | }
130 | }
131 |
132 | // usage
133 | new ParsedJSON(
134 | new ReadDataByPath('./../file.txt', 'utf8')
135 | ).call()
136 | ```
137 | ## Read more
138 |
139 | 1. [Async Tree Pattern](https://guseyn.com/pdf/Async_Tree_Pattern.pdf)
140 | 2. [Async Objects instead of Async Calls](https://guseyn.com/posts/async-objects-instead-of-async-calls)
141 |
142 | ## Run test
143 |
144 | `npm test`
145 |
146 | ## Run build
147 |
148 | `npm run build`
149 |
150 | ## Libraries that use cutie
151 |
152 | [node-test-executor](https://github.com/Guseyn/node-test-executor), [cutie-is](https://github.com/Guseyn/cutie-is), [cutie-assert](https://github.com/Guseyn/cutie-assert), [cutie-fs](https://github.com/Guseyn/cutie-fs), [cutie-http](https://github.com/Guseyn/cutie-http), [cutie-https](https://github.com/Guseyn/cutie-http), [cutie-rest](https://github.com/Guseyn/cutie-rest), [cutie-buffer](https://github.com/Guseyn/cutie-buffer), [cutie-error](https://github.com/Guseyn/cutie-error), [cutie-date](https://github.com/Guseyn/cutie-date), [cutie-json](https://github.com/Guseyn/cutie-json), [cutie-event](https://github.com/Guseyn/cutie-event), [cutie-stream](https://github.com/Guseyn/cutie-stream), [cutie-object](https://github.com/Guseyn/cutie-object), [cutie-process](https://github.com/Guseyn/cutie-process), [cutie-iterator](https://github.com/Guseyn/cutie-iterator), [cutie-path](https://github.com/Guseyn/cutie-path), [cutie-if-else](https://github.com/Guseyn/cutie-if-else), [cutie-cluster](https://github.com/Guseyn/cutie-cluster), [page-static-generator](https://github.com/Guseyn/page-static-generator) and many others.
153 |
--------------------------------------------------------------------------------
/test/test.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const assert = require('assert')
4 | const AsyncObject = require('./../src/AsyncObject')
5 | const Event = require('./../src/Event')
6 | const as = require('./../src/As')
7 | const StrictEqualAssertion = require('./StrictEqualAssertion')
8 | const DeepStrictEqualAssertion = require('./DeepStrictEqualAssertion')
9 | const AsyncMaxNum = require('./AsyncMaxNum')
10 | const SyncMaxNum = require('./AsyncMaxNum')
11 | const BrokenAsyncObject = require('./BrokenAsyncObject')
12 | const SafeAsyncObject = require('./SafeAsyncObject')
13 | const AsyncObjectWithoutError = require('./AsyncObjectWithoutError')
14 | const EventOfAsyncObjectWithEvent = require('./EventOfAsyncObjectWithEvent')
15 | const BrokenEvent = require('./BrokenEvent')
16 | const InvokedEvent = require('./InvokedEvent')
17 | const BrokenTreeNode = require('./BrokenTreeNode')
18 | const AsyncObjectWithArgs = require('./AsyncObjectWithArgs')
19 | const AsyncObjectWithAssertedErrorInCallback = require('./AsyncObjectWithAssertedErrorInCallback')
20 | const AsyncObjectWithAssertedSafeErrorInCallback = require('./AsyncObjectWithAssertedSafeErrorInCallback')
21 | const AsyncObjectWithAssertedErrorInDefinedCall = require('./AsyncObjectWithAssertedErrorInDefinedCall')
22 | const AsyncObjectWithAssertedSafeErrorInDefinedCall = require('./AsyncObjectWithAssertedSafeErrorInDefinedCall')
23 | const AsyncObjectWithAssertedResultInCallback = require('./AsyncObjectWithAssertedResultInCallback')
24 | const AsyncObjectWithAssertedSafeResultInCallback = require('./AsyncObjectWithAssertedSafeResultInCallback')
25 | const AsyncObjectWithAssertedSafeErrorAndResultInCallback = require('./AsyncObjectWithAssertedSafeErrorAndResultInCallback')
26 |
27 | let testAsyncObject =
28 | new AsyncMaxNum(
29 | new AsyncMaxNum(1, 3,
30 | new AsyncMaxNum(1, 2, 4).as('max1')
31 | ).as('max2'),
32 | new AsyncMaxNum(2,
33 | new SyncMaxNum(3, 4,
34 | new SyncMaxNum(5, 4, 6).as('max3')
35 | ).as('max4'), 1
36 | ).as('max5'),
37 | new SyncMaxNum(1, 2, 8).as('max6')
38 | ).as('max7')
39 | .after(
40 | new StrictEqualAssertion(
41 | new StrictEqualAssertion(
42 | new AsyncMaxNum(
43 | as('max1'),
44 | new AsyncMaxNum(
45 | new SyncMaxNum(
46 | 3, as('max2'), as('max3')
47 | ), 4, as('max4')
48 | ),
49 | new AsyncMaxNum(4,
50 | new SyncMaxNum(as('max5'), 4,
51 | new AsyncMaxNum(4, 6, 2)
52 | ), as('max6')
53 | )
54 | ),
55 | new AsyncMaxNum(3, 4, 8)
56 | ),
57 | new AsyncMaxNum(1, 4, 8).as('max8')
58 | ).after(
59 | new StrictEqualAssertion(8, as('max8'))
60 | )
61 | )
62 |
63 | testAsyncObject.call()
64 |
65 | // can't save value into cache with the same key
66 | try {
67 | new SyncMaxNum(1, 3,
68 | new SyncMaxNum(1, 2, 4).as('max1')
69 | ).after(
70 | new SyncMaxNum(1, 2, 4).as('max1')
71 | ).call()
72 | } catch (err) {
73 | assert.deepStrictEqual(err, new Error('There is already value that is cached with key max1'))
74 | }
75 |
76 | let testAsyncObjectWithFailedAs =
77 | new StrictEqualAssertion(
78 | new AsyncMaxNum(1, 2, as('max'))
79 | )
80 |
81 | // we can do it, because here AsyncMaxNum does not work with I/O asynchronously
82 | try {
83 | testAsyncObjectWithFailedAs.call()
84 | } catch (err) {
85 | assert.deepStrictEqual(err, new Error('There is no value that is cached with key max'))
86 | }
87 |
88 | let testAsyncObjectWithoutDefinedCall =
89 | new BrokenAsyncObject()
90 |
91 | try {
92 | testAsyncObjectWithoutDefinedCall.call()
93 | } catch (err) {
94 | assert.deepStrictEqual(err, new Error('asyncCall or syncCall must be defined'))
95 | }
96 |
97 | let testAsyncObjectWithSafeError =
98 | new DeepStrictEqualAssertion(
99 | new SafeAsyncObject(),
100 | new Error('safe error')
101 | )
102 |
103 | testAsyncObjectWithSafeError.call()
104 |
105 | let testAsyncObjectWithoutError =
106 | new StrictEqualAssertion(
107 | new AsyncObjectWithoutError(), 'value'
108 | )
109 |
110 | testAsyncObjectWithoutError.call()
111 |
112 | let testAsyncObjectWithBrokenEvent =
113 | new InvokedEvent(
114 | new EventOfAsyncObjectWithEvent(
115 | new BrokenEvent()
116 | )
117 | )
118 |
119 | try {
120 | testAsyncObjectWithBrokenEvent.call()
121 | } catch (err) {
122 | assert.deepStrictEqual(err, new Error('Method body must be overriden with arguments of the event/eventListener you call'))
123 | }
124 |
125 | let brokenTreeNode = new BrokenTreeNode()
126 |
127 | try {
128 | brokenTreeNode.isLeaf()
129 | } catch (err) {
130 | assert.deepStrictEqual(err, new Error('isLeaf must be overridden'))
131 | }
132 |
133 | try {
134 | brokenTreeNode.call()
135 | } catch (err) {
136 | assert.deepStrictEqual(err, new Error('call must be overridden and insert result into parent node'))
137 | }
138 |
139 | // just test safe error in callback
140 | new AsyncObjectWithAssertedSafeErrorInCallback().call()
141 |
142 | // test safe error in callback (with parent async tree node)
143 | new AsyncObjectWithArgs(
144 | new AsyncObjectWithAssertedSafeErrorInCallback()
145 | ).call()
146 |
147 | // just test error in callback
148 | new AsyncObjectWithAssertedErrorInCallback().call()
149 |
150 | // test error in callback (with parent async tree node)
151 | new AsyncObjectWithArgs(
152 | new AsyncObjectWithAssertedErrorInCallback()
153 | ).call()
154 |
155 | // just test error in definedCall
156 | new AsyncObjectWithAssertedErrorInDefinedCall().call()
157 |
158 | // test error in definedCall (with parent async tree node)
159 | new AsyncObjectWithArgs(
160 | new AsyncObjectWithAssertedErrorInDefinedCall()
161 | ).call()
162 |
163 | // just test safe error in definedCall
164 | new AsyncObjectWithAssertedSafeErrorInDefinedCall().call()
165 |
166 | // test safe error in definedCall (with parent async tree node)
167 | new AsyncObjectWithArgs(
168 | new AsyncObjectWithAssertedSafeErrorInDefinedCall()
169 | ).call()
170 |
171 | // just test result in callback
172 | new AsyncObjectWithAssertedResultInCallback().call()
173 |
174 | // test result in callback (with parent async tree node)
175 | new AsyncObjectWithArgs(
176 | new AsyncObjectWithAssertedResultInCallback()
177 | ).call()
178 |
179 | // just test safe result in callback
180 | new AsyncObjectWithAssertedSafeResultInCallback().call()
181 |
182 | // test safe result in callback (with parent async tree node)
183 | new AsyncObjectWithArgs(
184 | new AsyncObjectWithAssertedSafeResultInCallback()
185 | ).call()
186 |
187 | // just test safe error and safe result in callback
188 | new AsyncObjectWithAssertedSafeErrorAndResultInCallback().call()
189 |
190 | // test safe error and safe result in callback (with parent async tree node)
191 | new AsyncObjectWithArgs(
192 | new AsyncObjectWithAssertedSafeErrorAndResultInCallback()
193 | ).call()
194 |
195 | class A extends AsyncObject {}
196 | class B extends A {}
197 | class C extends B {}
198 | class E extends Event {}
199 | class D extends E {}
200 | class F extends D {}
201 | class N {}
202 |
203 | assert(new A().isAsyncObject(new A()))
204 | assert(new A().isAsyncObject(new B()))
205 | assert(new A().isAsyncObject(new C()))
206 | assert(!new A().isAsyncObject(new N()))
207 | assert(new A().isEvent(new E()))
208 | assert(new A().isEvent(new D()))
209 | assert(new A().isEvent(new F()))
210 | assert(!new A().isEvent(new N()))
211 |
--------------------------------------------------------------------------------