Hacking Liza
************

This chapter provides general information and guidance for [prospective] developers of Liza.

For writing classes; interfaces; and traits, developers should familiarize themselves with easejs. For writing unit tests, developers should be familiarize themselves with mocha and chai. For more information on the libraries used by Liza, see Libraries.

Most source files have a general structure that must be followed. For example, all such files must have a copyright header and must be named after the class they define or system under test. For more information, see Source Files.

Generally speaking, developers should be familiar with vanilla ECMAScript; DOM APIs; and have a basic understanding of Node.js for well-rounded Liza development. Writing this manual requires basic understanding of Texinfo. References for these topics and others are provided in see Developer Resources.

Source Files
============

This section describes conventions for organizing files, both in directory structure and in content.

Copyright Header
----------------

Every source file should begin with a copyright header including the appropriate years and license information. This ensures that this information is always available even if the file becomes separated from the source distribution (e.g. is distributed independently). Further, it is necessary to indicate that the source file is distributed under the GNU General Public License version 3 or later mdash that ``or later'' clause does not exist as part of the license itself, and so the mere presence of the license in COPYING is insufficient.

The copyright headers vary slightly between JavaScript and Texinfo source files, represented in f:cheader-js and f:cheader-texi respectively.

     /**
      * DESCRIPTION OF FILE
      *
      *  Copyright (C) 2017, 2018 R-T Specialty, LLC.
      *
      *  This file is part of the Liza Data Collection Framework
      *
      *  Liza is free software: you can redistribute it and/or modify
      *  it under the terms of the GNU General Public License as published by
      *  the Free Software Foundation, either version 3 of the License, or
      *  (at your option) any later version.
      *
      *  This program is distributed in the hope that it will be useful,
      *  but WITHOUT ANY WARRANTY; without even the implied warranty of
      *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      *  GNU General Public License for more details.
      *
      *  You should have received a copy of the GNU General Public License
      *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
      */

Figure: Example copyright header for JavaScript files

     @c  This document is part of the Liza Data Collection Framework manual.
     @c  Copyright (C) 2018 R-T Specialty, LLC.
     @c
     @c    Permission is granted to copy, distribute and/or modify this document
     @c    under the terms of the GNU Free Documentation License, Version 1.3
     @c    or any later version published by the Free Software Foundation;
     @c    with no Invariant Sections, no Front-Cover Texts, and no Back-Cover
     @c    Texts.  A copy of the license is included in the section entitled ``GNU
     @c    Free Documentation License''.

Figure: Example copyright header for JavaScript files

For more information, see ``How to Apply These Terms to Your New Programs'' under the gplvthree.

ECMAScript Strict Mode
----------------------

ECMAScript 5's Strict Mode (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode) throws errors in more situations that may lead to buggy code, allows for better optimization of ECMAScript code during runtime, and prohibits syntax that conflicts with future ECMAScript versions. It also enables certain features, like using let inside blocks.

It should always be enabled going forward as shown in f:strict-mode. The statement should immediately follow the copyright header (see Copyright Header), before any other code.

     // Copyright header

     'use strict';

     // ...

Figure: Enabling strict mode

Libraries Used
==============

Liza does not use many libraries. The primary reason for this was that few libraries useful to Liza existed during its initial development mdash Node.js and its community was still very young. With that said, care should be taken to ensure that libraries are added only after a careful analysis of its costs and benefits, as they add volatility to the whole system and may also introduce security vulnerabilities outside of our control. They further introduce maintenance obligations for keeping up with newer versions of those libraries and addressing backwards-compatibility concerns.

System Libraries
----------------

Liza was originally developed using JavaScript (first ECMAScript 3, and then ECMAScript 5). JavaScript does not natively support the classical object-oriented model familiar to users of more traditional classical object-oriented languages like Java, C++, C#, and PHP. Liza is built using easejs, which provides those familiar features. The primary language used by developers in the office that created Liza is PHP, which motivated the creation of ease.js to ease the burden of entry.

