Release 0.2.7: Quality and XML
- Stories and bugs implemented
- Sources
- Documentation
- NPM package
Release content
A smaller release
As announced during the release of version 0.2.6, the month of June was busy developing a sample application to support the UICon'18 conference.
Unexpectedly, another interesting project emerged from this development but this will be detailed later on the blog.
In the end, the bandwidth was limited to work on this release.
XML Serialization
This version introduces the IXmlContentHandler interface as well as the gpf.xml.Writer class to enable XML writing.
If you are not familiar with the Simple API for XML, there are tons of existing implementation in different languages. The Java one is considered to be normative.
To put it in a nutshell, SAX proposes an interface to parse and generate XML.
The parsing part might be implemented later, only the generation one is required today.
Here is an example of an XML generation piped to a string buffer:
const
writer = new gpf.xml.Writer(),
output = new gpf.stream.WritableString();
gpf.stream.pipe(writer, output).then(() => {
console.log(output.toString());
});
writer.startDocument()
.then(() => writer.startElement("document"))
.then(() => writer.startElement("a"))
.then(() => writer.startElement("b"))
.then(() => writer.endElement())
.then(() => writer.endElement())
.then(() => writer.startElement("c"))
.then(() => writer.endElement())
.then(() => writer.endElement())
.then(() => writer.endDocument());
Which leads to the following output:
<document><a><b/></a><c/></document>
Representing the following structure:
document | +- a | | | +- b | +- c
Since all the methods returns a promise, the syntax is quite tedious. When writing the first tests, it quickly became clear that its complexity could be greatly reduced by augmenting the result promise with the interface methods.
As a result, a wrapper was designed to simplify the tests leading to this improved syntax:
const
writer = new gpf.xml.Writer(),
output = new gpf.stream.WritableString();
gpf.stream.pipe(writer, output).then(() => {
console.log(output.toString());
});
wrap(writer).startDocument()
.startElement("document")
.startElement("a")
.startElement("b")
.endElement()
.endElement()
.startElement("c")
.endElement()
.endElement()
.endDocument();
This will surely be standardized in a future version.
Improved gpf.require
Preloading
The goal of the library is to support application development. As explained in the article My own require implementation, splitting the code into modules enforces better code. However, at some point, all these modules must be consolidated to speed up the application loading.
This version offers the possibility to preload the sources by passing a dictionary mapping the resources path to their textual content. As a result, when the resource is required, it does not need to be loaded.
Here is the proposed bootstrap implementation:
gpf.http.get("preload.json")
.then(function (response) {
if (response.status === 200) {
return JSON.parse(response.responseText);
}
return Promise.reject();
})
.then(function (preload) {
gpf.require.configure({
preload: preload
});
})
.catch(function (reason) {
// Document and/or absorb errors
})
.then(function () {
gpf.require.define({
app: "app.js" // Might be preloaded
}, function (require) {
require.app.start();
});
});
Modern browsers
One of the challenges of building a feature-specific version of the library (a.k.a. flavor) is to test it with modern browsers only. The compatibility layer of the library takes a significant part of it and is useless if the flavor's target is NodeJS or any recent browser.
Worst, while building the release, the tests were failing when 'old' browsers were configured.
So, the concurrent task was modified to include a condition on modern browsers.
These are considered modern:
- Chrome
- Firefox
- Safari (if on Mac)
Quality improvement
Abstract classes
Quality is also about providing tools to make sure that developers don't make mistake. Abstract classes concept is one of them. This version offers the possibility to create abstract classes by adding $abstract in their definition.
If one wants to deal with abstract methods, they can be defined with gpf.Error.abstractMethod. However, this won't prevent class instantiation.
Debugging with sources
Debugging the library can be laborious. I am more familiar with Chrome development tools and I sometimes use them with NodeJS. Because the sources are loading through the evil-ish use of eval, they don't appear in the debugger sources tab.
To solve that problem, source maps were applied.
To put it in a nutshell:
- Test files loading injects sourceURL before evaluation
- A special version of the boot.js file is handled by a new middleware. It generates a unique source file (sources.js) with its corresponding source map thanks to uglify-js.
As a result, sources appear:
Hosted automated code review
GitHub is a huge source of information. While browsing some repositories, I discovered two code review services that integrates nicely.
They both focus on code quality (based on static checks) and propose exhaustive report on potential issues or code smells found in your code.
Today, only the src folder of the repository is submitted for review.
It revealed some interesting issues such as:
- Code similarities, i.e. opportunity for code refactoring
- Code complexities:
Some were already known and have been addressed in this version (in particular src/compatibility/promise.js where plato was giving a little 74.46).
The surprise came from a class definition with more than 20 methods as it was considered an issue (src/xml/writer.js). After having diligently improved the code, by isolating the XML validation helpers, one must admit that it makes things more readable !
Finally, these tools rank the overall quality with a score that can be inserted in the project readme.
Lessons learned
From a pure development prospective, a lot was done in a very limited time. Since the quality of the code is enforced by the usual best practices (TDD, static code validation) but also measured (with plato), modifications are safe and immediately validated.
A lot was learned on JavaScript source mappings since it was required to enable debugging in the browser.
The relevance of the problems raised by the Code Climate tool was quit surprising: the overall project quality benefited from this integration.
Next release
The next release content is not even defined. For the next months, I will focus on a side project that requires all my attention.