Matt Smith's Blog

Software Engineering related topics

Event Serialization in FubuMVC.ServerSentEvents

| Comments

The IServerEvent.Data property is defined by the interface to be an object. Hence this object needs to be serialized prior to broadcasting it down to connected clients. By default the FubuMVC.ServerSentEvents bottle will use the FubuMVC JSON serializer. So an example stream would look like this:

Example of stream with serialized data
1
2
3
4
id: <EVENT ID>/<EVENT NAME>\n
data: { "property" : "value" }\n
retry: <RECONNECTION TIME>\n
\n

You can easily change the serializer by implementing your own version of IDataFormatter then in your registry add ReplaceService<IDataFormatter, YOURIMPLEMENTATION>()

This is where appending the event name to the end of the event id comes in handy. You can route all of your events to the default message handler on the client, deserialize the data, then trigger a non-SSE event with the deserialized data, similar to how everything else is handled in FubuMVC.

FubuMVC.ServerSentEvents OOTB Support

| Comments

FubuMVC now has support for Server Sent Events (SSE)! I have recently used it for our internal call center dashboard here at Extend Health. Since it’s inception we are now seeing data on the dashboard within seconds of it happening, as opposed to the previous 5 second ajax refresh interval we were doing before.

Out of the box (OOTB) the FubuMVC.ServerSentEvents bottle already does just about everything you need on the server. The only thing you need to implement is a topic for which your events will be coordinated against. A topic is created by inheriting the FubuMVC.ServerSentEvents.Topic class. The topic will be used to coordinate how events are stored and pushed to the connected clients. This is accomplished by overriding the bool Eqauls(object item) and int GetHashCode() methods. See an example below:

SimpleTopic.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
using System;
using FubuMVC.ServerSentEvents;

namespace SomeNamespace
{
    public class SimpleTopic : Topic
    {
        public Guid SomeId { get; set; }

        public override bool Equals(object obj)
        {
            return Equals(obj as SimpleTopic);
        }

        public bool Equals(SimpleTopic other)
        {
            if (ReferenceEquals(null, other)
                return false;

            return ReferenceEquals(this, other) ||
                   SomeId.Equals(other.SomeId);
        }

        public override int GetHashCode()
        {
            return SomeId.GetHashCode();
        }
    }
}

When an event is published or when a client connects, the code will pass through a TopicFamily which is keyed by the topic object you define. A separate channel is created for each topic, which is where the method overrides come in. The diagram below is a BASIC flow of the how events are stored and retrieved:

Basic Flow

Now all you need to do is publish your events and connect a client. You publish events via the IEventPublisher. I will cover the event and how you can customize it in a future post. For now though the ServerEvent class is the basic OOTB event that you can use.

Publishing Events
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
using System;
using FubuMVC.ServerSentEvents;

namespace SomeNamespace
{
    public class PutHandler
    {
        private IEventPublisher _eventPublisher;

        public PutHandler(IEventPublisher eventPublisher)
        {
            _eventPublisher = eventPublisher;
        }

        public void Execute()
        {
            var topic = new SimpleTopic { SomeId = ...someid...  };
            _eventPublisher.WriteTo(topic, new ServerEvent("1", "data-1"));
        }
    }
}

Now the client can connect to the stream via http://<YOUR HOST>/_events/<TOPIC NAME> which would be http://localhost:8080/_events/SimpleTopic in my example.

The standard protocol for SSE according to W3C specifies that events should follow the format:

W3C SSE Standard
1
2
3
4
5
id: <EVENT ID>\n
event: <EVENT NAME>\n
data: <EVENT DATA>\n
retry: <RECONNECTION TIME>\n
\n

The bottle takes a slightly different stance on this subject. However, you can override it with your own if you like by replacing the IServerEventWriter service with your own implementation. The stream that comes OOTB follows this format:

FubuMVC.ServerSentEvents SSE Standard
1
2
3
4
id: <EVENT ID>/<EVENT NAME>\n
data: <EVENT DATA>\n
retry: <RECONNECTION TIME>\n
\n

This is done so that you only need to setup one event listener on the client, from which you can build up a standard for processing events on the client. Which I plan to describe more in future posts.

Writing Unit Testable JavaScript

| Comments

What is testable?

The first thing we can do that will render our JavaScript testable would be to avoid inline JavaScript like the plague! Now what do I mean by that? Well, simply don’t write JavaScript inline with your html, like so:

Inline JavaScript DON’T DO THIS
1
2
3
4
5
<!-- THIS IS BAD -->
<button onclick="someFunction('some value')">Button Text!</button>

<!-- BUT THIS IS EVEN WORSE -->
<button onclick="$.ajax({url: 'http://someurl.com', success: function(){/* do something */}})">Button Text!</button>

I see a number of problems with this approach, that are not only related to testability. The first being that this is incredibly difficult to read. I didn’t even include any inline CSS here, but can you imagine how bad this would look if I did! If you can’t tell by now I’m a big fan of clean and elegant code. As far as testability goes, I would have to rely on a tool that can read the HTML and parse out the JavaScript I wish to test. Why go through all that work! We can clean this up a bit, to a better more elegant solution:

Script Tag DON’T DO THIS
1
2
3
4
5
6
7
8
9
10
<!-- BETTER THAN PREVIOUS EXAMPLE
  - MORE ELEGANT
  - EASIER TO READ/MAINTAIN
  - BUT... STILL NOT TESTABLE) -->
