Web Development


Web Development13 Aug 2014 06:01 am

The AngularJS $http service is great for most any communication you need to do with the web services that are likely backing your single-page-application. However one place where it doesn’t quite do what you usually want is in the deserialization of dates.

Using $http to post a blog entry for example is pretty easy:

myPost = 
  id: 123
  title: 'Using HTTP Interceptors to Deserialize Dates'
  status: 'draft'
  creationDate: new Date()
  body: 'TBD'
 
$http.post('/api/blog', myPost)

This results in the following JSON being posted to the server:

{
  "id": 123
  "title": "Using HTTP Interceptors to Deserialize Dates",
  "status": "draft",
  "creationDate": "2014-08-13T10:13:39.399Z",
  "body": "TBD"
}

This is great. Notice that the service has automatically serialized the creation date to an ISO-8601 compliant string. I couldn’t ask for more… or could I?

What if I want to now get that post back:

$http.get("/api/blog/123").success (blog) ->
    console.log blog.creationDate    
    console.log typeof blog.creationDate

Assuming that the endpoint returns the exact same JSON that was just posted, this code will output:

2014-08-13T10:13:39.399Z
string

This is not the end of the world but it may not be what one is expecting. The expectation is usually that if I put a Date instance into a serialization function, then I get a Date instance out of the matching deserialization function.

Fortunately, this situation is easily remedied with a Angular http interceptor. Angular’s $httpProvider service allows one to configure interceptors that can pre-process all out-going requests before they are sent and post-process all received responses before handing them back to the calling application. A response post-processor to find all ISO-8601 date strings and convert them to actual Date objects is easy to implement.

angular.module('myModule', []).config([
  '$httpProvider'
  ($httpProvider) ->
    $httpProvider.interceptors.push [
      ->
        #Matches YYYY-MM-ddThh:mm:ss.sssZ where .sss is optional
        iso8601RegEx = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?Z$/
 
        response: (response) ->
 
          convertDates = (obj) ->
 
            for key, value of obj
 
              #If it is a string of the expected form convert to date  
              type = typeof value
              if type is 'string' and iso8601RegEx.test value
                  obj[key] = new Date(value)
 
              #Recursively evaluate nested objects
              else if type is 'object'
                convertDates value
 
          convertDates response.data
 
          response
    ]
])

And there you have it. Happy coding!

Web Development14 Jan 2014 09:03 am

Figuring out load order issues in Require.JS can be a big pain some times. However, if you are using the non-minified version, its easy to add some instrumentation that makes things clear. The Require.JS code is well documented so feel free to look around. I find the following methods most helpful to instrument.

