dos

1.0.2 • Public • Published

dos -- DELIGHT object system

An advanced OO-Framework for JavaScript. This framework was used in production in the DELIGHT engine. Supports both node/io environments as well as all major browsers using browserify.

Install

To install just use npm.

    npm install dos

Example

A small example of defining and using a class in dos.

var dos = require("dos");
var namespace = {};
dos.$class(namespace, "subns.MyClass", {
    // The $ denotes the class' body.
    $: function(This, Super, Static, Public, Protected, Private) {
        // Declare a private field called myMember
        Private.$field("myMember");
        // A public constructor taking an arbitrary amount of params as a Params object
        Public.$init(function(Params) {
            // Initializes the declared private field myMember with the contents of the required constructor argument myMember
            Private.myMember = Params.get("myMember");
        });
        // a destructor disposing of the private member myMember
        Public.$dispose(function() {
        });
        // A public method called myMethod taking no args and returning Private.myMember
        Public.$method("myMethod", function() {
            return Private.myMember;
        });
    }
});
// use it right away
var myObject = new namespace.subns.MyClass();
// or export for later use
module.exports = namespace;

Functionality

Just a quick overview of what you can do with dos. This section is very rough and incomplete as of now. Feel free to ask and/or browse the code.

Defining

The main functionality of the dos is to define classes. There are different kinds of classes that you can define:

Normal Classes

Your standard classes that everybody should know.

dos.$class(ns, "Class", { /* ... */ });

Abstract Classes

Classes that cannot be instantiated using new -- they can contain interface methods.

dos.$abstract(ns, "AbstractClass", { /* ... */ });

Static Classes

Classes that only have a static part, most commonly used as util holders.

dos.$static(ns, "StaticClass", { /* ... */ });

Interfaces

Interfaces are orthogonal to the class hierarchy. They cannot contain data. You can implement multiple interfaces in one class.

dos.$interface(ns, "Interface", { /* ... */ });

Exceptions

Wrapping Error objects in dos. This functionality comes in handy when serializing exceptions (not covered in this guide),

dos.$exception(ns, "Exception", { /* ... */ });

Allocators

In dos you can control how new behaves on a class. The default is a PoolAllocator, but you can write custom ones using this notation. See below for a small example on how to subclass PoolAllocator.

dos.$allocator(ns, "Allocator", { /* ... */ });

Class Descriptor

In dos the actual class description is encoded in a class descriptor. Here are some of the most important options you can use. There are more advanced ones that this small guide does not cover. Please ask and/or look at the code for more information.

Extends

Simple single inheritance using the extends option.

var ns = {};
dos.$class(ns, "Class", {
    extends: SomeBaseClass
    /* ... */
});

Implements

Simple implementation of multiple interfaces using implements.

var ns = {};
dos.$class(ns, "Class", {
    implements: [ SomeInterface, SomeOtherInterface ]
    /* ... */
});

Non-Static

The $ sign holds a class definition function for all non-static members. There are many things that you can define inside of the function, most of which are outlined below. The parameters to the function are:

This A reference to the object.

Super A reference to the super class members

Public The public class definition functions of the class.

Protected The private class definition functions of the class.

Private The private class definition functions of the class.

var ns = {};
dos.$class(ns, "Class", {
    $: function(This, Super, Public, Protected, Private) {

        // fields 
        Public.$field("somePublicField", { autoDispose: true });
        Protected.$field("someProtectedField", {
            getter: Public
        });
        Private.$field("somePrivateField", { transient: true });

        // methods
        Public.$method("somePublicMethod", function() {
            return Private.somePrivateField;
        });
        Protected.$method("someProtectedMethod", function() {
            return "foo";
        });
        Private.$method("somePublicMethod", function(baz) {
            return baz;
        });

        // partial interface
        Public.$method("toBeImplemented");

        // getter
        Public.$getter("x", function() {
            return 0;
        });
        Protected.$getter("y", function() {
            return 1;
        });
        Private.$getter("z", function() {
            return 2;
        });

        // setter
        Public.$setter("x", function(v) {
            Private.somePrivateField = v;
        });
        Protected.$setter("y", function(v) {
            Private.somePrivateField = v;
        });
        Private.$setter("z", function(v) {
            Private.somePrivateField = v;
        });

        // constructor / init
        Public.$init(function(Defaults) {
            Defaults.someField = 42;
        }, function(Params) {
            Private.somePrivateField = Params.getType("someField", dos.Types.Number);
            Public.somePublicField = Params.getType("someField", someNamespace.SomeOtherClass);
        });

        // destructor / dispose
        Public.$dispose(function() {
            // free some system resources
        });

        Private.$reflect(function(Public, Protected, Private) {
            Public.$method("reflectedMethod", function() {
                return "reflected!";
            });
        });

    }
    /* ... */
});

