Skip to content

Instantly share code, notes, and snippets.

@nathan-appere
Last active April 6, 2018 23:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nathan-appere/08bf04336fa5b43e5ddbcd88d94fb641 to your computer and use it in GitHub Desktop.
Save nathan-appere/08bf04336fa5b43e5ddbcd88d94fb641 to your computer and use it in GitHub Desktop.
Quick implementation of an event-broker with channels + topics
`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