catena

2.2.0 • Public • Published

catena

Prototype Helper for JavaScript

 

If you haven't used Grunt before, be sure to check out the Getting Started guide, as it explains how to create a Gruntfile as well as install and use Grunt plugins. Once you're familiar with that process, you may install this plugin with this command:

npm install catena --save-dev

Once the plugin has been installed, it may be enabled inside your Gruntfile with this line of JavaScript:

grunt.loadNpmTasks('catena');

 

Task

You must have the java command line tool installed for the google-closure-compiler npm module.

Run this task with the grunt catena command.

Task targets, files and options may be specified according to the Grunt Configuring tasks guide.

 

Description

catena is a framework that facilites the use of prototypes and forces coding conventions that make the maintenance of big projects easier.

 

Demo

Basic chess game built with catena. Click here for demo app.

 

Usage Example

// Project configuration.
grunt.config.init({
    catena: {
        options: {
            externs: ['BLITTER', 'YUI', 'jQuery', 'd3'],
            license: 'LICENSE'
        },
        dev {
            src: ['js/'], // Must be directories.
            dest: 'dist/app.js',
            options: {
             watch: true
            }
        },
        deploy {
            src: ['js/'],
            dest: 'dist/app.js',
            options: {
                test: true,
             deploy: true
            }
        }
    }
});

options.watch
Type: Boolean
Default: false

Set watch to true to write dest file everytime time you change a file in one of the src directories. If deploy is true then watch will not run.

options.test
Type: Boolean
Default: false

Run static code analysis tools before deploying. If errors are found deploy will be canceled.

options.lint
Type: Boolean
Default: true

Analyze code with eslint when test is true.

options.deploy
Type: Boolean
Default: false

Optimize dest file for deployment. Will perform ADVANCED_COMPILATION with the closure compiler after pre optimization phase if minify is set to true.

options.minify
Type: Boolean
Default: true

dest file will be minified when deploy is true. If minify is false then dest file will be beautified instead.

options.minifyLanguageIn
Type: String
Default: ECMASCRIPT_2019

The specification parsed src files should conform to when minifying with closure compiler.

options.minifyLanguageOut
Type: String
Default: ECMASCRIPT_2015

The specification minified dest file should conform to when minifying with closure compiler.

options.license
Type: String

Path to file containing license agreement.

options.externs
Type: String[]
Default: []

List of globally exposed dependencies (libraries, frameworks, etc.) that prevent the closure compiler from throwing an error when minifying, externs are only applied when test or deploy is true.

When working with external modules in catena, you should access properties and invoke methods using string literals. This will prevent the closure compiler from minifying property names for external modules. The closure compiler is set to use ADVANCED_COMPILATION always, by using string literals the compiler will leave property names as they are.

// Wrong
BLITTER.getImageData('test-icon');
 
require('path').isAbsolute('/');
 
// Correct
BLITTER['getImageData']('test-icon');
 
require('path')['isAbsolute']('/');

 

Project Structure

All JavaScript files should be placed inside the src directories, all of them will be concatenated recursively. All projects must have a CLASS.Main module declared as the entry point of the application.

You should only declare one module per file. catena will parse all JavaScript files in the src directories individually when deploy argument is used. Parsing will fail if more than one module is declared in a file.

srcDir
│   Main.js
│
├─── dir-one
│    │   FileOne.js
│    └─  FileTwo.js
│
└─── dir-two
     └─  FileThree.js

 

CLASS

References all of the modules that can be instantiated. Always remember to declare the append property for all CLASS modules as object literals. Properties declared in the append object are tied to the prototype of the CLASS module.

Here's a list of internal properties exposed by catena (inside the prototype of CLASS modules):

  • $isClass
  • $applied
  • $parentName
  • $name
// Parent.js
 
CLASS.Parent = function () {
    // Constructor
};
 
CLASS.Parent.append = {
    // Properties declared in append will be shared by
    // all instances of the module in the prototype.
};
 
// Child.js
 
extend('Parent', 'Child');
 
CLASS.Child = function () {
    // Call parent's constructor and inherit all of its properties.
    this.super();
};
 
CLASS.Child.append = {
    // Properties that share the same name to any of the Parent's
    // append properties can be overwritten or overridden.
};

extend

Chain the child's prototype with the parent's prototype. Always declare at the top of the file.

extend (parentName: String, childName: String)

super

Call the parent's constructor.

super (args: Array)
 
// Example
// Point.js
 
CLASS.Point = function (x, y) {
    this.x = x;
    this.y = y;
};
 
// append omitted for brevity.
 
// Square.js
 
extend('Point', 'Square');
 
CLASS.Square = function (x, y, width, height) {
    // Pass arguments into the parent's constructor.
    this.super(x, y);
 
    this.width = width;
    this.height = height;
};
 
// append omitted for brevity.

abstract

Flag a method as abstract.

abstract ()
 
// Example
// Shape.js
 
