Jump To …

events.coffee

Require

EventEmitter = require('events').EventEmitter
balUtilFlow = require("#{__dirname}/flow.coffee")
debug = false

===================================== Event & EventSystem Extends the standard EventEmitter with support for: - blocking events - start and finish events - cycling synchronous events

Event

class Event

The name of the event

	name: null

Is the event currently locked?

	locked: false

Has the event finished running?

	finished: false

Apply our name on construction

	constructor: ({@name}) ->

EventSystem

class EventSystem extends EventEmitter

Event store initialised in our event function to prevent javascript reference problems

	_eventSystemEvents: null
	

Fetch the event object for the event

	event: (eventName) ->

Prepare

		@_eventSystemEvents or= {}

Return the fetched event, create it if it doesn't exist already

		@_eventSystemEvents[eventName] or= new Event(eventName)
	

Lock the event next(err)

	lock: (eventName, next) ->

Grab the event

		event = @event eventName

Grab a lock on the event

		if event.locked is false

Place the lock

			event.locked = true

Trigger our event then fire our callback

			try
				@emit eventName+':locked'
			catch err
				next?(err)
				return @
			finally
				next?()
		else

Wait until the current task has finished

			@onceUnlocked eventName, (err) =>
				return next?(err)  if err

Then try again

				@lock eventName, next
		

Chain

		@
	

Unlock the event next(err)

	unlock: (eventName, next) ->

Grab the event

		event = @event eventName

Release the lock

		event.locked = false

Trigger our event then fire our callback

		try
			@emit eventName+':unlocked'
		catch err
			next?(err)
			return @
		finally
			next?()

Chain

		@
	

Start our event 1. Performs a lock 2. Sets event's finished flag to false 3. Fires callback next(err)

	start: (eventName, next) ->

Grab a locak

		@lock eventName, (err) =>

Error?

			return next?(err)  if err

Grab the event

			event = @event eventName

Set as started

			event.finished = false

Trigger our event then fire our callback

			try
				@emit eventName+':started'
			catch err
				next?(err)
				return @
			finally
				next?()

Chain

		@
	

Finish, alias for finished

	finish: (args...) ->
		@finished.apply(@,args)
	

Finished our event 1. Sets event's finished flag to true 2. Unlocks the event 3. Fires callback next(err)

	finished: (eventName, next) ->

Grab the event

		event = @event eventName

Set as finished

		event.finished = true

Unlock

		@unlock eventName, (err) =>

Error?

			return next?(err)  if err

Trigger our event then fire our callback

			try
				@emit eventName+':finished'
			catch err
				next?(err)
				return @
			finally
				next?()

Chain

		@
	

Run one time once an event has unlocked next(err)

	onceUnlocked: (eventName, next) ->

Log

		console.log "onceUnlocked #{eventName}"  if debug

Grab the event

		event = @event eventName

Check lock status

		if event.locked

Wait until our event has unlocked to fire the callback

			@once eventName+':unlocked', next
		else

Fire our callback now

			next?()

Chain

		@
	

Run one time once an event has finished next(err)

	onceFinished: (eventName, next) ->

Grab the event

		event = @event eventName

Check finish status

		if event.finished

Fire our callback now

			next?()
		else

Wait until our event has finished to fire the callback

			@once eventName+':finished', next

Chain

		@
	

Run every time an event has finished next(err)

	whenFinished: (eventName, next) ->

Grab the event

		event = @event eventName

Check finish status

		if event.finished

Fire our callback now

			next?()

Everytime our even has finished, fire the callback

		@on eventName+':finished', next

Chain

		@
	

When, alias for on

	when: (args...) ->
		@on.apply(@,args)

Block an event from running next(err)

	block: (eventNames, next) ->

Ensure array

		if (eventNames instanceof Array) is false
			if typeof eventNames is 'string'
				eventNames = eventNames.split /[,\s]+/g
			else
				return next? new Error 'Unknown eventNames type'
		total = eventNames.length
		done = 0

Block these events

		for eventName in eventNames
			@lock eventName, (err) ->

Error?

				if err
					done = total
					return next?(err)

Increment

				done++
				if done is total
					next?()

Chain

		@

Unblock an event from running next(err)

	unblock: (eventNames, next) ->

Ensure array

		if (eventNames instanceof Array) is false
			if typeof eventNames is 'string'
				eventNames = eventNames.split /[,\s]+/g
			else
				return next? new Error 'Unknown eventNames type'
		total = eventNames.length
		done = 0

Block these events

		for eventName in eventNames
			@unlock eventName, (err) ->

Error?

				if err
					done = total
					return next?(err)

Increment

				done++
				if done is total
					next?()

Chain

		@
	

Emit a group of listeners asynchronously next(err,result,results)

	emitAsync: (eventName,data,next) ->

Get listeners

		listeners = @listeners(eventName)

Prepare tasks

		tasks = new balUtilFlow.Group(next)

Add the tasks for the listeners

		balUtilFlow.each listeners, (listener) ->
			tasks.push (complete) ->
				listener(data,complete)

Trigger asynchronously

		tasks.async()

Chain

		@

Emit a group of listeners synchronously next(err,result,results)

	emitSync: (eventName,data,next) ->

Get listeners

		listeners = @listeners(eventName)

Prepare tasks

		tasks = new balUtilFlow.Group(next)

Add the tasks for the listeners

		balUtilFlow.each listeners, (listener) ->
			tasks.push (complete) ->
				listener(data,complete)

Trigger synchronously

		tasks.sync()

Chain

		@

===================================== Export

module.exports = {Event,EventSystem}