pubsub.coffee | |
---|---|
| do (window) -> |
Internal unique identifiers for messages and subscribers global to the PubSub constructor. This ensures cross hub references never clash. | huid = 1
suid = 1
muid = 1 |
SubscriberA subscriber is composed of a
| class Subscriber
constructor: (@publisher, @handler, @context) ->
@id = suid++
@online = true
@tip = null |
MessageA message is created and sent by it's | class Message
constructor: (@publisher, @content) ->
@id = muid++
@previous = @publisher.tip()
@publisher.messages.push @
copy: -> @content.slice()
available: -> @publisher.active |
PublisherMessages are send via a publisher whom broadcasts them to it's subscribers. Similarly to a subscriber, a publisher can also be deactivated. During this time, no messages will be queued nor broadcasted to it's subscribers. | class Publisher
constructor: (@topic) ->
@subscribers = []
@messages = []
@active = true
tip: -> @messages[@messages.length - 1]
_add: (subscriber) -> @subscribers.push subscriber |
Dereferences a subscriber from the local list | _remove: (subscriber) ->
len = @subscribers.length
while len--
if subscriber is @subscribers[len]
@subscriber.pop len
break
class PubSub
version: '0.3'
constructor: (@debug=false) ->
@id = huid++
@publishers = {}
@subscribers = {}
@messages = {}
_addPublisher: (topic) ->
if not (publisher = @publishers[topic])
publisher = new Publisher topic
@publishers[topic] = publisher
@log "Publisher '#{publisher.topic}' added"
return publisher
_addSubscriber: (publisher, handler, context) ->
subscriber = new Subscriber publisher, handler, context
@subscribers[subscriber.id] = subscriber
publisher._add subscriber
@log "Subscriber ##{subscriber.id} added"
return subscriber |
Subscribes a handler for the given publisher. An optional When a new (or existing) subscriber is created, by default, any
messages that have already been queued up by the publisher are
forwarded to the subscriber. This migration can be turned off
In addition to new subscribers, an existing subscriber can be
re-subscribed. If this method is used, the second parameter is
treated as the | subscribe: (topic, handler, context, migrate=true) ->
if topic instanceof Subscriber
subscriber = topic
subscriber.online = true
publisher = subscriber.publisher
migrate = handler or migrate
@log "Subscriber ##{subscriber.id} online"
else |
Handle the shorthand notation, only handling topic, subscriber and migrate. | if context in [true, 'tip', false]
[migrate, context] = [context, migrate]
publisher = @_addPublisher topic
subscriber = @_addSubscriber publisher, handler, context
if migrate then @_migrate publisher, subscriber, migrate
return subscriber |
Unsubscribes a subscriber. If | unsubscribe: (subscriber, complete=false) ->
if not subscriber instanceof Subscriber
return if not (subscriber = @subscribers[subscriber])
if complete
delete @subscribers[subscriber.id]
subscriber.publisher._remove subscriber
@log "Subscriber ##{subscriber.id} removed"
else
subscriber.online = false
@log "Subscriber ##{subscriber.id} offline" |
Publishes content for the specified | publish: (topic, content...) ->
publisher = @_addPublisher topic
return if not publisher.active
@log "'#{publisher.topic}' published ->", content
message = @_record publisher, content
@_applyToAll message
return publisher |
Handles applying a migration for a subscriber depending on it's state relative to the publisher and hub. | _migrate: (publisher, subscriber, type) ->
if publisher.messages.length
if type is 'tip'
messages = [publisher.tip()]
else
messages = publisher.messages
tip = if subscriber.tip then subscriber.tip.id else -1
@_migrateAll messages, subscriber, tip |
Applies a forward migration in the ascending order. | _migrateAll: (messages, subscriber, tip) ->
for message in messages
continue if message.id <= tip
@_applyToOne message, subscriber |
Broadcasts a mesage to all subscribers of the messages' publisher. | _applyToAll: (message, tip) ->
for subscriber in message.publisher.subscribers
@_applyToOne message, subscriber, tip
return |
Given a message and subscriber, forward the message to the subscriber
only if the subscriber is online and does not exceed the This hub is locked for the during of this call stack (relative to the handler) to prevent dependent messages from being recorded in the messages. Errors must be caught here to ensure other subscribers are not affected downstream. | _applyToOne: (message, subscriber) ->
return if not subscriber.online
@log "Subscriber ##{subscriber.id} <-", message.content
subscriber.handler.apply subscriber.context, message.copy()
subscriber.tip = message |
Create a new | _record: (publisher, content) ->
message = new Message publisher, content
@messages[message.id] = @
return message
if console?.log
PubSub::log = (msg, args...) ->
if @debug
msg = "Hub ##{@id}: #{msg}"
if args.length
console.log msg, args...
else
console.log msg
else
PubSub::log = ->
window.PubSub = PubSub
|