CLASS.Shape = function () {};
 
CLASS.Shape.append = {
    // If method is invoked directly, program will error out.
    calculateArea: function () {
        this.abstract();
    }
};
 
// Square.js
 
extend('Shape', 'Square');
 
// Omitted constructor for brevity.
 
CLASS.Square.append = {
    calculateArea: function () {
        // Calculate the area of the square with overwritten method.
    }
};
 
// Triangle.js
 
extend('Shape', 'Triangle');
 
// Omitted constructor for brevity.
 
CLASS.Triangle.append = {
    calculateArea: function () {
        // Calculate the area of the triangle.
    }
};

Overriding Methods

Invoke a method from a parent class while keeping the same context (instance reference).

// Parent.js
 
CLASS.Parent = function () {};
 
CLASS.Parent.append = {
    callMe: function () {}
};
 
// Child.js
 
extend('Parent', 'Child');
 
CLASS.Child = function () {
    this.super(); // Call parent's constructor.
};
 
CLASS.Child.append = {
    // The _$_ shorthand references all CLASS module prototypes.
    callMe: function () {
        _$_.Parent.callMe.call(this);
 
        alert('With _$_ we can expand the functionality of Parent.callMe in Child.callMe');
    }
};

 

CONST

Constants used inside the app should be declared here. Properties declared inside CONST cannot be changed after the CLASS.Main module is initialized. Nested objects and arrays are recursively frozen too.

Here's a list of constants exposed by catena (inside CONST):

  • $DEV: Will always be true unless you run catena with the deploy argument. Useful for performing tests at runtime prior to having your code deployed.
CONST.GRAVITY = 9.8;

 

SINGLE

Singletons reference object literals, like the append property in CLASS modules. If the init or postInit method are declared in a SINGLE module, they will be invoked prior to CLASS.Main being instantiated.

Here's a list of internal properties exposed by catena (inside SINGLE modules):

  • $isSingle
  • $name
SINGLE.Mouse = {
    // Declaring init or postInit as something other than a function will throw an error.
    // init and postInit will be unreachable after being invoked at the start of the program.
    init: function () {
        // Initialize singleton properties.
    },
 
    // postInit is invoked after all init methods are invoked.
    postInit: function () {
        // Interact with other initialized singletons.
    }
};

 

Access Modifiers

Public properties can be read, written and invoked externally.

this.x = 0;

Private properties can only be read, written and invoked internally.

this._x = 0;

Protected properties can only be read, written and invoked by instances of the same CLASS module.

this.__x = 0;

It is strongly advised to not start any references with $ as catena uses this convention internally to solve dependencies at runtime. The only internal properties you should access are the ones declared in CONST. Internal properties exposed in CLASS and SINGLE modules are used to solve dependencies at runtime and help with debugging when CONST.$DEV is true, you should never access them in your application as they will not be declared when deploying.

// Wrong
CLASS.Test = function () {
    this.$testValue = 0;
};
 
CLASS.Test.append = {
    $testMethod: function () {}
};
 
let $testFunction = function () {};
 
// Correct
if (CONST.$DEV) {
    // Do expensive test.
}

 

Helper API

Functions that catena uses internally and are exposed to be used externally also.

 

Error Functions

throwError

All arguments are optional. Type will always be shown as uppercase. The index determines how far back in the call stack must be traveled to find the method that will be pretty printed beside the module's name in the message.

throwError (message: String, type: String, module: Object, index: Number)

throwArgumentError

For throwArgumentError the last two params are optional but the first two are required for the error message.

throwArgumentError (name: String, type: String, module: Object, index: Number)

 

Validation Functions

Type checking functions.

isNaN,  isNull,  isArray,  isEmptyArray,  isNonEmptyArray,  isObject,  isNumber,  isString,  isEmptyString,  isNonEmptyString,  isBoolean,  isFunction,  isUndefined

All of these functions require only one argument.

func (arg: *) : Boolean
 
// Example
isArray([]);

isInstance

isInstance is unique compared to the other validation functions as it requires two arguments instead of one.

isInstance (type: *, arg: *) : Boolean
 
// Example
isInstance(CLASS.Shape, new CLASS.Triangle());

 

Test Functions

testArray,  testEmptyArray,  testNonEmptyArray,  testObject,  testNumber,  testString,  testEmptyString,  testNonEmptyString,  testBoolean,  testFunction

Functions that will check if type is valid and error the program out with throwArgumentError if type is invalid.

testArray (arg: *, argName: String, module: Object, errorIndex: Number)

testInstance,  testOptionalInstance

testInstance and testOptionalInstance are unique as they require two more arguments than the other functions.

testInstance (type: *, arg: *, typeName: String, argName: String, module: Object, errorIndex: Number)

Package Sidebar

Install

npm i catena

Weekly Downloads

0

Version

2.2.0

License

MIT

Unpacked Size

100 kB

Total Files

17

Last publish

Collaborators

  • jamarante