479 lines
17 KiB
Plaintext
479 lines
17 KiB
Plaintext
@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''.
|
|
|
|
@node Hacking
|
|
@chapter Hacking Liza
|
|
@helpwanted
|
|
|
|
This chapter provides general information and guidance for
|
|
[prospective] developers of Liza.
|
|
|
|
@dnindex Libraries
|
|
@dnindex GNU ease.js
|
|
@dnindex Mocha
|
|
@dnindex Chai
|
|
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 @ref{Libraries}.
|
|
|
|
@dnindex Copyright Header
|
|
@dnindex Source Files
|
|
@dnindex Source File Naming
|
|
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,
|
|
@pxref{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
|
|
@pxref{Developer Resources}.
|
|
|
|
@menu
|
|
* Source Files:: Conventions for project files
|
|
* Libraries:: The few libraries used by Liza
|
|
* Developer Resources:: Where to look for more information
|
|
* TypeScript Migration:: Information on migrating to TypeScript
|
|
@end menu
|
|
|
|
|
|
@node Source Files
|
|
@section Source Files
|
|
@helpwanted
|
|
|
|
@dnindex Source Files
|
|
This section describes conventions for organizing files,
|
|
both in directory structure and in content.
|
|
|
|
@menu
|
|
* Copyright Header:: Important header at the top of all
|
|
source files
|
|
* ECMAScript Strict Mode:Strict Mode. Always indicate strict mode
|
|
@end menu
|
|
|
|
@node Copyright Header
|
|
@subsection Copyright Header
|
|
@dnindex Copyright Header
|
|
@dnindex License
|
|
@dnindex GNU General Public License version 3
|
|
@dnindex GNU General Public License version 3, Or Later
|
|
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@tie{}3 @emph{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 @file{COPYING} is
|
|
insufficient.
|
|
|
|
The copyright headers vary slightly between JavaScript and Texinfo
|
|
source files,
|
|
represented in @ref{f:cheader-js} and @ref{f:cheader-texi}
|
|
respectively.
|
|
|
|
@float Figure, f:cheader-js
|
|
@example
|
|
/**
|
|
* 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/>.
|
|
*/
|
|
@end example
|
|
@caption{Example copyright header for JavaScript files}
|
|
@end float
|
|
|
|
@float Figure, f:cheader-texi
|
|
@example
|
|
@@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''.
|
|
@end example
|
|
@caption{Example copyright header for JavaScript files}
|
|
@end float
|
|
|
|
For more information, see ``How to Apply These Terms to Your New
|
|
Programs'' under the @gplvthree{}.
|
|
|
|
|
|
@node Strict Mode
|
|
@subsection ECMAScript Strict Mode
|
|
@dnindex Strict Mode, ECMAScript
|
|
ECMAScript@tie{}5's
|
|
@url{https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode,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 @code{let} inside blocks.
|
|
|
|
It should always be enabled going forward as shown in @ref{f:strict-mode}.
|
|
The statement should immediately follow the copyright header
|
|
(@pxref{Copyright Header}),
|
|
before any other code.
|
|
|
|
@float Figure, f:strict-mode
|
|
@example
|
|
// Copyright header
|
|
|
|
'use strict';
|
|
|
|
// ...
|
|
@end example
|
|
@caption{Enabling strict mode}
|
|
@end float
|
|
|
|
|
|
|
|
@node Libraries
|
|
@section Libraries Used
|
|
@dnindex Libraries
|
|
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.
|
|
|
|
|
|
@subsection System Libraries
|
|
@dnindex GNU ease.js
|
|
Liza was originally developed using JavaScript
|
|
(first ECMAScript@tie{}3, and then ECMAScript@tie{}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@tie{}PHP.
|
|
Liza is built using @easejs{},
|
|
which provides those familiar features.
|
|
The primary language used by developers in the office that created
|
|
Liza is@tie{}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 @code{class} keyword introduced in ECMAScript@tie{} is largely
|
|
syntatic sugar around the prototype model and does not address the
|
|
primary concerns of ease.js,
|
|
nor does it provide traits.
|
|
|
|
@dnindex TypeScript
|
|
@emph{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 @xref{TypeScript Migration}.
|
|
|
|
|
|
@subsection Testing Libraries
|
|
@dnindex Mocha
|
|
@dnindex Chai
|
|
@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 @url{http://www.chaijs.com/guide/styles/#expect,``expect''}.
|
|
|
|
@devnotice{A library to aid in mocking TypeScript classes needs to be
|
|
researched.}
|
|
|
|
|
|
@subsection UI Libraries
|
|
@dnindex jQuery
|
|
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@tie{}6,
|
|
the standard DOM APIs are more than sufficient.
|
|
|
|
Liza instead provides its own UI and DOM abstractions
|
|
(@srcrefraw{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.
|
|
|
|
|
|
@node Developer Resources
|
|
@section Developer Resources
|
|
@dnindex Resources, Developer
|
|
@dnindex MDN
|
|
@url{https://developer.mozilla.org/en-US/docs/Web,MDN}@footnote{
|
|
Formerly the ``Mozilla Developer Network''; see
|
|
@url{https://blog.mozilla.org/opendesign/future-mdn-focus-web-docs/,''The Future of MDN: A Focus on 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 @url{https://developer.mozilla.org/en-US/docs/Learn/JavaScript/First_steps,unfamiliar with JavaScript}.
|
|
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.
|
|
|
|
@dnindex TypeScript
|
|
An overview of TypeScript can be found in its
|
|
@url{https://www.typescriptlang.org/docs/handbook/basic-types.html,Handbook}.
|
|
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''
|
|
(@srcrefraw{src/types/misc.d.ts}).
|
|
A @url{https://github.com/microsoft/TypeScript/blob/master/doc/spec.md,language specification}
|
|
is also available.
|
|
|
|
@dnindex Node.js
|
|
The Server (@pxref{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
|
|
@url{https://nodejs.org/en/docs/,Node.js documentation} 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.
|
|
|
|
@dnindex Texinfo, GNU
|
|
@cindex Documentation
|
|
This manual is written using
|
|
@url{https://www.gnu.org/software/texinfo/,Texinfo},
|
|
which is the documentation format of the GNU@tie{}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.
|
|
|
|
@dnindex MongoDB
|
|
@cindex Database
|
|
Data are persisted using @url{https://www.mongodb.com/,MongoDB}.
|
|
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,
|
|
@pxref{Libraries}.
|
|
|
|
|
|
@node TypeScript Migration
|
|
@section TypeScript Migration
|
|
@dnindex TypeScript
|
|
@helpwanted{}
|
|
|
|
This section contains notes regarding a migration to TypeScript.
|
|
It is intended to serve as a guide@mdash{
|
|
}it is not prescriptive.
|
|
|
|
|
|
@subsection Migrating Away From GNU ease.js
|
|
Liza was originally written in @easejs.
|
|
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,
|
|
@url{https://www.typescriptlang.org/docs/handbook/advanced-types.html#type-guards-and-type-assertions,type assertions}
|
|
can potentially be used to coerce types during a transition period
|
|
in conjunction with ease.js'
|
|
@url{https://www.gnu.org/software/easejs/manual/easejs.html#Type-Checks-and-Polymorphism,@samp{Class.isA}}.
|
|
|
|
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 @code{.d.ts} file in the same directory
|
|
as the dependency.
|
|
For example,
|
|
if a class @code{Foo} is contained in @file{Foo.js},
|
|
create a sibling @file{Foo.d.ts} file.
|
|
For more information,
|
|
see @url{https://www.typescriptlang.org/docs/handbook/declaration-files/introduction.html,Declaration Files}
|
|
in the TypeScript handbook.
|
|
|
|
ease.js implements stackable Scala-like traits.
|
|
Traits are @emph{not} provided by TypeScript.
|
|
Traits will therefore have to be refactored into,
|
|
for example,
|
|
decorators or strategies.
|
|
|
|
|
|
@subsection Structural Typing
|
|
@dnindex Typing, Duck
|
|
@dnindex Typing, Structural
|
|
TypeScript implements
|
|
@url{https://en.wikipedia.org/wiki/Structural_typing,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.
|
|
|
|
|
|
@subsection Nominal Typing
|
|
@dnindex Typing, Nominal
|
|
It is sometimes desirable to distinguish between two otherwise
|
|
compatible types.
|
|
Consider, for example, a user@tie{}id and a Unix timestamp.
|
|
Both are of type @code{number},
|
|
but it's desirable to ensure that one is not used where another is
|
|
expected.
|
|
|
|
TypeScript doesn't directly support
|
|
@url{https://en.wikipedia.org/wiki/Nominal_typing,nominal typing},
|
|
where compatibility of data types are determined by name.
|
|
Liza uses a convention called ``branding'',
|
|
abstracted behind a @code{NominalType} generic
|
|
(defined in @srcrefraw{src/types/misc.d.ts}).
|
|
|
|
@float Figure, f:nom-type
|
|
@verbatim
|
|
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 );
|
|
|
|
@end verbatim
|
|
@caption{Example of nominal typing}
|
|
@end float
|
|
|
|
Consider the example in @ref{f:nom-type}.
|
|
Both @code{UnixTimestamp} and @code{Milliseconds} are a @code{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 @emph{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
|
|
@url{https://www.typescriptlang.org/docs/handbook/advanced-types.html#type-guards-and-type-assertions,type assertions}.
|
|
|
|
@float Figure, f:nom-assert
|
|
@verbatim
|
|
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 );
|
|
}
|
|
@end verbatim
|
|
@caption{Validating nominal types}
|
|
@end float
|
|
|
|
In @ref{f:nom-assert} above,
|
|
we only assume something to be a @code{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.
|
|
|
|
@devnotice{Never cast values
|
|
(e.g. using @samp{<PositiveInteger>user_input})
|
|
when type predicates are provided,
|
|
since that undermines type safety.}
|