@@ -1517,10 +1517,11 @@ var requirejs, require, define;
              * A load event could be a script load or just a load pass from a synchronous
              * load call.
              * @param {String} moduleName the name of the module to potentially complete.
              */
             completeLoad: function (moduleName) {
+                console.log('Completed load of ' + moduleName);
                 var found, args, mod,
                     shim = getOwn(config.shim, moduleName) || {},
                     shExports = shim.exports;
 
                 takeGlobalQueue();
@@ -1635,10 +1635,11 @@ var requirejs, require, define;
             },
 
             //Delegates to req.load. Broken out as a separate function to
             //allow overriding in the optimizer.
             load: function (id, url) {
+                console.log('Loading ' + id + ' from ' + url);
                 req.load(context, id, url);
             },
 
             /**
              * Executes a module callback function. Broken out as a separate function
@@ -1646,10 +1647,11 @@ var requirejs, require, define;
              * layer in the right sequence.
              *
              * @private
              */
             execCb: function (name, callback, args, exports) {
+                console.log('Initializing ' + name);
                 return callback.apply(exports, args);
             },
 
             /**
              * callback for script loads, used to check status of loading.

F.Y.I. This diff was applied to Require.JS 2.1.10

Automated Testing&Web Development17 Nov 2013 09:28 pm

Background

I have been working with our UI team recently to help them do better testing of their Backbone.js based single-page web application. We found it useful to bring in Squire.js to assist us in doing dependency injection into our many Require.js modules. Squire works quite well for this but invariably when writing these sorts of apps, you need to pull in libraries that are not AMD compliant at all or are simply “AMD aware.” When these sorts of modules enter the mix, Squire needs a little help.

jQuery is a great example of this sort of library. Recent versions are AMD aware, and include a define() call. Unlike a true AMD module, though, jQuery’s functionality is not fully encapsulated within the factory function provided to define. Indeed, none of jQuery’s initialization is handled in its factory function. Rather jQuery initializes upon load, just like any legacy JavaScript module. jQuery must do this in order to remain compatible with the millions of lines of non-AMD code that use it.

The Problem

This presents a problem when using Squire. In order to supply alternate versions of AMD modules to the module under test, Squire creates a new Require.JS context in which to load the module under test and its dependencies. Each new Require.JS context in turn loads afresh all the javascript files that are needed by that context. If all of these files are AMD modules, whose state is fully encapsulated within their factory functions, and only initialize when told to do so, then everything is fine. In the case of jQuery, or other non-AMD modules, which initialize upon load and store state in the global space, this can be a problem.

Consider this simple example. Two separate tests use Squire to load jQuery and the jQuery.BlockUI plug-in. Depending on timing details between your browser and your web server, both jQuery instances may load first, followed by both plugin instances, or they may load interleaved: jQuery, plugin, jQuery, plugin. The latter will work out well, the former (and in our experience most typical case) will not. In the former case, because of the shared global namespace, the second jQuery module loaded is the one that the global jQuery and $ variables point to when both of the BlockUI plug-ins load. Because of this, they both plug into the second jQuery instance leaving the first one plug-in free. For non-AMD modules who access jQuery from the global $ variable, this is not a problem. The instance they get has the plugin. For AMD modules that are handed a jQuery instance as an argument to their factory function, the context that loaded the first jQuery instance is stuck with that instance, which did not get its plug-in. This should lead to a lot of failing tests.

The Solution

At first pass, it may seem that the solution is to some how ensure the load order or otherwise ensure that both jQuery instances get their plug-in. That may be a theoretical ideal, but most non-AMD libraries were never designed to have multiple instances loaded and running and doing so can cause all kinds of problems. jQuery, because it supports loading multiple versions of itself at the same time actually handles this better than most. Nonetheless the best solution is to simply avoid loading multiple instances of non-AMD libraries. The question is how to do this in Squire.

Best we can tell, Squire does not explicitly support this. However, there is a simple workaround that can be put in place to enable it. The trick is to require jQuery, any plug-ins, and any other non-AMD modules that may be loading twice at the same time as you require Squire itself. Then for each of these libraries, tell Squire to mock the module and provide Squire with the initial instance as the mock. For modules that don’t return anything when invoked by Require (BlockUI plug-in in our case), Squire must still be told to mock it, but null can be provided as the value for the mock.

Here is some example code taken from a complete working example on github.

define([
  'vendor/squire/Squire', 
  'data/mock-data', 
  'vendor/jquery', 
  'vendor/jquery.blockui'], function(Squire, mock_data, $) {
  var injector = new Squire();
  injector.mock('data/real-data', mock_data);
 
  //Our fix to avoid loading jQuery and BlockUI twice
  injector.mock('jquery', function() { return $; });
  injector.mock('vendor/jquery.blockui', null);
 
  injector.require(['app/example-view'], function(View) {
    describe('Testing with Squire only', function() {
      var view = null;
      before(function() {
        view = new View();
      });
      it('$.blockUI should be defined', function() {
        assert.isDefined(view.getBlockUI(), '$.blockUI was undefined in example-view');
      });
      it('the data should be mocked', function() {
        view.getDataType().should.equal('mock');
      });
    });
  });
});

This approach works because by requiring the modules up front using Require and its default context, we rely on the standard Require logic to ensure the modules only load once. By telling Squire to mock the modules, it will not try to load them but will use the mocks provided, the common instances loaded by Require.

In the case of non-AMD libraries that return nothing to the factory function, such as the BlockUI plug-in above, simply requiring it will cause Require to load it. Upon load, the library does its thing (registers itself with jQuery) and that is all that is needed from it. Telling Squire to mock it keeps it from being loaded again in the new context, and because the library doesn’t provide a value, providing null as it mock value to Squire works just fine.

One final item to note is that in defining the mock for jQuery we cannot simply write

injector.mock('jquery', $ );

rather we must do

injector.mock('jquery', function() { return $; });

The reason for this is that contrary to the Squire documents, the second argument to mock is not always “the mock itself.” The second argument to mock works just like the final argument to define in Require. It may be an object or a function. If it is a function, then Require presumes it to be a factory function that it will invoke in order to get the mock. Since both jQuery and classes (i.e. constructors) are functions, they must be wrapped in factory in order not to be invoked as a factory.