How the shoutbox client sockets work
-
This is about the following file: sockets.js
We need more posts, why not write something about how the shoutbox works? Could be interesting for some people I suppose :P
The fun thing about the recent refactor of the client sockets is that you can call them by simple doing
Shoutbox.sockets.<action>(data, callback);
. This is possible because we add each message to the globalShoutbox.sockets
object. Doing this for every single message would take a lot of time and code, especially with the recent addition of commands. So how do we do this?As you can see at the beginning of the file there are 2 objects with a bunch of method names as key and socket events as value, these names will later be used as the
<action>
:var Messages = { getShouts: 'plugins.shoutbox.get', sendShout: 'plugins.shoutbox.send', removeShout : 'plugins.shoutbox.remove', editShout: 'plugins.shoutbox.edit', notifyStartTyping: 'plugins.shoutbox.notifyStartTyping', notifyStopTyping: 'plugins.shoutbox.notifyStopTyping', getOriginalShout: 'plugins.shoutbox.getOriginalShout', saveSettings: 'plugins.shoutbox.saveSetting', getSettings: 'plugins.shoutbox.getSettings', getUsers: 'user.loadMore', getUserStatus: 'user.isOnline' }; var Events = { onUserStatusChange: Messages.getUserStatus, onReceive: 'event:shoutbox.receive', onDelete: 'event:shoutbox.delete', onEdit: 'event:shoutbox.edit', onStartTyping: 'event:shoutbox.startTyping', onStopTyping: 'event:shoutbox.stopTyping' };
These are the default messages and events we work with. Extra events or messages required for commands etc are defined by the command itself (we get to this later).
After that we have a
Handlers
object, which essentially has all the default socket handlers required for the shoutbox to actually work. You can see some basic stuff ilkeonReceive
,onDelete
etc. You can pretty much guess by their name what their function is. Interesting here is thedefaultSocketHandler
:var Handlers = { onReceive: ..., onDelete: ..., onEdit: ..., onUserStatusChange: .., onStartTyping: ..., onStopTyping: ..., defaultSocketHandler: function(message) { this.message = message; var self = this; return function (data, callback) { if (typeof data === 'function') { callback = data; data = null; } socket.emit(self.message, data, callback); }; } };
In the next bit I explain how the
defaultSocketHandler
works.At the very end of the file we find what we actually “expose” to the public/global
Shoutbox
object.Shoutbox.sockets = { messages: Messages, events: Events, registerMessage: function(handle, message) { if (!Shoutbox.sockets.hasOwnProperty(handle)) { Shoutbox.sockets[handle] = new Handlers.defaultSocketHandler(message); } }, registerEvent: function(event, handler) { if (socket.listeners(event).length === 0) { socket.on(event, handler); } }, initialize: function() { for (var e in Events) { if (Events.hasOwnProperty(e)) { this.registerEvent(Events[e], Handlers[e]); } } for (var m in Messages) { if (Messages.hasOwnProperty(m)) { this.registerMessage(m, Messages[m]); } } } };
The first two keys,
messages
andevents
simply expose the default messages and events that we talked about at the beginning.registerMessage
is more interesting. Let’s take a closer look at it.registerMessage: function(handle, message) { if (!Shoutbox.sockets.hasOwnProperty(handle)) { Shoutbox.sockets[handle] = new Handlers.defaultSocketHandler(message); } },
registerMessage
first checks if we don’t already have the requested key in theShoutbox.sockets
object, if we don’t it adds a new key with a newdefaultSocketHandler
as value. We pass the message from the second argument to thedefaultSocketHandler
constructor, which, if you scroll up, is stored in the newly created object withthis.message = message;
. If you look at the return value ofdefaultSocketHandler
you can see that it returns a function that takes 2 arguments. This function essentially just emits a socket message with the data and callback as arguments. Because we store this newly created function as the value of theShoutbox.sockets[handle]
key, this allows us to do theShoutbox.sockets.<action>(data, callback);
from the beginning of this post.registerEvent
just registers a new event withsocket.io
for the passed in event and handler.intialize
simply loops over all theEvents
andMessages
and calls the appropriate functions to register all the default events and messages.Because we expose
registerMessage
andregisterEvent
commands and actions can easily add their own methods and event handlers to theShoutbox.sockets
object.Hopefully this post was somewhat interesting to read ;)