Changeset View
Changeset View
Standalone View
Standalone View
lib/app/hookable.js
const Bluebird = require("bluebird"); | const Bluebird = require("bluebird"); | ||||
const assert = require("assert"); | const assert = require("assert"); | ||||
class Hook { | class Hook { | ||||
constructor({ event_matcher, callback, is_blocking = true }) { | constructor({ event_matcher, callback, is_blocking = true }) { | ||||
assert.equal(typeof callback, "function"); | assert.equal(typeof callback, "function"); | ||||
assert.equal(typeof is_blocking, "boolean"); | assert.equal(typeof is_blocking, "boolean"); | ||||
this.event_matcher = event_matcher; | this.event_matcher = event_matcher; | ||||
this.callback = callback; | this.callback = callback; | ||||
this.is_blocking = is_blocking; | this.is_blocking = is_blocking; | ||||
} | } | ||||
appliesTo(event_description) { | appliesTo(event_description) { | ||||
return event_description.isEquivalentTo(this.event_matcher); | return event_description.isEquivalentTo(this.event_matcher); | ||||
} | } | ||||
} | } | ||||
class EventMatcher { | class EventMatcher { | ||||
constructor({ when, subject_path = /.*/, action }) { | constructor({ when, subject_path = /.*/, action }) { | ||||
if (arguments[0] instanceof EventMatcher) { | if (arguments[0] instanceof EventMatcher) { | ||||
return arguments[0]; | return arguments[0]; | ||||
} | } | ||||
assert.equal(typeof when, "string"); | assert.equal(typeof when, "string"); | ||||
assert(subject_path instanceof RegExp); | assert(subject_path instanceof RegExp); | ||||
assert(typeof action === "string" || Array.isArray(action)); | assert(typeof action === "string" || Array.isArray(action)); | ||||
this.when = when; | this.when = when; | ||||
this.subject_path = subject_path; | this.subject_path = subject_path; | ||||
this.action = action; | this.action = action; | ||||
} | } | ||||
containsAction(action_name) { | containsAction(action_name) { | ||||
if (typeof this.action === "string") { | if (typeof this.action === "string") { | ||||
return this.action === action_name; | return this.action === action_name; | ||||
} | } | ||||
if (Array.isArray(this.action)) { | if (Array.isArray(this.action)) { | ||||
return this.action.includes(action_name); | return this.action.includes(action_name); | ||||
} | } | ||||
return false; | return false; | ||||
Show All 30 Lines | constructor({ when, subject_path = "", action, metadata = {} }) { | ||||
assert.equal(typeof subject_path, "string"); | assert.equal(typeof subject_path, "string"); | ||||
assert.equal(typeof action, "string"); | assert.equal(typeof action, "string"); | ||||
assert.equal(typeof metadata, "object"); | assert.equal(typeof metadata, "object"); | ||||
this.when = when; | this.when = when; | ||||
this.subject_path = subject_path; | this.subject_path = subject_path; | ||||
this.action = action; | this.action = action; | ||||
this.metadata = metadata; | this.metadata = metadata; | ||||
} | } | ||||
isEquivalentTo(source_event) { | isEquivalentTo(source_event) { | ||||
return ( | return ( | ||||
this.matchWhen(source_event) && | this.matchWhen(source_event) && | ||||
this.matchAction(source_event) && | this.matchAction(source_event) && | ||||
this.matchSubjectPath(source_event) | this.matchSubjectPath(source_event) | ||||
); | ); | ||||
} | } | ||||
matchWhen(source_event) { | matchWhen(source_event) { | ||||
return this.when === source_event.when; | return this.when === source_event.when; | ||||
} | } | ||||
matchAction(source_event) { | matchAction(source_event) { | ||||
if (typeof source_event.action === "string") { | if (typeof source_event.action === "string") { | ||||
return source_event.action === this.action; | return source_event.action === this.action; | ||||
} | } | ||||
if (Array.isArray(source_event.action)) { | if (Array.isArray(source_event.action)) { | ||||
return source_event.action.includes(this.action); | return source_event.action.includes(this.action); | ||||
} | } | ||||
return false; | return false; | ||||
} | } | ||||
matchSubjectPath(source_event) { | matchSubjectPath(source_event) { | ||||
return source_event.subject_path.test(this.subject_path); | return source_event.subject_path.test(this.subject_path); | ||||
} | } | ||||
} | } | ||||
class Hookable { | class Hookable { | ||||
constructor() { | constructor() { | ||||
this.hooks = []; | this.hooks = []; | ||||
} | } | ||||
addHook(event_description, callback, is_blocking) { | addHook(event_description, callback, is_blocking) { | ||||
const event_matcher = new EventMatcher(event_description); | const event_matcher = new EventMatcher(event_description); | ||||
const hook = new Hook({ event_matcher, callback, is_blocking }); | const hook = new Hook({ event_matcher, callback, is_blocking }); | ||||
this.hooks.push(hook); | this.hooks.push(hook); | ||||
} | } | ||||
async emit(_event_description, data = {}) { | async emit(_event_description, data = {}) { | ||||
const emitted_event = new EventDescription(_event_description); | const emitted_event = new EventDescription(_event_description); | ||||
return await Bluebird.reduce( | return Bluebird.reduce( | ||||
this.hooks.filter(hook => hook.appliesTo(emitted_event)), | this.hooks.filter(hook => hook.appliesTo(emitted_event)), | ||||
async (acc, hook) => | async (acc, hook) => | ||||
(await hook.callback(emitted_event, acc)) || acc, | (await hook.callback(emitted_event, acc)) || acc, | ||||
data | data | ||||
); | ); | ||||
} | } | ||||
} | } | ||||
module.exports = { | module.exports = { | ||||
Hookable, | Hookable, | ||||
ResourceEventMatcher, | ResourceEventMatcher, | ||||
CollectionEventMatcher, | CollectionEventMatcher, | ||||
EventMatcher, | EventMatcher, | ||||
}; | }; |