Socket
Socket
Sign inDemoInstall

effects-as-data

Package Overview
Dependencies
Maintainers
1
Versions
155
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

effects-as-data - npm Package Compare versions

Comparing version 2.14.0 to 2.14.1

2

package.json
{
"name": "effects-as-data",
"version": "2.14.0",
"version": "2.14.1",
"description":

@@ -5,0 +5,0 @@ "A micro abstraction layer for Javascript that makes writing, testing, and monitoring side-effects easy.",

# Effects-as-data
Effects-as-data is a micro abstraction layer for Javascript that makes writing, [testing](https://github.com/orourkedd/effects-as-data#second-test-your-business-logic), and [monitoring](https://github.com/orourkedd/effects-as-data#fifth-optionally-setting-up-monitoring--telemetry) side-effects easy.
Effects-as-data is a micro abstraction layer for Javascript that makes writing, [testing](#testing), and [monitoring](#telemetry) side-effects easy.

@@ -11,2 +11,3 @@ * Using effects-as-data can dramatically reduce the time it takes to deliver tested code.

* Anywhere you can use promises, you can use effects-as-data.
* The effects-as-data runtime is 100% stateless.

@@ -19,2 +20,3 @@ ## Table of Contents

* [Getting Starting Using Existing Commands and Handlers](#getting-starting-using-existing-commands-and-handlers)
* [Calling an Effects-as-data Function](#calling-an-effects-as-data-function)
* [Creating Your Own Commands and Handlers](#creating-your-own-commands-and-handlers)

@@ -37,5 +39,5 @@ * [Error handling](#error-handling)

In comes effects-as-data. Effects-as-data is a runtime that allows you to write pure functions that merely declare side effects (commands). Effects-as-data uses [generators](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Iterators_and_Generators).
In comes effects-as-data. Effects-as-data is a runtime that allows you to write pure functions that merely declare side effects (commands). The runtime will take care of handling the command. In effects-as-data, you write these pure functions using [generators](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Iterators_and_Generators).
Why generators? A generator is like a "function with multiple returns". The power of a generator is that it can hold a lexical scope between these returns and normal control flow operators can be used. This gives us the power to write very complex side effect production operations as a pure function.
Why generators? A generator is like a "function with multiple returns". The power of a generator is that it can hold a lexical scope between these returns and normal control flow operators can be used. This gives you the power to write very complex side effect production operations as a pure function.

@@ -58,3 +60,3 @@ ### Think in terms of inputs and outputs only

### First, create a command creator.
This function creates a plain JSON `command` object that effects-as-data will pass to a handler function which will perform the actual HTTP request. The `type` field on the command matches the name of the handler to which it will be passed (see step 4). *Note* we have not yet actually implemented the function that will actual do the HTTP GET request, we have just defined a `command`. The command is placed on the `cmds` object for convenience. This command represents the `data` in `effects-as-data`. [See Working Code](https://github.com/orourkedd/effects-as-data-examples/blob/master/basic/cmds.js)
This function creates a plain JSON `command` object that effects-as-data will pass to a handler function which will perform the actual HTTP request. The `type` field on the command matches the name of the handler to which it will be passed (see step 4). *Note* we have not yet actually implemented the function that will actual do the HTTP GET request, we have just defined a `command`. This command represents the `data` in `effects-as-data`. [See Working Code](https://github.com/orourkedd/effects-as-data-examples/blob/master/basic/cmds.js)
```js

@@ -76,3 +78,3 @@ // cmds.js

### Second, test your business logic.
Write a test for `getPeople` function that you are about to create. These tests can be used stand-alone or in any test runner like Jest, Mocha, etc. There are a few ways to test `effects-as-data` functions demonstrated below. [See Working Code](https://github.com/orourkedd/effects-as-data-examples/blob/master/basic/functions.spec.js)
Write a test for the `getPeople` function (that you are about to create). Effects-as-data tests are compatible with any test runner like Jest, Mocha, etc. For more on testing, see [Testing](#testing).

@@ -102,3 +104,3 @@ Semantic test example:

### Third, write your business logic.
Effects-as-data uses a generator function's ability to give up execution flow and to pass a value to an outside process using the `yield` keyword. You create `command` objects in your business logic and `yield` them to `effects-as-data`. It is important to understand that when using effects-as-data that your business logic never actually `httpGet`'s anything. It ONLY creates plain JSON objects and `yield`'s them out (`cmds.httpGet()` simply returns the JSON object from step 1). This is one of the main reasons `effects-as-data` functions are easy to test. [See Working Code](https://github.com/orourkedd/effects-as-data-examples/blob/master/basic/functions.js)
Effects-as-data uses a generator function's ability to give up execution flow and to pass a value to an outside process using the `yield` keyword. You create `command` objects in your business logic and `yield` them to `effects-as-data`. It is important to understand that when using effects-as-data that your business logic never actually `httpGet`'s anything. It ONLY creates plain JSON objects and `yield`'s them out (`cmds.httpGet()` simply returns the JSON object from [Step 1](#first-create-a-command-creator)). This is one of the main reasons `effects-as-data` functions are easy to test. [See Working Code](https://github.com/orourkedd/effects-as-data-examples/blob/master/basic/functions.js)
```js

@@ -121,3 +123,3 @@ // functions.js

### Fourth, create a command handler.
After the `command` object is `yield`ed, effects-as-data will pass it to a handler function that will perform the side-effect producing operation (in this case, an HTTP GET request). This is the function mentioned in step 1 that actually performs the HTTP GET request. Notice that the business logic does not call this function directly; the business logic in step 1 simply `yield`s the `httpGet` `command` out, and `effects-as-data` takes care of getting it to the handler. The handler does the `effect` in `effects-as-data`. [See Working Code](https://github.com/orourkedd/effects-as-data-examples/blob/master/basic/handlers.js)
After the `command` object is `yield`-ed, effects-as-data will pass it to a handler function that will perform the side-effect producing operation (in this case, an HTTP GET request). This is the function mentioned in [Step 1](#first-create-a-command-creator) that actually performs the HTTP GET request. Notice that the business logic does not call this function directly; the business logic in [Step 1](#first-create-a-command-creator) simply `yield`-s the `httpGet` `command` out, and `effects-as-data` takes care of getting it to the handler. The handler does the `effect` in `effects-as-data`. [See Working Code](https://github.com/orourkedd/effects-as-data-examples/blob/master/basic/handlers.js)
```js

@@ -137,3 +139,3 @@ // handlers.js

### Fifth, optionally setting up monitoring / telemetry.
The effects-as-data config accepts an `onCommandComplete` callback which will be called every time a `command` completes, giving detailed information about the operation. This data can be logged to the console or sent to a logging service. *Note*, this step is optional.
The effects-as-data config accepts several lifecycle callbacks which will be called when a function is called and completes and when a command is called and completes, giving detailed information about the operation. This data can be logged to the console or sent to a logging service. *Note*, this step is optional.
```js

@@ -191,32 +193,19 @@ // Normally this will be in index.js (see below)

See full example in the `effects-as-data-examples` repository: [https://github.com/orourkedd/effects-as-data-examples/blob/master/basic](https://github.com/orourkedd/effects-as-data-examples/blob/master/basic).
See full example in the `effects-as-data-examples` repository: [effects-as-data-examples](https://github.com/orourkedd/effects-as-data-examples/blob/master/basic).
## Getting Starting Using Existing Commands and Handlers
This example demonstrates using the `effects-as-data-universal` module which contains commands/handler that can be used anywhere Javascript runs.
This example demonstrates using the `effects-as-data-universal` module which contains commands/handlers that can be used anywhere Javascript runs.
```js
const { call, buildFunctions } = require('effects-as-data')
const { testFn, args } = require('effects-as-data/test')
const { buildFunctions } = require('effects-as-data')
const { cmds, handlers } = require('effects-as-data-universal')
function* getPeople() {
const { payload } = yield cmds.httpGet('https://swapi.co/api/people')
const names = payload.results.map(p => p.name)
const { results } = yield cmds.httpGet('https://swapi.co/api/people')
const names = results.map(p => p.name)
return names
}
// Semantic test style
testFn(getPeople, () => {
const apiResults = { payload: { results: [{ name: 'Luke Skywalker' }] } }
return args()
.yieldCmd(cmds.httpGet('https://swapi.co/api/people')).yieldReturns(apiResults)
.returns(['Luke Skywalker'])
})()
const config = { /* lifecycle callbacks, etc */ }
const config = {
onCommandComplete: telemetry => {
console.log('Telemetry (from onCommandComplete):', telemetry)
}
}
const functions = buildFunctions(config, handlers, { getPeople })

@@ -234,2 +223,30 @@

## Calling an Effects-as-data Function
There are two ways to call an effects-as-data function.
The first is to use `buildFunctions()` which will turn multiple effects-as-data functions in to normal, promise-returning functions. You can see an example of this in [Getting Starting Using Existing Commands and Handlers](#getting-starting-using-existing-commands-and-handlers).
The second way is to use the call function:
```js
const { call } = require('effects-as-data')
const { cmds, handlers } = require('effects-as-data-universal')
function* getPeople() {
const { results } = yield cmds.httpGet('https://swapi.co/api/people')
const names = results.map(p => p.name)
return names
}
const config = { /* lifecycle callbacks, etc */ }
call(config, handlers, getPeople, /* arg1, arg2, etc */)
.then(names => {
console.log('\n')
console.log('Function Results:')
console.log(names.join(', '))
})
.catch(console.error)
```
## Creating Your Own Commands and Handlers

@@ -239,12 +256,59 @@

### Anatomy of a handler
A handler is simply a function that receives a command object and does something with it.
A few notes on handlers:
1. Effects-as-data will handle errors from handlers so they should throw or reject when things go wrong.
1. Handlers can return a promise, return a normal value (string, number, object, etc), return nothing, or throw an error.
1. Handlers can call another effects-as-data functions (see below).
#### Simple Handler
```js
function httpGet(cmd) {
return fetch(cmd.url).then(r => r.json());
}
```
#### Simple Handler Returning a Number
```js
// notice that the cmd is not even used
function now(cmd) {
return Date.now()
}
```
#### Handler that calls another effects-as-data function
This example is taken from the [either](https://github.com/orourkedd/effects-as-data-universal/blob/master/src/handlers/either.js) hander in [effects-as-data-universal](https://github.com/orourkedd/effects-as-data-universal).
Notice that all handlers receive an object as a second argument. This object has a reference to the [call](#calling-an-effects-as-data-function) function, the current effects-as-data config and handlers.
```js
// notice that the command has a cmd property
function either({ cmd, defaultValue }, { call, config, handlers }) {
return call(config, handlers, function*() {
try {
const result = yield cmd
return result || defaultValue
} catch (e) {
return defaultValue
}
})
}
```
## Error handling
Below are various examples of error handling with effects-as-data. It is important to note that effects-as-data will catch any error thrown by your effects-as-data function or thrown by handler and will:
Below are various examples of error handling with effects-as-data. It is important to note that effects-as-data will catch any error thrown by your effects-as-data function or thrown by a handler and will:
1. Pass the error to the `onCommandComplete` or `onCallComplete` lifecycle callbacks. This means you don't have to do any logging in your business logic.
1. Reject the promise created by effects-as-data around your running function and pass the error out.
1. You don't have to write a test to verify that an error is handled ([unless you are doing something specific in a `catch` block](#using-trycatch-like-in-asyncawait))
By default errors should act just like they do in `async/await`. Things get fun, however, when you use command modifiers like [either](#using-cmdseither) or [retry](#using-cmdsretry). Using command modifiers can add sophisticated error handling to your code without adding complexity. Pro tip: Because command modifiers are not really a thing (I just call them that because they run other commands), they are all composable.
Because the runtime handles errors for you, you don't have to write a test to verify that an error is handled ([unless you are doing something specific in a `catch` block](#using-trycatch-like-in-asyncawait))
By default errors should act just like they do in `async/await`. Things get fun, however, when you use command modifiers like [either](#using-cmdseither) or [retry](#using-cmdsretry). Using command modifiers can add sophisticated error handling to your code without adding complexity.
NOTE: you can easily write your own command modifiers. Follow the example of `either` here: [either cmd](https://github.com/orourkedd/effects-as-data-universal/blob/master/src/cmds/either.js), [either handler](https://github.com/orourkedd/effects-as-data-universal/blob/master/src/handlers/either.js).

@@ -296,3 +360,3 @@

The `either` handler will process the `httpGet` command, and if the command is successful, will return the response. If the `httpGet` command fails or returns a falsey value, the `either` handler will return `defaultResults`. Because the `either` handler will never throw an exception and will either return a successful result or `defaultResults`, there is no need for an `if` statement to ensure success before the `map`. Using this pattern will reduce the number of code paths and simplify code.
The `either` handler will process the `httpGet` command, and if the command is successful, will return the response. If the `httpGet` command fails or returns a falsey value, the `either` handler will return `defaultResults`. Because the `either` handler will never throw an exception and will either return a successful result or `defaultResults`, there is no need for an `if` or a `try/catch` statement to ensure success before the `map`. Using this pattern will reduce the number of code paths and simplify code.

@@ -397,3 +461,3 @@ See Working Example: [https://github.com/orourkedd/effects-as-data-examples/tree/master/misc-examples).

test(
'getPeople should return an empty list if all retried fail',
'getPeople should return an empty list if all retries fail',
testGetPeople(() => {

@@ -495,3 +559,4 @@ const apiResults = { results: [{ name: 'Luke Skywalker' }] }

{config, handlers, fn: [Function: getPeople], args}
]
],
// ... whatever other arbitrary values you put onto the effects-as-data config
}

@@ -532,3 +597,4 @@ }

{config, handlers, fn: [Function: getPeople], args}
]
],
// ... whatever other arbitrary values you put onto the effects-as-data config
}

@@ -571,3 +637,4 @@ }

{config, handlers, fn: [Function: getPeople], args}
]
],
// ... whatever other arbitrary values you put onto the effects-as-data config
}

@@ -617,3 +684,4 @@ }

{config, handlers, fn: [Function: getPeople], args}
]
],
// ... whatever other arbitrary values you put onto the effects-as-data config
}

@@ -626,3 +694,3 @@ }

Testing in effects-as-data is really easy, even for complex asynchronous operations. This is because effects-as-data functions are pure functions and only output JSON objects. Effects-as-data tests don't make assertions; they simply declare a data-structure and the test runner validates that the inputs and outputs in the data structure match the inputs and outputs of the function.
Testing in effects-as-data is easy, even for complex asynchronous operations. This is because effects-as-data functions are pure functions and only output JSON objects. Effects-as-data tests don't make assertions; they simply declare a data-structure and the test runner validates that the inputs and outputs in the data structure match the inputs and outputs of the function.

@@ -653,5 +721,6 @@ Below are a few examples of testing with effects-as-data:

it('should get a person return his/her name', testGetPerson(() => {
const person = { name: 'C-3P0'}
return args(2)
.yieldCmd(cmds.httpGet(`https://swapi.co/api/people/2`)).yieldReturns({ name: 'C-3P0'})
.returns('C-3P0')
.yieldCmd(cmds.httpGet(`https://swapi.co/api/people/2`)).yieldReturns(person)
.returns(person.name)
}))

@@ -666,3 +735,3 @@ })

const cmds = require('effects-as-data-universal')
const { postgres } = require('effects-as-data-postgres')
const { postgres } = require('some-effects-as-data-postgres-module')

@@ -680,3 +749,3 @@ function* getPeopleWithSameName(id) {

const cmds = require('effects-as-data-universal')
const { postgres } = require('effects-as-data-postgres')
const { postgres } = require('some-effects-as-data-postgres-module')
const getPeopleWithSameName = require('./get-person')

@@ -683,0 +752,0 @@

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc