Coding Standards

This section is concerned with providing specific details for LUA scripting within the system:inmation solution deployment. This information should be read in conjunction with the organization’s standard practices and processes, and where there may be conflict the coder should usually defer to the organization’s practices by default. The information provided below is not exhaustive, and may be subject to change without notice.

Libraries

For each Custom Development there shall be at least one library created.

All Libraries shall be defined in a Core object. Global libraries shall be defined in the Global (Master) Core and local libraries shall be defined in the Local Core. The solution requirements will determine whether a library should be Global (multiple use case across the organization) or local (specific to a particular instance or location’s needs).

The libraries shall be named according to the following naming convention:

  • All libraries shall start with the prefix gxpc

  • Global libraries shall start with gxpc-global

  • Local libraries shall start with gxpc-local

  • Library naming shall define their functional area and their function (in short form as needed), in lowercase and dash separated.

  • Library storage in the repository should use a filename "gxpc-global-area-function-lib.lua" consistent with the library naming and global/local instance.

This leads to the following examples:

  • gxpc-global-import-excel (stored as gxpc-global-import-excel-lib.lua in the repository)

  • gxpc-local-monitoring-network (stored as gxpc-local-monitoring-network-lib.lua in the repository)

There shall be no reuse of custom libraries between different custom developments in order to prevent cross dependencies and release blocks (See Architecture Principles).

Coding Standard

First Line Code

The first line of each library shall be a comment. This comment shall start with the module name of the library (optionally postfixed with the word "library"). This should be followed by a short description of the purpose of the library. It is important that this visibility of library name on the first line match the "ScriptLibrary.LuaModuleName" property of the object in which the library resides.

Example:

-- gxpc-global-import-excel : globally available library for importing Microsoft Excel data.

Version Numbers

Version numbers shall have a numerical major, minor and revision version separated by a dot ( e.g. 1.2.0).

Major should only be set to one '1' when the library development has been finished and the library is ready for productive use. It should be increased above '1', when the library has undergone major extensions or rework and is in production again.

Minor should be increased with every code change, which brings new features/functions/classes and the like. Minor should also be incremented where changes are made to fix bugs or other short-comings of the code. Revision is mostly used for ESI-TO-MARKDOWN compatibility, but can also be used for minor bugfixes.

Testing and approval activities shall not have an influence on the version. This ensures that the library under test is identical to the released library.

NOTE: The Version Number in the Library Header is the overriding version number of the library, and may differ significantly from any numbering implemented by system:inmation through Audit practices. There is NO DIRECT CORRELATION between the system:inmation object version numbering and that imposed on the library within the development and maintenance process.

Information Block

Each library must include an .INFO function, which will return pertinent information about the library from the INFO function call. It is recommended this is placed at the beginning of the library document.

The .INFO function should return (at a minimum) the fields highlighted in the example below:

function importExcelClass.INFO(_)
    return {
        version = { major=1,  minor=0, revision=0 },
        contacts = { {
                name="Anne Other",
                company="A Software Company",
                email=Anne.Other@asoftwarecompany.com
        }, },
        library = {
            modulename= "gxpc-global-import-excel",
            filename= "gxpc-global-import-excel-lib.lua",
            -- Filename is always modulename plus "-lib.lua" and the modulename
            -- must be used for the ScriptLibrary.LuaModuleName property.
            description=[[ Import from Excel ]]
        },
        dependencies = {
            {        modulename = "dkjson",
                    version = { major = 5,  minor = 3, revision = 0  }  }
        },
    }
end

It is the responsibility of the code author (or editor) to ensure that changes to version numbers, dependencies, descriptions, etc. are reflected consistently in both the Library Header and the .INFO function (as appropriate).

Version History

The change history and the description of the changes for each new version shall be tracked in the source control tool only. There shall be no release notes and version histories within the library itself, suffice for that necessary to adequately describe the function of the library.

Library Scope

The Lua libraries require a scope which needs to be returned at the end of the library. The library scope must always be local lib = {} and ended with return lib. See the basic example below.

-- gxpc-local-basic-textfunctions library
local lib={}
-- note: the required INFO function has been omitted for readability
function lib:UPPER(x)
    return tostring(x):upper()
end
return lib

Public Interfaces

The public interface of a library are all functions with CAPITALIZED names that always start with a letter. The Name shall be structured for readability by using the underscore _ character, e.g. CREATE_NEW_OBJECT().

Functions should be implemented for calling using the colon lib:FUNC() notation. Whether the implicit self is required in the function body or not is not important, it is a convention. However, if the self parameter is not used, to prevent “linter” problems, the classic dot-based declaration may be used.

For each public function the parameters (identified as required and optional) and the usage of the function, as well as a usage example, shall be provided in a comment above the function definition. The comment must be ESI-TO-MARKDOWN compatible (see https://guides.github.com/features/mastering-markdown/).

Each Property of the Class which shall be usable outside the library must be written in CAPITAL letters. In general the direct access to class properties from outside of the class should be restricted. Alternatively SET_… and GET_… methods shall be used to access the content by leveraging system:inmation objects.

Private Interfaces

Functions and properties which are only used within the library must be preceded by a underscore character sign and must be all lowercase. This indicates private members of the class. Alternatively the double underscore _ may be used. This makes internal (helper) functions easier to identify in the code.

Comments

All public interfaces must have comments as described above. Private interfaces do not require comments, but comments can be very helpful and are recommended.

Throughout the code use comments whenever appropriate. Try to describe what is not common knowledge and not 100% obvious from the code. Also use comments to give an short insight into what the intention of a function or variable is, unless that is 100% obvious. Comment as if the reader would be new to the functions and variables.

Object Orientation and Base Class

All libraries shall be created based on Object Oriented Programming (OOP) principles, therefore the library esi-class.lua shall be used as the base class for all libraries created.

Single inheritance is supported and shall be used wherever suitable; Multiple inheritance is currently not supported and must not be used.

If complex compounds are needed they can be created as aggregates of classes, where a central object of class X has different properties, which themselves are objects of class Y,Z, etc.

In each library create a new class which inherits from CLASS with a suitable name, representing the function or the functional area of the class, although this should not be the name of the library.

Error Handling

The usage of error and assert needs to be controlled, as they will break the execution in a system:inmation environment, which is very unlikely to be wanted in a production environment.

Instead, such functions should be wrapped so that the behavior can be changed in a single location.

An example of a wrapper for the assert function looks like below:

CLASS._assert= function(self, ...)
    if self._debug then
        return assert(...)
    else
        local suc, err = pcall(assert, ...)
        if not suc then
            self._log_error("ASSERT failed", err)
        end
    end
end

Static Code Analysis

For Lua there exists a static code analysis tool called “lua lint”, which is also available as extension for Visual Studio Code (here called “lua check”). Lua lint is capable of detecting various issues such as usage of undefined global variables, unused variables and values, accessing uninitialized variables, unreachable code and so on. Be aware that lua lint sometimes fails to interpret long complex structures correctly.

Therefore it is prohibited to disable lint warnings in any way. Lint warning shall always be an exception and must be commented. A reason for not being lint compliant must be given.

Source Code Control

All source codes and source-code documentation (markdown) shall be placed in a Git repository. The Repository to be used is to be determined. The repository is a private Git repository, and members need to be onboarded before they can access the url. In this repository the master branch shall be used for the development activities and regular bug fixes.

When a new version is to be released, the master branch is replicated into a side branch dedicated to that release. This way there will be branches for GXPC-1.0, GXPC-2.0 and so on.

In the unlikely case that the development has already proceeded on the master branch and a bugfix for a released version is needed, then the release branch can be used to develop or to back-port the required bugfix. In the release branch then a new version (e.g. 2.1) can be created and the bundle generated.

Publishing the results is done by creating a bundle (zip) and placing this bundle in an agreed upon delivery folder. This bundle must not be modified. All changes / fixes must be done on the master branch or a release branch. The creation of a bundle is done in a standardized way, by using a publish.ps1 powershell script available on root level of the repository. The script ensures that the file is suffixed by a release number associated to the 'git describe --tags' command.