Skip to content
This repository has been archived by the owner on Jun 26, 2021. It is now read-only.

Commit

Permalink
Super powered listener 📢 (#14)
Browse files Browse the repository at this point in the history
  • Loading branch information
JRJurman authored Nov 9, 2017
1 parent f9eba20 commit e8f3ca1
Show file tree
Hide file tree
Showing 6 changed files with 75 additions and 22 deletions.
36 changes: 31 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ so if you're familar with those frameworks, you'll notice some similarities!
Hover-Engine gives your app the following super-powers:
* Trigger multiple sets of actions with a single dispatch!
* Notify as many listeners as you want!
* Handle async (or chained) action calls with [semi-predictable results](https://en.wikipedia.org/wiki/Magic_\(illusion\))!
* Handle async (or chained) action calls with predictable results!
* No Dependencies!
* Light enough to give you flight (~2 KB)!

Expand Down Expand Up @@ -92,7 +92,7 @@ All groups of actions **must** include the `init` action. This action dictates t

The `init` action is called after `engine.addActions` runs. It is passed no arguments.

#### Action Arguements
#### Action Arguments
```javascript
const temperatureActions = {
init: () => 70,
Expand Down Expand Up @@ -120,7 +120,7 @@ engine.actions.increaseTemp()
console.log(engine.store) // -> { temp: 71 }
```

The second arguement is anything that we pass in when we call the action. For example:
The second argument is anything that we pass in when we call the action. For example:
```javascript
const engine = new HoverEngine()
engine.addActions({temp: temperatureActions})
Expand Down Expand Up @@ -166,7 +166,7 @@ You'll notice in the above example that calling `addTodo` actually called both t

### `addListener(listener)`

Listeners in Hover-Engine are functions that get called whenever an action is called. It receives the `engine.store` and `engine.actions` as an arguments.
Listeners in Hover-Engine are functions that get called whenever an action is called. It receives the updated `engine.store`, `engine.actions`, as well as information about the action that was called (read below).

```javascript
const commentThreadActions = {
Expand All @@ -180,7 +180,33 @@ engine.addActions({thread: commentThreadActions})
engine.addListener((store) => document.body.innerHTML = store.thread.join('<br />'))
```

Like `addActions`, you can add as many listeners as you want, each will be called with the new store.
Like `addActions`, you can add as many listeners as you want by calling `addListener` multiple times. Each will be called with the new store.

#### Listener Arguments
Along with the store and actions, listeners also recieve the name of the action that was called, and the argument it was called with. With these, you can use listeners to debug what is happening in hover-engine. In the example below, we log the action and the new values in the store.

```javascript
const debugListener = (store, actions, actionName, actionArguments) => {
console.log(actionName, actionArguments, '->', store)
}
```

### `notifyListeners(actionName, actionArguments)`

`notifyListeners` is a function which tells all the listeners to be triggered. It takes in an action name and action argument (both of which are optional), and calls all the listeners that have been added with the current store and actions, and passes along the action name and argument if they were included. You shouldn't need this in most applications, but can be useful for testing or debugging your logic.

```javascript
const counterActions = {
init: () => 0,
increment: (counter) => counter + 1
}

const engine = new HoverEngine()
engine.addListener((store) => console.log('store:', store))
engine.notifyListeners() // store: {counter: 0}
```

Like `addActions`, you can add as many listeners as you want by calling `addListener` multiple times. Each will be called with the new store.

### `engine.actions`

Expand Down
14 changes: 8 additions & 6 deletions examples/counter.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ const numberActions = {
}

const engine = new HoverEngine()
engine.addActions({ num: numberActions })
engine.addListener((store) => console.log('NEW STATE:', store))
engine.addActions({num: numberActions})
engine.addListener((store, actions, actionName, actionArguments) => {
console.log(actionName, actionArguments, '->', store)
})

engine.actions.add(5) // -> NEW STATE: { num: 5 }
engine.actions.add(8) // -> NEW STATE: { num: 13 }
engine.actions.sub(5) // -> NEW STATE: { num: 8 }
console.log(engine.store) // -> { num: 8 }
engine.actions.add(5) // add 5 -> { num: 5 }
engine.actions.add(8) // add 8 -> { num: 13 }
engine.actions.sub(5) // sub 5 -> { num: 8 }
console.log(engine.store) // { num: 8 }
6 changes: 3 additions & 3 deletions hover-engine.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ class HoverEngine {
const updateStoreByAction = updateStoreByNextAction(nextAction)
this.store = nextAction.actions.reduce(updateStoreByAction, this.store)
this.actionQueue.shift()
this.notifyListeners()
this.notifyListeners(name, args)
}
}

Expand All @@ -87,8 +87,8 @@ class HoverEngine {
return this
}

notifyListeners() {
this.listeners.forEach(listener => listener(this.store, this.actions))
notifyListeners(actionName, actionArguments) {
this.listeners.forEach(listener => listener(this.store, this.actions, actionName, actionArguments))

return this
}
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
{
"name": "hover-engine",
"version": "1.2.0",
"version": "1.3.0",
"description": "a state-management library that runs on technology and magic",
"main": "dist/hover-engine.js",
"scripts": {
"lint": "xo hover-engine.js rollup.config.js specs/*.js",
"build": "rollup -c ./rollup.config.js",
"pretest": "npm run build",
"test": "jasmine specs/hover-engine-specs.js"
},
"keywords": [
Expand Down
2 changes: 1 addition & 1 deletion specs/action-groups.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ module.exports.argsActionGroup = (spies) => Object({
},
increment: (state, value, actions) => {
spies && spies.increment && spies.increment(state, value, actions)
return state + 1
return state + value
}
}
})
Expand Down
36 changes: 30 additions & 6 deletions specs/hover-engine-specs.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
const HoverEnginer = require('../hover-engine')
// we won't always have this file built, so don't depend on it to pass lint
// eslint-disable-next-line import/no-unresolved
const HoverEnginer = require('../dist/hover-engine')
const ag = require('./action-groups')

// asymmetric matcher for params that we don't care about
const whatever = {
asymmetricMatch: () => true
}

describe('HoverEngine', () => {
let engine
beforeEach(() => {
Expand Down Expand Up @@ -123,20 +130,28 @@ describe('HoverEngine', () => {
expect(spyB).toHaveBeenCalled()
})

it('should call listener with store and actions', () => {
it('should call listener with store, actions', () => {
const listenerSpy = jasmine.createSpy('listener')
engine.addActions(ag.singleActionGroup())
engine.addListener(listenerSpy)
engine.notifyListeners()
expect(listenerSpy).toHaveBeenCalledWith(engine.store, engine.actions)
expect(listenerSpy).toHaveBeenCalledWith(engine.store, engine.actions, whatever, whatever)
})

it('should call listener with updated store', () => {
const listenerSpy = jasmine.createSpy('listener')
engine.addActions(ag.singleActionGroup())
engine.addListener(listenerSpy)
engine.actions.increment()
expect(listenerSpy).toHaveBeenCalledWith(jasmine.objectContaining({A: 1}), jasmine.anything())
expect(listenerSpy).toHaveBeenCalledWith(jasmine.objectContaining({A: 1}), whatever, whatever, whatever)
})

it('should call listener with called action and action arguments', () => {
const listenerSpy = jasmine.createSpy('listener')
engine.addActions(ag.argsActionGroup())
engine.addListener(listenerSpy)
engine.actions.increment(5)
expect(listenerSpy).toHaveBeenCalledWith(whatever, whatever, 'increment', 5)
})

it('should be chainable', () => {
Expand Down Expand Up @@ -201,7 +216,16 @@ describe('HoverEngine', () => {
expect(engine.notifyListeners).toHaveBeenCalled()
})

it('should pass arguements into the action', () => {
it('should trigger notifyListeners with the action name and argument', () => {
spyOn(engine, 'notifyListeners')

engine.addActions(ag.argsActionGroup())
engine.actions.increment(5)

expect(engine.notifyListeners).toHaveBeenCalledWith('increment', 5)
})

it('should pass arguments into the action', () => {
const argsSpy = jasmine.createSpy('args spy')

const spies = {increment: argsSpy}
Expand All @@ -211,7 +235,7 @@ describe('HoverEngine', () => {
expect(argsSpy).toHaveBeenCalledWith(0, 10, engine.actions)
})

it('should be chainable off of actons arguement', () => {
it('should be chainable off of actons argument', () => {
engine.addActions(ag.chainActionGroup())

expect(engine.store.A).toEqual(0)
Expand Down

0 comments on commit e8f3ca1

Please sign in to comment.