Last active
April 6, 2018 23:59
-
-
Save nathan-appere/08bf04336fa5b43e5ddbcd88d94fb641 to your computer and use it in GitHub Desktop.
Quick implementation of an event-broker with channels + topics
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
`import Ember from 'ember'` | |
# Where we save channels. | |
# The format is: | |
# channels = { "components.auth" { "submit": { /* hash of callbacks */ } }} | |
channels = {} | |
# Given hash, search for keys that match a pattern and call "callback" on them | |
# Ex: forEachMatch({ "a.b.c": 1, "a.b": 2, "x.y": 3 }, 'a.b.c', fn) will call fn twice with: fn(1) and fn(2) | |
forEachMatch = (list, key, callback) -> | |
position = key.length | |
while position != -1 | |
key = key.substr(0, position) | |
if list.hasOwnProperty(key) | |
callback list[key] | |
position = key.lastIndexOf('.') | |
targetToHash = (target) -> | |
if _.isString(target) | |
[topic, channel] = target.split('#') | |
else | |
target | |
EventBrokerService = Ember.Service.extend | |
asyncDelivery: true | |
asyncExceptions: true | |
# Returns a wrapper function that hides aync / sync event delivery | |
_deliveryWrapper: Ember.computed 'asyncDelivery', -> | |
if @get('asyncDelivery') | |
Ember.run.next | |
else | |
(fn) -> fn() | |
# Returns a wrapper function that hides aync / sync exceptions when delivering | |
_exceptionsWrapper: Ember.computed 'asyncExceptions', -> | |
if @get('asyncExceptions') | |
(fn) -> | |
try | |
fn() | |
catch ex | |
Ember.run.next(-> throw ex) | |
else | |
(fn) -> fn() | |
# Emit an event && call listeners | |
# Ex: emit('submit#components.auth') | |
emit: (target, data = []) -> | |
target = targetToHash(target) | |
deliveryWrapper = @get('_deliveryWrapper') | |
exceptionsWrapper = @get('_exceptionsWrapper') | |
# Lets us ignore sync / async delivery | |
deliveryWrapper -> | |
# Iterate through channels && find matching ones | |
forEachMatch channels, target.channel, (topics) -> | |
# Iterate through topics insides channels && find matching ones | |
forEachMatch topics, target.topic, (listeners) -> | |
# We now have all matching listeners, let's send them the event! | |
for own callback, _ of listeners | |
# Lets us ignore sync / async exceptions | |
exceptionsWrapper -> | |
callback.apply(null, data); | |
# Listens to a named event | |
# Ex: on('submit#components.auth', function() {}) | |
on: (target, callback) -> | |
return false if typeof callback != 'function' | |
target = targetToHash(target) | |
if !channels.hasOwnProperty(target.channel) | |
channels[target.channel] = {} | |
topics = channels[target.channel] | |
if !topics.hasOwnProperty(target.topic) | |
topics[target.topic] = {} | |
listeners = topics[target.topic] | |
# For fast access, the value doesn't really matter. | |
listeners[callback] = callback | |
# Listens to a named event and cancel right after it gets triggered | |
one: (target, callback) -> | |
token = listen topic, channel, => | |
@off(target, callback) | |
callback.apply null, arguments | |
# Stop listneing for a named event | |
off: (target, callback) -> | |
target = targetToHash(target) | |
if channels[target.channel]?[target.topic]?[callback] | |
delete channels[target.channel][target.topic] callback | |
`export default EventBrokerService` |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment