Created
May 30, 2011 13:59
-
-
Save kixxauth/998935 to your computer and use it in GitHub Desktop.
Namespaced and "stateful" event emitter.
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
### | |
# Kris Walker <[email protected]> | |
# Copyright 2011 Licensed under the MIT License http://opensource.org/licenses/mit-license.php | |
# | |
# Namespaced and Stateful events | |
# * Event names are namespaced | |
# * Event name specificity is separated by a dot "." | |
# * Event names get more specific from left to right. | |
# | |
# - So when the event named "foo.bar" is emitted handlers attached to "foo" and "foo.bar" | |
# will be triggered. But if the "foo" event is emitted, only the handler attached to | |
# "foo" will be triggered. If the event "bar" is emitted, neither will be triggered. | |
# | |
# * Also, the emitter is stateful; it remembers the last data fired on each event. | |
# | |
# - So, when you attach a handler to the event "foo.bar" after it has already been fired, | |
# your handler will be triggered even though it technically missed the event. | |
# | |
# Gotcha: These events are not emitted or triggered on another event loop, which | |
# can wreak havoc on existing architecture. .emit() should default to async and there | |
# should be another method created called .emitSync() to avoid confusion. | |
# | |
# This code depends on jQuery ($) and a locally contrived "require()" system... but you | |
# can easily replace that code. | |
# | |
# And this could probably be optimized too. | |
### | |
declare (require, exports) -> | |
logging = require 'logging' | |
walkPath = (path, callback) -> | |
agg = [] | |
for part in path.split('.') | |
agg.push(part) | |
callback(agg.join('.')) | |
return | |
invoke = (fn, data, context) -> | |
data = $.isArray(data) and data or (data and [data] or []) | |
try | |
fn.apply(context or {}, data) | |
catch e | |
return e | |
return | |
class Registry | |
dict: {} | |
bind: (path, fn) -> | |
if not $.isArray(@dict[path]) | |
@dict[path] = [] | |
@dict[path].push(fn) | |
return @ | |
broadcast: (path, data, context) -> | |
callbacks = @dict[path] | |
if $.isArray(callbacks) | |
for fn in callbacks | |
err = invoke(fn, data, context) | |
if err | |
logging.log("callback error on '#{ path }'") | |
logging.log(err) | |
class Emitter | |
constructor: -> | |
@callbackRegistry = new Registry() | |
@eventMemo = {} | |
emit: (path, data, context) -> | |
context or= @ | |
walkPath path, (namespace) => | |
@callbackRegistry.broadcast(namespace, data, context) | |
@eventMemo[namespace] = {data: data, context: context} | |
on: (path, fn) -> | |
@callbackRegistry.bind(path, fn) | |
history = @eventMemo[path] | |
if history | |
{data, context} = history | |
err = invoke(fn, data, context) | |
if err | |
logging.log("callback error on '#{ path }'") | |
logging.log(err) | |
exports.Emitter = Emitter | |
return |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment