Monday, June 12, 2017

Release 0.2.1: Side project support

This new release supports my side projects by implementing html generation and http request helpers. It also improves the coverage measurement and this is the first version to be published again as an NPM package.

New version

Here comes the new version:

NPM Publishing

Starting from this version, the library will be published as an NPM package on every release.

The package was already existing since it was first published for version 0.1.4. However, the library has since been redesigned in a way that is not backward compatible. That's the reason why the MINOR version number was increased.

It is violating the normal backward compatibility rule but, actually, nobody was really using it... And I didn't want to increase the MAJOR number to 1 until the library is ready.

An .npmignore file instructs NPM which files should be included or not. The package is almost limited to the build folder.

HTML generation helper

I was watching the excellent funfunfunction video about the hidden costs of templating languages and, at some point, he showed some HTML generation helpers which syntax amazed me.

Hence, to support my side project, I decided to create my own HTML generation helpers based on this syntax.

For instance: var div = gpf.web.createTagFunction("div"), span = gpf.web.createTagFunction("span"); document.getElementById("placeholder") = div({className: "test1"}, "Hello ", span({className: "test2"}, "World!") ).toString();

Sadly, I realize that I completely forgot to document the feature properly. Fortunately, the tests demonstrate the feature.

HTTP Requests

The prominent feature of this version is the gpf.http.request helper. With it, you can trigger HTTP requests from any supported host using only one API. The response is given back to the script asynchronously through Promises.

The promise is resolved when the server provides a response, whatever the status code.

Some shortcuts are also defined to improve code readability:

gpf.http.get(requestUrl).then(function (response) { if (response.status === 500) { return Promise.reject(new Error("Internal Error")); } return process(response.responseText); }) .catch(function (reason) { onError(reason); });

The supported (and tested) features are:

  • Most common HTTP verbs
  • Possibility to provide request headers
  • Possibility to access response headers
  • Possibility to submit textual payload (on POST and PUT)
  • Access to response text

Other features will be added depending on the needs but this is enough to make it fully functional.

I will soon write an article about this very specific part as I had lot of challenges to test and implement it.

Improved coverage

Because each host benefits from its own http request implementation, it was important to assess the code coverage.

Up to this version, the coverage was measured with NodeJS through istanbul and mochaTest. Each host specific code was flagged to be ignored using the istanbul ignore comment.

However, as the library grows, more code was actually not verified.

After analyzing how istanbul generates, stores and consolidates the coverage information, I found a way to run the instrumented code on all hosts. A new grunt task consolidate all data into the global one.

In the end, there are still some branches / instructions that are ignored but they are all documented.

  • Statements ignored: 0.74%
  • Branches ignored: 1.77%
  • Functions ignored: 1.36%

Lessons learned

ECHO service

To be able to test the http helpers, I needed a server which responses could be controlled. I already had some middleware plugged inside the grunt connect task. I decided to create the ECHO service.

I took me a while to figure out the proper coding: for instance, I had to disable the cache because Internet Explorer was storing the request results and it was failing the tests.

Also, I had to change the way tests are running to transmit the http port information. This is done through the config global object.

Some code was definitely not tested

Having the coverage measured on almost every lines revealed sections of untested code. This leaded to some simplifications (boot for instance) but also to new tests.

Incomplete design for ending a stream

In order to prepare the CSV reader, I created a line adapter stream. It implements both the IReadableStream and IWritableStream interfaces. It caches the data being received with write until some line separator is detected.

However, because of caching, it requires a method to flush the buffered data.

As of now, I decided to create a method to explicitly flush the stream.

However, I am still wondering if it may change in the future: if you pipe several streams together, it could be convenient to have a standard way to flush all the different levels. One idea could be to introduce a special token that would be written at the end of a stream but then it would require all streams to implement it.

Next release

For now the project is on pause because of vacations. I will take some time to plan the next iteration more carefully but, still, I have to support my side project by creating a CSV reader as well as a record container.

No comments:

Post a Comment