Static

Most notably you define nested classes in the static part of your class definition. You can also define constants and even create a constant of the same type as the class that you are currently defining.

var ns = {};
dos.$class(ns, "Class", {
    static: function(This, Super, Public, Protected, Private) {
        // static constants
        Public.$const("somePublicStaticConstant", { value: "foo23" });
        Protected.$const("someProtectedStaticConstant", { value: 42 });
        Private.$const("somePrivateStaticConstant", { value: new ns.Class() });

        // nested classes
        dos.$class("StaticInnerClass", { /* everything supported as in outer class */ });
        
        /*
            everything as in $ except $reflect
        */

    }
    /* ... */
});

Interface

In interfaces you just write $method,$getter and so on but not supply an implementation. You will have to implement those in classes that use the interfaces.

var ns = {};
dos.$interface(ns, "Interface", {
    $: function(This, Super, Public, Protected, Private) {
        Public.$method("someInterfaceMethod");
        Public.$getter("someInterfaceGetter");
        Protected.$method("someProtectedInterfaceMethod");
    }  
    /* ... */
});

Allocators

Custom allocators allow you to control how objects are managed between init and dispose. The default should work well for most cases, but if you for example want to get some allocation pattern info for debugging purposes, you can subclass an Allocator.

var ns = {};

dos.$allocator(ns, "CustomAllocator", {
    extends: dos.PoolAllocator,
    $: function(This, Super, Static, Public, Protected, Private) {
        Public.$init(function(Params) {
        });
        Public.$method("alloc", function(ClassCtorProxy, Public) {
            // custom allocation code goes here
            return Super.alloc(ClassCtorProxy, Public);
        });
        Public.$method("dealloc", function(obj) {
            Super.dealloc(obj);
            // custom deallocation code goes here
        });
    }
}),         

dos.$class(ns, "SomeCustomAllocatorClass", {
    allocator: ns.CustomAllocator, 
    $: function(This, Super, Static, Public, Protected, Private) {
        /* ... */
    }
});

new ns.SomeCustomAllocatorClass(); // uses CustomAllocator

RTTI

The dos has a rich set of run-time type information features. Please note that they work on instances of as well as classes.

var ns = {};
dos.$class(ns, "subns.Class", {
    extends: SomeBaseClass,
    implements: [ SomeInterface, SomeOtherInterface ]
    /* ... */
});

ns.subns.Class.instanceof(SomeBaseClass);
ns.subns.Class.implements(SomeInterface);

var foo = new ns.subns.Class();

foo.instanceof(SomeBaseClass); // true
foo.implements(SomeOtherInterface); // true

SomeBaseClass.getAllDerivedClasses(); // outputs array: [ ns.subns.Class ]
SomeInterface.getAllImplementingClasses(); // outputs array: [ ns.subns.Class ]


ns.subns.Class.class; // this is the class ctor

ns.subns.Class.className; // "subns.Class" 
ns.subns.Class.simpleClassName; // "Class" 

ns.subns.Class.classHierarchy; // outputs array: [ ns.subns.Class.class, SomeBaseClass.class ];

Params

The dos works with named parameters. The init method (or constructor) of dos classes has a magic Params object which helps you validate the parameters given to the class at construction time.

Here are some examples, but there are more things you can do with it like extend on the validator pattern to write your own parameter validation.

var ns = {};
dos.$class(ns, "Class", {
    $: function(This, Super, Public, Protected, Private) {
        Public.$init(Params) {
            var x = Params.getType("x", dos.Types.Uint16Array);
            var y = Params.getOptionalType("y", dos.Types.RegExp);
            Private.myMember = Params.getType("myMember", dl8.core.Types.Number, dl8.core.Params.validIfNumberBetween(23, 42));
            /* ... TODO: see code as reference for now ... */
        });
    }
});

Tests

To run unit and coverage tests do the following on your favorite shell:

# install dev dependencies
npm install -d
# test in a browser
npm run test
# test in node
npm run testNode
# show test coverage
npm run coverage

Credits

(c) 2012-2015 xymatic GmbH. MIT License. See LICENSE file for more information.

Package Sidebar

Install

npm i dos

Weekly Downloads

8

Version

1.0.2

License

MIT

Last publish

Collaborators

  • fr-