Maker is a simple and powerful OO application toolkit for Javascript.
The central design goal of Maker is to provide a delcarative mechanism for defining classes, objects (instances of classes), and configurable command-line programs.
Maker supports both the classical and prototype patterns of implementing OO in a simple and unified manner. In addition, Maker is a Depedency Injection framework that allows for the creation of highly configurable re-usable software components and applications.
In particular, Maker provides mechanisms for:
- Defining objects and classes
- Defining re-usable software components and managing their lifecycle
- Defining top-level commandline interfaces with easy options parsing
- Writing synchronous-style programs with Fibers
- Managing application-level configuration
- Configuring and managing application logging
Using npm
% cd <your-app>
% npm install maker
From git:
% git clone [email protected]:objectlabs/maker.git
% cd <your-app>
% npm install <path-to-maker>
% node ./test/all.jsThe core of Maker is comprised of four operators:
- The
ooperator makes objects - The
oooperator makes classes - The
_ooperator resolves components by name - The
__operator that spawns Fibers
The o operator is used to make objects. The operator takes a
single object datum argument and returns an object based on the
supplied specification. The specification is an object that consists
of:
-
An optional
_typefield, whose value may be either aFunctionor (representing a class constructor) or another object. -
A series of name / value pairs specifying the properties of the object
The empty object
var o = require('maker').o(module)
o({})which is the same as
o({_type: Object})which simply evaluates to {}.
Simple object
var o = require('maker').o(module)
o({a: 1,
b: 2});Specifying a class via a constructor Function (in the classical style)
var o = require('maker').o(module)
function Person() {
this.name = "Some Person";
this.email = null,
this.age = 0;
}
o({_type: Person,
name: "Jo Smith",
email: "[email protected]",
age: 35});Specifying another object as a prototype
var o = require('maker').o(module)
var Person = o({
name: "Some Person",
email: null,
age: 0
});
o({_type: Person,
name: "Jo Smith",
email: "[email protected]",
age: 35});Nested objects
var o = require('maker').o(module)
o({_type: Person,
name: "Jo Smith",
email: "[email protected]",
age: 35,
address = o({
_type: Address
street: "100 Foo St.",
city: "San Francisco",
state: "CA",
zip: "93212"
})
});The oo operator is used to make classes. All oo expressions evaluate to a value that is a Function that can be used as a constructor. Like the o operator, the oo operator takes a single object argument. In this case the object specification is the specification for a class. The _type field can be used to specify superclass to extend and must be a Function value.
Classes defined with oo can optionally specify a constructor, which is a function to be used to initialize instance properties for objects of the defined class. Constructor functions are specified via the meta property _C.
Classes can define a super-class from which it extends via the _type meta property (the same way object specify which class they are an instance of when using the o operator).
If the class being defined has a super-class Maker will automatically chain constructors, calling the constructor of the super-class before calling the constructor of the class being defined.
You can use the _super method to call methods on your superclass. The method takes the name of the method as a string and returns a function.
var o = require('maker').o(module)
var oo = require('maker').oo(module)
var Animal = oo({
_C: function() {
this.name = "Some animal"
this.age = 0
this.weight = 0
},
say: function() {
return this.name;
}
})
var Dog = oo({
_type: Animal,
_C: function() {
this.name = "Some Dog"
},
say: function() {
return "woof: " + this._super('say')() // delegating to superclass
}
})
var fido = o({
_type: Dog,
name: "Fido",
age: 3,
weight: 10
})Properties can be defined as simple fieldname / value pairs
o({
name: "John Smith"
})or they can be defined dynamically with getters and setters as you would with Javascript's Object.defineProperty
o({
now: {
$property: {
get: function() {
return new Date()
}
}
}
})Object creation via the o operator follows this sequence:
- The
_typefield is evaluated. If it is a function it is then considered a constructor and a new instance of that Class is created. If it is an object that object is used as the new object's prototype. If no_typeis supplied the default value ofObjectis used. - If the class defines a constructor (via
_C) that constructor is called after calling the constructor of the class's_type(constructors defined by_Care automatically chained). - All field definitions in the object passed to the
ooperator are added to the newly created object - If the object has an
_initmethod that method is called - The newly created object is returned
Example using _init:
o({
port: 8080,
app: null,
db: null,
_init: function() {
this.app = express.createServer()
this.app.listen(this.port)
}
})TBD
Maker allows for the easy creation of command line programs with built-in argument parsing. You can use the _main method to define a top-level entry point, or "main" function, to your application.
Example:
var o = require('maker').o(module);
var _o = require('maker')._o(module);
module.exports = o({
port: null,
verbose: false,
_app: null,
cmdargs: { // supports nomnom definitions (see https://github.com/harthur/nomnom)
port: {
abbr: "p",
help: "port server should listen on",
required: false,
default: 8080
},
verbose: {
abbr: "v",
help: "enable verbose logging",
required: false,
default: false,
property: true // set this value as a field on this object when parsed as a cmdline option
}
}
_main: function(options) {
this.port = options.port
this._app = express.createServer()
this._app.listen(this.port)
}
})You can then call your program from the commandline like this:
% node <path-to-your-module> <options>
Example:
% node SimpleCmdlineApp -h
Usage: node SimpleCmdlineApp [options]
Options:
-p, --port port server should listen on [8080]
-v, --verbose enable verbose logging [false]
The arg-parser used by Maker is nomnom. For full documentation on how you specify cmdargs please see https://github.com/harthur/nomnom
TODO
- Document _o
- Document Fibers
- Revisit _super