Skip to content

Commit

Permalink
Merge pull request #11 from Alexandre-Herve/parametric-slot
Browse files Browse the repository at this point in the history
Parametric slot
  • Loading branch information
Alexandre-Herve authored Sep 9, 2019
2 parents 9f09836 + 144293e commit 0f7d5f8
Show file tree
Hide file tree
Showing 14 changed files with 801 additions and 355 deletions.
49 changes: 39 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,15 @@ Once connected, the clients can start by using the slots on the event bus
// firstModule.ts
import EventBus from './firstModule.EventBus.ts'

// Slots can be called with a parameter, here 'michel'
EventBus.say('michel', 'Hello')

// Or one can rely on the default parameter: here DEFAULT_PARAMETER
// is implicitely used.
EventBus.say('Hello')

// Triggering an event always returns a promise
EventBus.sayHello('michel').then(() => {
EventBus.say('michel', 'Hello').then(() => {
...
})

Expand All @@ -88,12 +95,14 @@ EventBus.ping()
// secondModule.ts
import EventBus from './secondModule.EventBus.ts'

EventBus.ping().on(() => {
// Add a listener on the default parameter
EventBus.ping.on(() => {
console.log('pong')
})

EventBus.sayHello.on(name => {
console.log(`${name} said hello!`)
// Or listen to a specific parameter
EventBus.say.on('michel', (words) => {
console.log('michel said', words)
})

// Event subscribers can respond to the event synchronously (by returning a value)
Expand Down Expand Up @@ -130,27 +139,47 @@ Remote or local clients are considered equally. If a client was already connecte
at the time when `lazy` is called, the "connect" callback is called immediately.

```typescript
const connect = () => {
console.log('Someone somewhere has begun listening to the slot with `.on`.')
const connect = (param) => {
console.log(`Someone somewhere has begun listening to the slot with .on on ${param}.`)
}

const disconnect = () => {
console.log('No one is listening to the slot anymore.')
const disconnect = (param) => {
console.log(`No one is listening to the slot anymore on ${param}.`)
}

const disconnectLazy = EventBus.ping.lazy(connect, disconnect)

const unsubscribe = EventBus.ping().on(() => { })
// console output: 'Someone somewhere has begun listening to the slot with `.on`.'
// console output: 'Someone somewhere has begun listening to the slot with .on on $_DEFAULT_$.'

unsubscribe()
// console output: 'No one is listening to the slot anymore.'
// console output: 'No one is listening to the slot anymore on $_DEFAULT_$.'

const unsubscribe = EventBus.ping().on('parameter', () => { })
// console output: 'Someone somewhere has begun listening to the slot with .on on parameter.'

unsubscribe()
// console output: 'No one is listening to the slot anymore on parameter.'

// Remove the callbacks.
// "disconnect" is called one last time if there were subscribers left on the slot.
disconnectLazy()
```

### Buffering

When the eventBus is created with channels, slots will wait for all transports to have
registered callbacks before triggering.

This buffering mechanism can be disabled at the slot level with the `noBuffer` config option:

```typescript
const MyEvents = {
willWait: slot<string>(),
wontWait: slot<string>({ noBuffer: true }),
}
```

### Syntactic sugar

You can combine events from different sources.
Expand Down
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,9 @@
],
"resolutions": {
"js-yaml": ">=3.13.1",
"lodash": ">=4.17.11"
"lodash": ">=4.17.12",
"mixin-deep": ">=1.3.2 <2.0.0 || >=2.0.1",
"set-value": ">=2.0.1 <3.0.0 || >=3.0.1"
},
"license": "Apache-2.0"
}
1 change: 1 addition & 0 deletions src/Constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const DEFAULT_PARAM = '$_DEFAULT_$'
34 changes: 31 additions & 3 deletions src/Events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,44 @@ export interface EventDeclaration {
[slotName: string]: Slot<any, any>
}

export function combineEvents<C extends EventDeclaration>(...args: C[]): { [P in keyof C]: C[P] } {
// TODO: throw if event buses have duplicate events
export function combineEvents<
C1 extends EventDeclaration, C2 extends EventDeclaration, C3 extends EventDeclaration,
C4 extends EventDeclaration, C5 extends EventDeclaration, C6 extends EventDeclaration,
C7 extends EventDeclaration, C8 extends EventDeclaration, C9 extends EventDeclaration,
C10 extends EventDeclaration, C11 extends EventDeclaration, C12 extends EventDeclaration,
C13 extends EventDeclaration, C14 extends EventDeclaration, C15 extends EventDeclaration,
C16 extends EventDeclaration, C17 extends EventDeclaration, C18 extends EventDeclaration,
C19 extends EventDeclaration, C20 extends EventDeclaration, C21 extends EventDeclaration,
C22 extends EventDeclaration, C23 extends EventDeclaration, C24 extends EventDeclaration
>(
_c1: C1, _c2: C2, _c3?: C3, _c4?: C4, _c5?: C5, _c6?: C6, _c7?: C7, _c8?: C8,
_c9?: C9, _c10?: C10, _c11?: C11, _c12?: C12, _c13?: C13, _c14?: C14, _c15?: C15,
_c16?: C16, _c17?: C17, _c18?: C18, _c19?: C19, _c20?: C20, _c21?: C21, _c22?: C22,
_c23?: C23, _c24?: C24
): C1 & C2 & C3 & C4 & C5 & C6 & C7 & C8 & C9 & C10 & C11 & C12 & C13 & C14 & C15 & C16
& C17 & C18 & C19 & C20 & C21 & C22 & C23 & C24 {

const args = Array.from(arguments)

const keys = args.reduce((keys, arg) => {
return [...keys, ...Object.keys(arg)]
}, [])

const uniqKeys = [...new Set(keys)]

if (keys.length > uniqKeys.length) {
throw new Error('ts-event-bus: duplicate slots encountered in combineEvents.')
}

return Object.assign({}, ...args)
}

export function createEventBus<C extends EventDeclaration>(args: { events: C, channels?: Channel[] }): C {
const transports = (args.channels || []).map(c => new Transport(c))
return Object.keys(args.events)
.reduce((conn: C, slotName) => {
conn[slotName] = connectSlot(slotName, transports as Transport[])
const config = args.events[slotName].config
conn[slotName] = connectSlot(slotName, transports as Transport[], config)
return conn
}, {} as any)
}
2 changes: 1 addition & 1 deletion src/Handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ function callOneHandler(h: Handler<any>, data: any): Promise<any> {
* of all these handlers is discarded.
*/
export function callHandlers(data: any, handlers: Handler<any, any>[]): Promise<any> {
if (handlers.length === 0) {
if (!handlers || handlers.length === 0) {
// No one is listening
return new Promise(resolve => { /* NOOP, this promise will never resolve */ })
} else if (handlers.length === 1) {
Expand Down
46 changes: 38 additions & 8 deletions src/Message.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,44 @@
export type TransportRequest = { type: 'request', slotName: string, id: string, data: any }
export type TransportResponse = { type: 'response', slotName: string, id: string, data: any }
export type TransportError = { type: 'error', slotName: string, id: string, message: string, stack?: string }
export type TransportRegistrationMessage = { type: 'handler_registered', slotName: string }
export type TransportUnregistrationMessage = { type: 'handler_unregistered', slotName: string }
export type TransportRequest = {
type: 'request',
slotName: string,
id: string,
data: any,
param: string
}

export type TransportResponse = {
type: 'response',
slotName: string,
id: string,
data: any,
param: string
}

export type TransportError = {
id: string,
message: string,
param: string,
slotName: string,
stack?: string,
type: 'error'
}

export type TransportRegistrationMessage = {
type: 'handler_registered',
slotName: string,
param: string
}
export type TransportUnregistrationMessage = {
type: 'handler_unregistered',
slotName: string,
param: string
}
export type TransportMessage =
TransportRegistrationMessage
| TransportUnregistrationMessage
TransportError
| TransportRegistrationMessage
| TransportRequest
| TransportResponse
| TransportError
| TransportUnregistrationMessage

export function isTransportMessage(m: { type: string }): m is TransportMessage {
switch (m.type) {
Expand Down
Loading

0 comments on commit 0f7d5f8

Please sign in to comment.