Consequently, Liza is written in a classical object-oriented style rather than using prototypes. The class keyword introduced in ECMAScript  is largely syntatic sugar around the prototype model and does not address the primary concerns of ease.js, nor does it provide traits.

The project is now migrating toward TypeScript, so new code should not use ease.js unless required and an effort should be made to move existing code away from ease.js. For more information on this migration, see TypeScript Migration.

Testing Libraries
-----------------

mocha is used as the test runner for JavaScript unit tests. chai is the assertion library. This differs from PHP development where a single system (PHPUnit) encompasses both of these needs.

Chai offers a few different styles of assertions (``should'', ``expect'', and ``assert''); Liza uses ``expect'' (http://www.chaijs.com/guide/styles/#expect).

A library to aid in mocking TypeScript classes needs to be researched.

UI Libraries
------------

jQuery was used in the past, but has been largely purged from the system (and continues to be removed) due to strong performance issues. Further, now that browser APIs have stabalized and Liza no longer needs to support as far back as Internet Explorer 6, the standard DOM APIs are more than sufficient.

Liza instead provides its own UI and DOM abstractions (src/ui) that have been optimized for Liza's needs.

There are modern frameworks that may overlap with the type of UI operations that Liza performs, as well as certain DOM optimizations that it performs; however, it is unlikely that such frameworks (e.g. React, Angular, Meteor) will ever be integrated, as the cost of doing so exceeds the marginal benefit.

Developer Resources
===================

MDN (https://developer.mozilla.org/en-US/docs/Web) (Formerly the ``Mozilla Developer Network''; see ''The Future of MDN: A Focus on Web Docs'' (https://blog.mozilla.org/opendesign/future-mdn-focus-web-docs/) for the history of the rename.) is an essential resource for web development in general, especially for JavaScript/ECMAScript and the various Web APIs. It contains resources for all levels, including for those unfamiliar with JavaScript (https://developer.mozilla.org/en-US/docs/Learn/JavaScript/First_steps). All developers should familiarize themselves with the resources available on MDN so that they understand what type of information is readily accessible for future reference.

An overview of TypeScript can be found in its Handbook (https://www.typescriptlang.org/docs/handbook/basic-types.html). The language borrows concepts from a number of others, so many concepts may be familiar to you. TypeScript uses structural typing (duck typing). In Liza, we also choose to implement nominal typing using ``branding'' (src/types/misc.d.ts). A language specification (https://github.com/microsoft/TypeScript/blob/master/doc/spec.md) is also available.

The Server (see Server) uses Node.js. Although it's largely abstracted away, there may be times where you need to touch on it, in which case the Node.js documentation (https://nodejs.org/en/docs/) will be helpful. However, it is important to note the version of Node.js that Liza is currently using, as it may be woefully out of date and require looking at older versions of the documentation.

This manual is written using Texinfo (https://www.gnu.org/software/texinfo/), which is the documentation format of the GNU operating system. The format is structured and well-suited for software documentation with output in a variety of formats. Looking at the source code of this manual will be helpful mdash it provides the general structure and numerous macros that are specific to Liza.

Data are persisted using MongoDB (https://www.mongodb.com/). Database operations in Liza are abstracted away, but it's helpful to understand how to query the database directly to understand how the system works and composes its data, and for the purposes of debugging.

For information on specific libraries used by Liza, see Libraries.

TypeScript Migration
====================

helpwanted

This section contains notes regarding a migration to TypeScript. It is intended to serve as a guide mdash it is not prescriptive.

Migrating Away From GNU ease.js
-------------------------------

Liza was originally written in. TypeScript now provides many features that ease.js was written to address, though not all (most notably traits).

Since ease.js was designed with JavaScript interoperability in mind, and TypeScript generates prototypes from classes, TypeScript classes serve as drop-in replacements under most circumstances. However, subtypes must be migrated at the same time as their parents, otherwise type checking in TypeScript cannot properly be performed. If this is a concern, type assertions (https://www.typescriptlang.org/docs/handbook/advanced-types.html#type-guards-and-type-assertions) can potentially be used to coerce types during a transition period in conjunction with ease.js' Class.isA (https://www.gnu.org/software/easejs/manual/easejs.html#Type-Checks-and-Polymorphism).

Often times you will need to reference a class or interface as a dependency before it has been migrated away from ease.js. To do this, create a corresponding .d.ts file in the same directory as the dependency. For example, if a class Foo is contained in Foo.js, create a sibling Foo.d.ts file. For more information, see Declaration Files (https://www.typescriptlang.org/docs/handbook/declaration-files/introduction.html) in the TypeScript handbook.

ease.js implements stackable Scala-like traits. Traits are not provided by TypeScript. Traits will therefore have to be refactored into, for example, decorators or strategies.

Structural Typing
-----------------

TypeScript implements structural typing (https://en.wikipedia.org/wiki/Structural_typing), also called duck typing. This means that any two types sharing the same ``shape'' are compatible with one-another.

For classes, this can be mitigated by defining private members, which then ensures that compatible types are indeed subtypes.

Interfaces can be used in either the traditional OOP sense, or as a means to define the shape of some arbitrary object. Since interfaces do not define implementation details, the distinction isn't important mdash it does not matter if we receive an instance of an object implementing an interface, or some object arbitrary that just happens to adhere to it.

In other instances where we want to distinguish between two values with otherwise compatible APIs, Nominal Typing below.

Nominal Typing
--------------

It is sometimes desirable to distinguish between two otherwise compatible types. Consider, for example, a user id and a Unix timestamp. Both are of type number, but it's desirable to ensure that one is not used where another is expected.

TypeScript doesn't directly support nominal typing (https://en.wikipedia.org/wiki/Nominal_typing), where compatibility of data types are determined by name. Liza uses a convention called ``branding'', abstracted behind a NominalType generic (defined in src/types/misc.d.ts).

type UnixTimestamp = NominalType<number, 'UnixTimestamp'>;
type Milliseconds  = NominalType<number, 'Milliseconds'>;

function timeElapsed( start: UnixTimestamp, end: UnixTimestamp ): Milliseconds
{
    return end - start;
}

const start = <UnixTimestamp>1571325570000;
const end   = <UnixTimestamp>1571514320000;

// this is okay
const elapsed = timeElapsed( start, end );

// this is not, since elapsed is of type Milliseconds
timeElapsed( start, elapsed );

Figure: Example of nominal typing

Consider the example in f:nom-type. Both UnixTimestamp and Milliseconds are a number type, but they have been defined in such a way that their names are part of the type definition. Not only does the compiler prevent bugs caused from mixing data, but nominal types also help to make the code self-documenting.

If you want to have self-documenting types without employing nominal typing, use type aliases.

There are no prescriptive rules for whether a type should be defined nominally.

In some cases, it's useful to use nominal types after having validated data, so that the compiler can enforce that assumption from that point forward. This can be done using type assertions (https://www.typescriptlang.org/docs/handbook/advanced-types.html#type-guards-and-type-assertions).

type PositiveInteger = NominalType<number, 'PositiveInteger'>;

const isPositiveInteger = ( n: number ): n is PositiveInteger => n > 0;

const lookupIndex<T>( arr: T[], i: PositiveInteger ): T => arr[ i ];

// untrusted input from the user
const user_input = readSomeValue();

if ( isPositiveInteger( user_input ) )
{
    // user_input is now of type PositiveInteger
    return lookupIndex( data, user_input );
}
Figure: Validating nominal types

In f:nom-assert above, we only assume something to be a PositiveInteger after having checked its value. After that point, we can use TypeScript's type system to ensure at compile time that we are only using positive integers in certain contexts.

Never cast values (e.g. using <PositiveInteger>user_input) when type predicates are provided, since that undermines type safety.