<script type="text/JavaScript">
  function someFunction() {
    /* do something */
  };
</script>
<button onclick="someFunction()">Button Text!</button>

While this is a better approach to writing elegant JavaScript, it still doesn’t provide for easy testing. This is because it is still embedded in our HTML, and we would require some kind of parser just to extract it. The good thing about this though is the single, and simple method call from the onclick event property. This is the testable example:

Separate JavaScript File
1
2
3
function someFunction() {
  /* do something */
};
HTML File
1
2
<script type="text/javascript" src="/path/to/js/file"></script>
<button onclick="someFunction()">Button Text!</button>

The reason that I call this testable is because you can take this JavaScript and use it in nearly any medium completely separate from the view, ie. a test runner. The only drawback to the above example is that there is still a very small amount of JavaScript embedded in our HTML the onlick="someFunction()" a global method call. This pattern will quickly pollute the global namespace. The only way we can completely remove this, easily that is, is with the help of either a JavaScript library like JQuery, KnockoutJs, BackboneJs, etc.

Using Multiple Versions of KnockoutJs Simultaneously

| Comments

When new versions of Knockout are released that implement breaking changes, you may not have to time or resources to go back and update all of you legacy code base just to get the latest version. Using RequireJs you can adopt the latest changes without breaking old code. So let’s look at an example of an old app using Knockout 1.2.1:

index.js
1
2
3
4
5
6
7
8
9
10
11
12
<html>
  <head>
    <script type="text/javascript" src="scripts/jquery-1.6.min.js"></script>
    <script type="text/javascript" src="scripts/knockout-1.2.1.js"></script>
    <script type="text/javascript" src="scripts/v1.2.1.js"></script>
  </head>
  <body>
    <div>
      Loaded with <span data-bind="text: loadedVersion"></span>
    </div>
  </body>
</html>
v1.2.1.js
1
2
3
4
5
6
7
$(document).ready(function(){
  var appViewModel = function() {
    this.loadedVersion = ko.observable(!ko.computed ? 'Knockout v2.1.0' : 'Knockout v1.2.1');
  }

  ko.applyBindings(new appViewModel());
});

You you will notice that since ko.computed was introduced in v2.0.0 the loaded version that is displayed on the screen is “Knockout v1.2.1”

Now with the release of 2.1.0 which supports AMD loading, ko is not defined in global scope when knockout is loaded with an AMD loader such as RequireJs, CurlJs, etc. (We will use RequireJs for this demonstration, the AMD loader you wish to use is up to you. Each has it’s own pros and cons which goes beyond the scope of this document.). This means that we will be able to use both versions of Knockout simultaneously, with minor tweeks to the legacy code which will be explained momentarily. First let’s add our new features.

index.js [Updated]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<html>
  <head>
    <script type="text/javascript" src="scripts/jquery-1.6.min.js"></script>
    <script type="text/javascript" src="scripts/knockout-1.2.1.js"></script>
    <script type="text/javascript" src="scripts/v1.2.1.js"></script>
    <script type="text/javascript" data-main="scripts/init.js" src="scripts/require.js"></script>
  </head>
  <body>
    <div id="v210">
      Loaded with <span data-bind="text: loadedVersion"></span>
    </div>
    <div id="v121">
      Loaded with <span data-bind="text: loadedVersion"></span>
    </div>
  </body>
</html>
v1.2.1.js [Updated]
1
2
3
...
  ko.applyBindings(new appViewModel(), $('#v121')[0]);
...

Notice the specific DOM element that applyBindings is being applied to! This is done so that the binding is not applied to the entire document, this is the gotcha. You cannot apply bindings to the same DOM tree with different versions of Knockout. Now onto the new stuff loaded through an AMD Loader.

init.js
1
require(['v2.1.0'], function() {});
v2.1.0.js
1
2
3
4
5
6
7
define(['knockout-2.1.0'], function(ko){
  var appViewModel = function() {
    this.loadedVersion = ko.observable(ko.computed ? 'Knockout v2.1.0' : 'Knockout v1.2.1');
  }

  ko.applyBindings(new appViewModel(), $('#v210')[0]);
});

You can now go through as time permits and update your legacy features to utilize the newer Knockout libraries! Unfortunately you will not be able to use two versions together if they are both prior to v2.1.0, at least one must be this later version since all previous versions did not support AMD loading and ko was defined on the global variable.