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.
To install just use npm.
npm install dos
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;
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.
The main functionality of the dos is to define classes. There are different kinds of classes that you can define:
Your standard classes that everybody should know.
dos.$class(ns, "Class", { /* ... */ });
Classes that cannot be instantiated using new -- they can contain interface methods.
dos.$abstract(ns, "AbstractClass", { /* ... */ });
Classes that only have a static part, most commonly used as util holders.
dos.$static(ns, "StaticClass", { /* ... */ });
Interfaces are orthogonal to the class hierarchy. They cannot contain data. You can implement multiple interfaces in one class.
dos.$interface(ns, "Interface", { /* ... */ });
Wrapping Error objects in dos. This functionality comes in handy when serializing exceptions (not covered in this guide),
dos.$exception(ns, "Exception", { /* ... */ });
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", { /* ... */ });
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.
Simple single inheritance using the extends option.
var ns = {};
dos.$class(ns, "Class", {
extends: SomeBaseClass
/* ... */
});
Simple implementation of multiple interfaces using implements.
var ns = {};
dos.$class(ns, "Class", {
implements: [ SomeInterface, SomeOtherInterface ]
/* ... */
});
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!";
});
});
}
/* ... */
});
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
*/
}
/* ... */
});
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");
}
/* ... */
});
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
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 ];
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 ... */
});
}
});
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
(c) 2012-2015 xymatic GmbH. MIT License. See LICENSE file for more information.