From ac656b51e8888a355010a301771bdf66bdd989fa Mon Sep 17 00:00:00 2001 From: Ritchie Martori Date: Fri, 17 May 2013 17:56:11 -0700 Subject: [PATCH] Remove aux test --- node_modules/sl-module-loader/.npmignore | 11 + node_modules/sl-module-loader/README.md | 246 +++++++++++++++ .../example/express-app/app.js | 41 +++ .../express-app/models/user/config.env.json | 7 + .../express-app/models/user/config.json | 9 + .../example/express-app/package.json | 14 + .../express-app/routes/bar/config.json | 6 + .../express-app/routes/foo/config.env.json | 12 + .../express-app/routes/foo/config.json | 6 + .../express-app/routes/hello/config.json | 6 + .../express-app/routes/users/config.json | 6 + .../express-app/routes/users/users-route.js | 27 ++ .../express-app/stores/memory/config.json | 6 + .../express-app/stores/mongo/config.json | 8 + .../another-module/another-module-class.js | 9 + .../module-script/another-module/config.json | 7 + .../example/module-script/example.js | 5 + .../module-script/my-module/config.json | 8 + .../example/module-script/my-module/main.js | 7 + .../my-module/my-module-class.js | 9 + .../sample-app/custom-scripts/config.json | 6 + .../sample-app/custom-scripts/construction.js | 9 + .../example/sample-app/default/config.json | 6 + .../example/sample-app/example.js | 12 + .../example/sample-app/hello/config.json | 6 + .../example/sample-app/hello/hello.js | 14 + .../example/sample-app/inter-deps/config.json | 6 + .../sample-app/inter-deps/my-module.js | 18 ++ .../example/sample-app/my-module-type.js | 9 + node_modules/sl-module-loader/index.js | 6 + .../sl-module-loader/lib/module-loader.js | 283 ++++++++++++++++++ node_modules/sl-module-loader/lib/module.js | 209 +++++++++++++ .../sl-module-loader/lib/patched-module.js | 66 ++++ node_modules/sl-module-loader/package.json | 23 ++ .../test/module-loader.test.js | 24 ++ .../sl-module-loader/test/module.test.js | 48 +++ node_modules/sl-module-loader/test/support.js | 5 + test/asteroid.test.js | 22 -- 38 files changed, 1200 insertions(+), 22 deletions(-) create mode 100644 node_modules/sl-module-loader/.npmignore create mode 100644 node_modules/sl-module-loader/README.md create mode 100644 node_modules/sl-module-loader/example/express-app/app.js create mode 100644 node_modules/sl-module-loader/example/express-app/models/user/config.env.json create mode 100644 node_modules/sl-module-loader/example/express-app/models/user/config.json create mode 100644 node_modules/sl-module-loader/example/express-app/package.json create mode 100644 node_modules/sl-module-loader/example/express-app/routes/bar/config.json create mode 100644 node_modules/sl-module-loader/example/express-app/routes/foo/config.env.json create mode 100644 node_modules/sl-module-loader/example/express-app/routes/foo/config.json create mode 100644 node_modules/sl-module-loader/example/express-app/routes/hello/config.json create mode 100644 node_modules/sl-module-loader/example/express-app/routes/users/config.json create mode 100644 node_modules/sl-module-loader/example/express-app/routes/users/users-route.js create mode 100644 node_modules/sl-module-loader/example/express-app/stores/memory/config.json create mode 100644 node_modules/sl-module-loader/example/express-app/stores/mongo/config.json create mode 100644 node_modules/sl-module-loader/example/module-script/another-module/another-module-class.js create mode 100644 node_modules/sl-module-loader/example/module-script/another-module/config.json create mode 100644 node_modules/sl-module-loader/example/module-script/example.js create mode 100644 node_modules/sl-module-loader/example/module-script/my-module/config.json create mode 100644 node_modules/sl-module-loader/example/module-script/my-module/main.js create mode 100644 node_modules/sl-module-loader/example/module-script/my-module/my-module-class.js create mode 100644 node_modules/sl-module-loader/example/sample-app/custom-scripts/config.json create mode 100644 node_modules/sl-module-loader/example/sample-app/custom-scripts/construction.js create mode 100644 node_modules/sl-module-loader/example/sample-app/default/config.json create mode 100644 node_modules/sl-module-loader/example/sample-app/example.js create mode 100644 node_modules/sl-module-loader/example/sample-app/hello/config.json create mode 100644 node_modules/sl-module-loader/example/sample-app/hello/hello.js create mode 100644 node_modules/sl-module-loader/example/sample-app/inter-deps/config.json create mode 100644 node_modules/sl-module-loader/example/sample-app/inter-deps/my-module.js create mode 100644 node_modules/sl-module-loader/example/sample-app/my-module-type.js create mode 100644 node_modules/sl-module-loader/index.js create mode 100644 node_modules/sl-module-loader/lib/module-loader.js create mode 100644 node_modules/sl-module-loader/lib/module.js create mode 100644 node_modules/sl-module-loader/lib/patched-module.js create mode 100644 node_modules/sl-module-loader/package.json create mode 100644 node_modules/sl-module-loader/test/module-loader.test.js create mode 100644 node_modules/sl-module-loader/test/module.test.js create mode 100644 node_modules/sl-module-loader/test/support.js diff --git a/node_modules/sl-module-loader/.npmignore b/node_modules/sl-module-loader/.npmignore new file mode 100644 index 00000000..3e9ee8b0 --- /dev/null +++ b/node_modules/sl-module-loader/.npmignore @@ -0,0 +1,11 @@ +.DS_Store +*.seed +*.log +*.csv +*.dat +*.out +*.pid +*.swp +*.swo +node_modules/ +.idea diff --git a/node_modules/sl-module-loader/README.md b/node_modules/sl-module-loader/README.md new file mode 100644 index 00000000..4300df32 --- /dev/null +++ b/node_modules/sl-module-loader/README.md @@ -0,0 +1,246 @@ +# sl-module-loader +v0.0.1 + +## Purpose + +The `sl-module-loader` allows your program to register classes (or types) that are instantiated via configuration files. Configuration files point to an implementation `module` constructor. The `module`'s job is to construct a useful instance with the given configuration options. This allows programs to be free from bootstrapping code and manageable via config. + +## Install + + slnode install sl-module-loader + +## Example + +Given a simple `Dog` module: + + function Dog(options) { + this.options = options; + } + + Dog.prototype.speak = function() { + console.log('roof', 'my name is', this.options.name); + } + + module.exports = Dog; + +And a set of `config.json` files: + + /my-app + /fido + config.json + /santas-little-helper + config.json + /rex + config.json + /node_modules + /dog + index.js + package.json + +Where a `config.json` looks like this: + + { + "module": "dog", // the "dog" module + "options": { + "name": "fido" + } + } + +We can load up all the dogs like so (app.js): + + var moduleLoader = require('sl-module-loader').create('my-app'); + + moduleLoader.load(function (err, modules) { + if(err) throw err; + + moduleLoader + .instanceOf('dog') // a module in node_modules or declared as a dependency in package.json + .forEach(function (m) { + m.speak(); + }); + }); + +The above calls a method on all module instances that inherit from `Dog` and outputs: + + roof my name is fido + roof my name is santa's little helper + roof my name is rex + +## Creating Module Classes + +The purpose of a module class is to take meaningful input (configuration, options, dependencies) and create a useful output: a module instance. + +### Module Classes + +A module class is a `node_module` that exports a constructor inheriting from the `Module` class. + + var inherits = require('util').inherits; + var Module = require('sl-module-loader').Module; + + module.exports = Dog; + + function Dog(options) { + this.options = options; + } + + inherits(Dog, Module); + + Dog.prototype.speak = function() { + console.log('roof', 'my name is', this.options.name); + } + +Module classes may define dependency contracts that tell the module loader to provide dependencies of a given module class during construction. + + function MyComplexModule() { + Module.apply(this, arguments); + console.log('loaded dependencies', this.dependencies); // {'my-dependency': } + } + + MyComplexModule.dependencies = { + 'my-dependency': 'another-module-class' + } + +#### Module Class Options + +Module classes may also describe the options they accept. This will validate the configuration of module instance and guarantee the module class constructor has enough information to construct an instance. + +Here is an example options description for a database connection module class. + + DatabaseConnection.options = { + 'hostname': {type: 'string', required: true}, + 'port': {type: 'number', min: 10, max: 99999}, + 'username': {type: 'string'}, + 'password': {type: 'string'} + }; + +**key** the option name given in `config.json`. + +**type** must be one of: + + - string + - boolean + - number + - array + +**min/max** depend on the option type + + { + min: 10, // minimum length or value + max: 100, // max length or value + } + +#### Module Events + +Module classes may also emit and listen to events. By default a Module will emit the following events: + +**destroy** + +Emitted when a module instance is being destroyed during a `moduleLoader.reset()`. Modules should cleanup any connections and unbind all event listeners. + +### Configuration + +Each module instance is defined by creating a `config.json` file in a directory with the module's name. + + /my-module-instance + config.json + other-files.txt + index.js + +This directory should contain files related to the module instance. For example it might contain a script that `require()`s the module instance. + +#### config.module + +The node module name that exports the module class that constructs the config's module instance. + + { + "module": "my-module-class" + } + +#### config.options + +Defines arbitrary options. A `file-upload` module might have an `uploads` option. + + { + "module": "file-upload", + "options": { + "uploads": "/tmp" + } + } + +#### config.dependencies + +Defines other module instances the configured instance depends on. + + { + "module": "collection" + "dependencies": { + "db": "my-db-module" + }, + "options": { + "collection-name": "my-collection" + } + } + +Where `my-db-module`'s config looks like this: + + { + "module": "couchdb-connector", + "options": { + "database": "my-db" + } + } + +#### config.env.json + +Separate file that overrides `config.json` depending on the current `NODE_ENV`. Useful for including config information that is environment specific or should not be committed to source control. + + { + // overrides for all envs + "*": { + "options": { + "upload-dir": "/uploads" + } + }, + "dev": { + "options": { + "upload-dir": "/dev-uploads" + } + } + } + +## Requiring Modules + +To use module instances, you can `require()` them anywhere in your program like you normally would require a node module. For example, you can get a reference to the `fido` object like this: + + var fido = require('fido'); // `fido` is the directory name containing the fido module config + +### Require Behavior + +After your program runs `require('sl-module-loader')` the `require()` function's behavior will change slightly to make referencing module instances simpler. Since some module instances may not have any program specific code, they can't be `require()`d with `node`'s existing require() implementation. + +## Config Loader + +`sl-module-loader` inherits from [sl-config-loader](https://github.com/strongloop/sl-config-loader). + + +## Bundled Modules / Aliasing + +Some modules need to be distributed together. For example, you have a set of related modules that all live under a single version number since they depend on features from each other. In this case you should bundle your sub modules using the package.json `bundledDependencies` array. + +Reference bundled modules by relative location (just like require). + + // config.json + { + "module": "myBundle/node_modules/foo" + } + +You may also provide aliases to any module path when creating a `ModuleLoader`. + + var moduleLoader = require('sl-module-loader').create('my-app', {alias: {'foo': 'myBundle/node_modules/foo'}}); + +Now the config can reference `foo` instead of the qualified path. + + // config.json + { + "module": "foo" + } \ No newline at end of file diff --git a/node_modules/sl-module-loader/example/express-app/app.js b/node_modules/sl-module-loader/example/express-app/app.js new file mode 100644 index 00000000..13317b75 --- /dev/null +++ b/node_modules/sl-module-loader/example/express-app/app.js @@ -0,0 +1,41 @@ +/** + * Main application + */ + +var express = require('express') + , http = require('http') + , path = require('path'); + +var app = express(); + +var ModuleLoader = require('../../'); + +var ml = ModuleLoader.create('.', {ttl: 0, ignore: ['node_modules']}); + +var options = {}; + +app.set('port', process.env.PORT || 3000); + +app.use(function (req, res, next) { + ml.load(function (err, config) { + if(err) { + throw err; + } else { + // simple routing + var module = ml.getByName(req.url.replace('/', '')); + + if(module) { + module.handle(req, res, next); + } else { + next(); + } + } + }); +}); + +app.use(app.router); + +http.createServer(app).listen(app.get('port'), function(){ + console.log("express-app listening on port " + app.get('port')); +}); + diff --git a/node_modules/sl-module-loader/example/express-app/models/user/config.env.json b/node_modules/sl-module-loader/example/express-app/models/user/config.env.json new file mode 100644 index 00000000..97035430 --- /dev/null +++ b/node_modules/sl-module-loader/example/express-app/models/user/config.env.json @@ -0,0 +1,7 @@ +{ + "prod": { + "dependencies": { + "store": "mongo" + } + } +} \ No newline at end of file diff --git a/node_modules/sl-module-loader/example/express-app/models/user/config.json b/node_modules/sl-module-loader/example/express-app/models/user/config.json new file mode 100644 index 00000000..76a722db --- /dev/null +++ b/node_modules/sl-module-loader/example/express-app/models/user/config.json @@ -0,0 +1,9 @@ +{ + "module": "data-model", + "options": { + "collection": "users" + }, + "dependencies": { + "store": "memory" + } +} \ No newline at end of file diff --git a/node_modules/sl-module-loader/example/express-app/package.json b/node_modules/sl-module-loader/example/express-app/package.json new file mode 100644 index 00000000..bba6b695 --- /dev/null +++ b/node_modules/sl-module-loader/example/express-app/package.json @@ -0,0 +1,14 @@ +{ + "name": "express-app", + "description": "express-app", + "private": true, + "version": "1.0.0", + "scripts": { + "start": "node app" + }, + "dependencies": { + "express": "latest", + "ejs": "latest", + "mongodb": "~1.2.14" + } +} diff --git a/node_modules/sl-module-loader/example/express-app/routes/bar/config.json b/node_modules/sl-module-loader/example/express-app/routes/bar/config.json new file mode 100644 index 00000000..3c4b085c --- /dev/null +++ b/node_modules/sl-module-loader/example/express-app/routes/bar/config.json @@ -0,0 +1,6 @@ +{ + "module": "responder", + "options": { + "msg": "this is bar" + } +} \ No newline at end of file diff --git a/node_modules/sl-module-loader/example/express-app/routes/foo/config.env.json b/node_modules/sl-module-loader/example/express-app/routes/foo/config.env.json new file mode 100644 index 00000000..07781f60 --- /dev/null +++ b/node_modules/sl-module-loader/example/express-app/routes/foo/config.env.json @@ -0,0 +1,12 @@ +{ + "*": { + "options": { + "msg": "foo bar" + } + }, + "dev": { + "options": { + "msg": "foo bar dev" + } + } +} \ No newline at end of file diff --git a/node_modules/sl-module-loader/example/express-app/routes/foo/config.json b/node_modules/sl-module-loader/example/express-app/routes/foo/config.json new file mode 100644 index 00000000..3ebfbf3d --- /dev/null +++ b/node_modules/sl-module-loader/example/express-app/routes/foo/config.json @@ -0,0 +1,6 @@ +{ + "module": "responder", + "options": { + "msg": "this is foo" + } +} \ No newline at end of file diff --git a/node_modules/sl-module-loader/example/express-app/routes/hello/config.json b/node_modules/sl-module-loader/example/express-app/routes/hello/config.json new file mode 100644 index 00000000..e9df27ef --- /dev/null +++ b/node_modules/sl-module-loader/example/express-app/routes/hello/config.json @@ -0,0 +1,6 @@ +{ + "module": "responder", + "options": { + "msg": "hello" + } +} diff --git a/node_modules/sl-module-loader/example/express-app/routes/users/config.json b/node_modules/sl-module-loader/example/express-app/routes/users/config.json new file mode 100644 index 00000000..a5991442 --- /dev/null +++ b/node_modules/sl-module-loader/example/express-app/routes/users/config.json @@ -0,0 +1,6 @@ +{ + "module": "./users-route.js", + "dependencies": { + "user": "user" + } +} \ No newline at end of file diff --git a/node_modules/sl-module-loader/example/express-app/routes/users/users-route.js b/node_modules/sl-module-loader/example/express-app/routes/users/users-route.js new file mode 100644 index 00000000..bd743121 --- /dev/null +++ b/node_modules/sl-module-loader/example/express-app/routes/users/users-route.js @@ -0,0 +1,27 @@ +var MODULE_LOADER = '../../../../'; +var Module = require(MODULE_LOADER).Module; +var inherits = require('util').inherits; + +module.exports = UsersRoute; + +function UsersRoute(options) { + Module.apply(this, arguments); +} + +inherits(UsersRoute, Module); + +UsersRoute.prototype.handle = function (req, res, next) { + var user = this.dependencies.user; + + user.getAll(function (err, users) { + if(err) { + next(err); + } else { + res.send(users); + } + }); +} + +UsersRoute.dependencies = { + 'user': 'data-model' +} \ No newline at end of file diff --git a/node_modules/sl-module-loader/example/express-app/stores/memory/config.json b/node_modules/sl-module-loader/example/express-app/stores/memory/config.json new file mode 100644 index 00000000..9b2ec528 --- /dev/null +++ b/node_modules/sl-module-loader/example/express-app/stores/memory/config.json @@ -0,0 +1,6 @@ +{ + "module": "memory-store", + "options": { + "database": "my-memory-db" + } +} \ No newline at end of file diff --git a/node_modules/sl-module-loader/example/express-app/stores/mongo/config.json b/node_modules/sl-module-loader/example/express-app/stores/mongo/config.json new file mode 100644 index 00000000..1e5cdae7 --- /dev/null +++ b/node_modules/sl-module-loader/example/express-app/stores/mongo/config.json @@ -0,0 +1,8 @@ +{ + "module": "mongo-store", + "options": { + "database": "sl-module-loader-testing-db", + "host": "127.0.0.1", + "port": "27017" + } +} \ No newline at end of file diff --git a/node_modules/sl-module-loader/example/module-script/another-module/another-module-class.js b/node_modules/sl-module-loader/example/module-script/another-module/another-module-class.js new file mode 100644 index 00000000..c8054def --- /dev/null +++ b/node_modules/sl-module-loader/example/module-script/another-module/another-module-class.js @@ -0,0 +1,9 @@ +module.exports = AnotherModuleClass; + +function AnotherModuleClass(options) { + this.options = options; +} + +AnotherModuleClass.prototype.foo = function () { + console.log('foo', this.options.msg); +} \ No newline at end of file diff --git a/node_modules/sl-module-loader/example/module-script/another-module/config.json b/node_modules/sl-module-loader/example/module-script/another-module/config.json new file mode 100644 index 00000000..5d3dcc4f --- /dev/null +++ b/node_modules/sl-module-loader/example/module-script/another-module/config.json @@ -0,0 +1,7 @@ +{ + "module": "./another-module-class.js", + "options": { + "msg": "bar bat baz", + "name": "another-module" + } +} \ No newline at end of file diff --git a/node_modules/sl-module-loader/example/module-script/example.js b/node_modules/sl-module-loader/example/module-script/example.js new file mode 100644 index 00000000..5ae894ac --- /dev/null +++ b/node_modules/sl-module-loader/example/module-script/example.js @@ -0,0 +1,5 @@ +var ml = require('../../').create('.'); + +ml.load(function () { + console.log('module loaded...'); +}); \ No newline at end of file diff --git a/node_modules/sl-module-loader/example/module-script/my-module/config.json b/node_modules/sl-module-loader/example/module-script/my-module/config.json new file mode 100644 index 00000000..514246ca --- /dev/null +++ b/node_modules/sl-module-loader/example/module-script/my-module/config.json @@ -0,0 +1,8 @@ +{ + "module": "./my-module-class.js", + "main": "main.js", + "options": { + "msg": "hello world", + "name": "my-module" + } +} \ No newline at end of file diff --git a/node_modules/sl-module-loader/example/module-script/my-module/main.js b/node_modules/sl-module-loader/example/module-script/my-module/main.js new file mode 100644 index 00000000..110d7ecc --- /dev/null +++ b/node_modules/sl-module-loader/example/module-script/my-module/main.js @@ -0,0 +1,7 @@ +var myModule = require('./'); + +myModule.hello(); // hello world (via config.json's options.msg) + +var anotherModule = require('../another-module'); + +anotherModule.foo(); \ No newline at end of file diff --git a/node_modules/sl-module-loader/example/module-script/my-module/my-module-class.js b/node_modules/sl-module-loader/example/module-script/my-module/my-module-class.js new file mode 100644 index 00000000..d685f425 --- /dev/null +++ b/node_modules/sl-module-loader/example/module-script/my-module/my-module-class.js @@ -0,0 +1,9 @@ +module.exports = MyModuleClass; + +function MyModuleClass(options) { + this.options = options; +} + +MyModuleClass.prototype.hello = function () { + console.log(this.options.msg); +} diff --git a/node_modules/sl-module-loader/example/sample-app/custom-scripts/config.json b/node_modules/sl-module-loader/example/sample-app/custom-scripts/config.json new file mode 100644 index 00000000..760392b1 --- /dev/null +++ b/node_modules/sl-module-loader/example/sample-app/custom-scripts/config.json @@ -0,0 +1,6 @@ +{ + "module": "module", + "scripts": { + "constructed": "construction.js" + } +} \ No newline at end of file diff --git a/node_modules/sl-module-loader/example/sample-app/custom-scripts/construction.js b/node_modules/sl-module-loader/example/sample-app/custom-scripts/construction.js new file mode 100644 index 00000000..6a7d9c25 --- /dev/null +++ b/node_modules/sl-module-loader/example/sample-app/custom-scripts/construction.js @@ -0,0 +1,9 @@ +/** + * not yet supported... + */ + +var modules = require('modules'); +var currentModule = modules(__dirname); + +console.log('this script is executed after a module is constructed'); +console.log('currentModule', currentModule); \ No newline at end of file diff --git a/node_modules/sl-module-loader/example/sample-app/default/config.json b/node_modules/sl-module-loader/example/sample-app/default/config.json new file mode 100644 index 00000000..69c2d596 --- /dev/null +++ b/node_modules/sl-module-loader/example/sample-app/default/config.json @@ -0,0 +1,6 @@ +{ + "module": "../my-module-type", + "options": { + "msg": "this module uses the default my-module-type" + } +} \ No newline at end of file diff --git a/node_modules/sl-module-loader/example/sample-app/example.js b/node_modules/sl-module-loader/example/sample-app/example.js new file mode 100644 index 00000000..68eba042 --- /dev/null +++ b/node_modules/sl-module-loader/example/sample-app/example.js @@ -0,0 +1,12 @@ +var ModuleLoader = require('../../'); +var moduleLoader = ModuleLoader.create('.'); + +moduleLoader.load(function (err, modules) { + if(err) throw err; + + console.log('loaded modules...'); + + moduleLoader.instanceOf('MyModuleType').forEach(function (m) { + m.speak(); + }); +}); \ No newline at end of file diff --git a/node_modules/sl-module-loader/example/sample-app/hello/config.json b/node_modules/sl-module-loader/example/sample-app/hello/config.json new file mode 100644 index 00000000..45ab8585 --- /dev/null +++ b/node_modules/sl-module-loader/example/sample-app/hello/config.json @@ -0,0 +1,6 @@ +{ + "module": "./hello.js", + "options": { + "msg": "world" + } +} \ No newline at end of file diff --git a/node_modules/sl-module-loader/example/sample-app/hello/hello.js b/node_modules/sl-module-loader/example/sample-app/hello/hello.js new file mode 100644 index 00000000..d4389658 --- /dev/null +++ b/node_modules/sl-module-loader/example/sample-app/hello/hello.js @@ -0,0 +1,14 @@ +var MyModuleType = require('../my-module-type'); +var util = require('util'); + +module.exports = HelloModule; + +function HelloModule(options) { + MyModuleType.call(this, options); +} + +util.inherits(HelloModule, MyModuleType); + +HelloModule.prototype.speak = function () { + console.log('from hello module', this.options); +} \ No newline at end of file diff --git a/node_modules/sl-module-loader/example/sample-app/inter-deps/config.json b/node_modules/sl-module-loader/example/sample-app/inter-deps/config.json new file mode 100644 index 00000000..599c870b --- /dev/null +++ b/node_modules/sl-module-loader/example/sample-app/inter-deps/config.json @@ -0,0 +1,6 @@ +{ + "module": "./my-module", + "dependencies": { + "custom-dep": "hello" + } +} \ No newline at end of file diff --git a/node_modules/sl-module-loader/example/sample-app/inter-deps/my-module.js b/node_modules/sl-module-loader/example/sample-app/inter-deps/my-module.js new file mode 100644 index 00000000..5db34176 --- /dev/null +++ b/node_modules/sl-module-loader/example/sample-app/inter-deps/my-module.js @@ -0,0 +1,18 @@ +var Module = require('../../../').Module; +var inherits = require('util').inherits; + + +module.exports = MyModule; + +function MyModule(options) { + Module.apply(this, arguments); + + console.log('the "inter-deps" module loads a dependency', this.dependencies['custom-dep']); +} + +MyModule.dependencies = { + // the dep name and path to a module that exports its supported type + 'custom-dep': '../my-module-type' +}; + +inherits(MyModule, Module); \ No newline at end of file diff --git a/node_modules/sl-module-loader/example/sample-app/my-module-type.js b/node_modules/sl-module-loader/example/sample-app/my-module-type.js new file mode 100644 index 00000000..18e4dbb7 --- /dev/null +++ b/node_modules/sl-module-loader/example/sample-app/my-module-type.js @@ -0,0 +1,9 @@ +module.exports = MyModuleType; + +function MyModuleType(options) { + this.options = options; +} + +MyModuleType.prototype.speak = function () { + console.log(this.options.msg || 'no message provided...'); +} \ No newline at end of file diff --git a/node_modules/sl-module-loader/index.js b/node_modules/sl-module-loader/index.js new file mode 100644 index 00000000..586b642b --- /dev/null +++ b/node_modules/sl-module-loader/index.js @@ -0,0 +1,6 @@ +/** + * sl-module-loader ~ public api + */ + +module.exports = require('./lib/module-loader'); +module.exports.Module = require('./lib/module'); \ No newline at end of file diff --git a/node_modules/sl-module-loader/lib/module-loader.js b/node_modules/sl-module-loader/lib/module-loader.js new file mode 100644 index 00000000..7d8666f1 --- /dev/null +++ b/node_modules/sl-module-loader/lib/module-loader.js @@ -0,0 +1,283 @@ +/** + * Expose `ModuleLoader`. + */ + +module.exports = ModuleLoader; + +/** + * Module dependencies. + */ + +var ConfigLoader = require('sl-config-loader') + , PatchedModule = require('./patched-module') + , fs = require('fs') + , path = require('path') + , debug = require('debug')('sl-module-loader') + , util = require('util') + , inherits = util.inherits + , assert = require('assert'); + +/** + * Create a new `ModuleLoader` with the given `options`. + * + * @param {Object} options + * @return {ModuleLoader} + */ + +function ModuleLoader(options) { + ConfigLoader.apply(this, arguments); + + this.types = {}; + this._modules = {}; + + // throw an error if args are not supplied + // assert(typeof options === 'object', 'ModuleLoader requires an options object'); + + this.options = options; + + debug('created with options', options); +} + +/** + * Inherit from `ConfigLoader`. + */ + +inherits(ModuleLoader, ConfigLoader); + +/** + * Simplified APIs + */ + +ModuleLoader.create = +ModuleLoader.createModuleLoader = function (root, options) { + options = options || {}; + options.root = root; + return new ModuleLoader(options); +} + +/** + * Load the configuration and build modules. + */ + +ModuleLoader.prototype.load = function (fn) { + if(this.remaining()) { + // wait until the current operation is finished + this.once('done', function () { + fn(null, this._files); + }); + // callback with an error if it occurs + this.once('error', fn); + } else if(this.ttl() > 0) { + fn(null, this._modules); + } else { + this.reset(); + + this.once('error', fn); + + this.bindListeners(); + + // load config files + this.task(fs, 'readdir', this.root); + this.once('done', function () { + this._mergeEnvConfigs(); + // create instances from config + this._constructModules(this._files); + fn(null, this._modules); + }); + } +} + +/** + * Load an sl module type from the given module name. + */ + +ModuleLoader.prototype.loadType = function (name) { + // get alias if it exists + var alias = (this.options.alias && this.options.alias[name]) || name; + + assert(alias, 'you must provide a name or alias when loading a type'); + + // use the root as the base dir for loading + var paths = PatchedModule.resolveLookupPaths(this.root)[1]; + + // add in node module paths + paths = PatchedModule.nodeModulePaths(this.root).concat(paths); + + var Type = PatchedModule.load(PatchedModule.resolveFilename(alias, {paths: paths})); + + // patch the module with a moduleName for reference + Type.moduleName = name; + + return Type; +} + +/** + * Reset the loaded modules and cache. + */ + +ModuleLoader.prototype.reset = function () { + // clear require cache + clearObject(require.cache); + + // clear module cache + clearObject(this._modules, function (module) { + if(typeof module.destroy === 'function') { + module.destroy(); + } + }); + + // clear type cache + clearObject(this.types); + + ConfigLoader.prototype.reset.apply(this, arguments); +} + +/** + * Build modules from a list of config files. + */ + +ModuleLoader.prototype._constructModules = function (configs) { + Object.keys(configs).forEach(function (p) { + this.constructModuleFromConfigAtPath(configs[p], p); + }.bind(this)); +} + +ModuleLoader.prototype.constructModuleFromConfigAtPath = function (config, p) { + config.options = config.options || {}; + config.options._moduleLoader = this; + config.options._name = path.basename(path.dirname(p)); + + var Type = this.getTypeFromConfig(config, p); + + assert(typeof Type === 'function', config.module + ' does not export a constructor'); + + // private options + config.options._path = p; + + // cache discovered types + var T = Type; + + while(T) { + this.types[T.name] = T; + + T = T.super_; + } + + var m = this._modules[p] = new Type(config.options, config.dependencies); + + // create NodeModule + var nmFilename = path.resolve(path.dirname(p)); + var nm = new PatchedModule(nmFilename, module); + nm.exports = m; + nm.filename = nmFilename; + nm.loaded = true; + require('module')._cache[nmFilename] = nm; + + // load main + if(config.main) { + var mnmFilename = path.resolve(path.dirname(p), config.main); + var mnm = new PatchedModule(mnmFilename, nm); + mnm.load(mnmFilename); + } + + return m; +} + +/** + * Return a module with the provided name. + */ + +ModuleLoader.prototype.getByName = function (name) { + var paths = Object.keys(this._modules); + + for (var i = 0; i < paths.length; i++) { + var n = path.basename(path.dirname(paths[i])); + + if(n === name) { + return this._modules[paths[i]]; + } + } +} + +/** + * Get a module config by module name. + */ + +ModuleLoader.prototype.getConfigAndPathByName = function (name) { + var paths = Object.keys(this._files); + + for (var i = 0; i < paths.length; i++) { + if(path.basename(path.dirname(paths[i])) === name) { + return {config: this._files[paths[i]], path: paths[i]}; + } + } +} + +ModuleLoader.prototype.getTypeFromConfig = function (config, configPath) { + var ml = this; + var Type; + var typeName = config.module; + + if(!typeName) return; + + // pointing to a relative type file + if(typeName[0] === '.') { + try { + var relPath = path.join(path.dirname(configPath), typeName); + + + debug('requiring relative module %s', relPath); + Type = require(relPath); + } catch(e) { + fail(e); + } + } else { + // pointing to a module + try { + // load from the programs main module + Type = require('module')._load(typeName, process.mainModule); + } catch(e) { + fail(e); + } + } + + if(Type) { + return Type; + } + + function fail(e) { + console.error('failed to load module at', configPath, typeName); + ml.emit('error', e); + } +} + +/** + * Return all module instances that inherit from the given type. + */ + +ModuleLoader.prototype.instanceOf = function (typeName) { + var Type = this.types[typeName]; + var results = []; + + if(!Type) { + throw new Error(typeName + ' is not a used type'); + } + + Object.keys(this._modules).forEach(function (k) { + if(this._modules[k] instanceof Type) { + results.push(this._modules[k]); + } + }.bind(this)); + + return results; +} + +function clearObject(obj, fn) { + Object.keys(obj).forEach(function (k) { + if(fn) { + fn(obj[k]); + } + + delete obj[k]; + }); +} \ No newline at end of file diff --git a/node_modules/sl-module-loader/lib/module.js b/node_modules/sl-module-loader/lib/module.js new file mode 100644 index 00000000..0ec90855 --- /dev/null +++ b/node_modules/sl-module-loader/lib/module.js @@ -0,0 +1,209 @@ +/** + * Expose `Module`. + */ + +module.exports = Module; + +/** + * Module dependencies. + */ + +var EventEmitter = require('events').EventEmitter + , debug = require('debug')('module') + , path = require('path') + , util = require('util') + , inherits = util.inherits + , assert = require('assert'); + +/** + * Create a new `Module` with the given `options`. + * + * @param {Object} options + * @return {Module} + */ + +function Module(options, dependencies) { + dependencies = dependencies || {}; + + EventEmitter.apply(this, arguments); + + this.options = options; + + if(this.constructor.dependencies) { + assert(typeof this.constructor.dependencies === 'object', this.constructor.name + + '.dependencies does not allow any dependencies!'); + + // merge dependencies for inheritence chain + var constructor = this.constructor; + var mergedDeps = {}; + + while(constructor) { + var deps = constructor.dependencies; + + if(deps) { + Object.keys(deps).forEach(function (key) { + if(!mergedDeps[key]) mergedDeps[key] = deps[key]; + }); + } + + // move up the inheritence chain + constructor = constructor.super_; + } + + this.dependencies = this._resolveDependencies(dependencies, mergedDeps); + } + + if(this.constructor.options) { + assert(typeof this.constructor.options === 'object', this.constructor.name + + '.options must be an object!'); + + // merge options for inheritence chain + var constructor = this.constructor; + var mergedOpts = {}; + + while(constructor) { + var opts = constructor.options; + + if(opts) { + Object.keys(opts).forEach(function (key) { + if(!mergedOpts[key]) mergedOpts[key] = opts[key]; + }); + } + + // move up the inheritence chain + constructor = constructor.super_; + } + + validateOptions(options, mergedOpts); + } +} + +/** + * Inherit from `EventEmitter`. + */ + +inherits(Module, EventEmitter); + +/** + * Build dependencies from constructor description (MyCustomModule.dependencies) and dependencie + * configuration (config.dependencies). + */ + +Module.prototype._resolveDependencies = function (depsConfig, desc) { + var types = {}; + var deps = {}; + var moduleLoader = this.options._moduleLoader; + + // iterate the class description of dependencies + Object.keys(desc).forEach(function (depName) { + var depRequired = true; + + if(typeof desc[depName] === 'object' && desc[depName].optional) { + depRequired = false; + } + + var depInstanceName = depsConfig[depName]; + + if(!depInstanceName) { + if(depRequired) { + throw new Error('Required dependency not defined: "' + depName + '"'); + } else { + // don't load the optional dep + return; + } + } + + // load the described type + try { + var modPath = desc[depName].module || desc[depName]; + + if(modPath[0] === '.') { + modPath = path.resolve('.', path.dirname(this.options._path), desc[depName]); + } + + types[depName] = require('module')._load(modPath, process.mainModule); + } catch(e) { + e.message = 'Failed to load dependency "' + depName + ': ' + modPath + '" for ' + this.options._path + '. ' + e.message; + throw e; + } + + var configAndPath = moduleLoader.getConfigAndPathByName(depInstanceName); + var config = configAndPath && configAndPath.config; + var configPath = configAndPath && configAndPath.path; + + // try to get the dependency by given dependency instance name + if(config) { + var m = moduleLoader.getByName(depInstanceName); + + if(!m) { + // construct the module now + m = moduleLoader.constructModuleFromConfigAtPath(config, configPath); + } + + if(!types[depName]) { + throw new Error(modPath + ' does not correctly export a constructor or does not exist.'); + } + + if(!(m instanceof types[depName])) { + throw new Error('Dependency ' + depName + ' is not an instance of ' + types[depName].name); + } + + deps[depName] = m; + } else { + console.log(depsConfig); + + throw new Error('Could not find dependency "'+ depInstanceName +'" config while resolving dependencies for ' + this.options._path); + } + }.bind(this)); + + return deps; +} + +Module.prototype.destroy = function () { + this.emit('destroy'); +} + +function validateOptions(options, def) { + if(!def) { + return options; + } + + Object.keys(def).forEach(function (key) { + var val = options[key]; + var keyDef = def[key] || {}; + + if(keyDef.required) { + assert(val, key + ' is required!'); + } + + if(typeof val === 'undefined') { + // stop validation if a value + // wasnt provided + return; + } + + if(keyDef.type === 'array') { + assert(Array.isArray(val), key + ' must be a ' + keyDef.type) + } else { + // type + assert(typeof val == keyDef.type, key + ' must be a ' + keyDef.type); + } + + // size / length + if(typeof val.length === 'number') { + if(keyDef.min) { + assert(val.length >= keyDef.min, key + ' length must be greater than or equal to ', keyDef.min); + } + if(keyDef.max) { + assert(val.length <= keyDef.min, key + ' length must be less than or equal to ', keyDef.max); + } + } else if(typeof val === 'number') { + if(keyDef.min) { + assert(val >= keyDef.min, key + ' must be greater than or equal to ', keyDef.min); + } + if(keyDef.max) { + assert(val <= keyDef.max, ' must be less than or equal to ', keyDef.max); + } + } + }); +} \ No newline at end of file diff --git a/node_modules/sl-module-loader/lib/patched-module.js b/node_modules/sl-module-loader/lib/patched-module.js new file mode 100644 index 00000000..be19f642 --- /dev/null +++ b/node_modules/sl-module-loader/lib/patched-module.js @@ -0,0 +1,66 @@ +/** + * Expose `PatchedModule`. + */ + +module.exports = PatchedModule; + +/** + * Module dependencies. + */ + +var Module = require('module') + , debug = require('debug')('configurable-module') + , path = require('path') + , util = require('util') + , inherits = util.inherits + , assert = require('assert'); + +/** + * Create a new `PatchedModule` with the given `options`. + * + * @param {Object} options + * @return {Module} + */ + +function PatchedModule(id, parent) { + Module.apply(this, arguments); +} + +/** + * Inherit from node's core `Module`. + */ + +inherits(PatchedModule, Module); + +/** + * Override the default require implementation to check the cache first. + */ + +PatchedModule.prototype.require = function (p) { + if(p === '.') p = './'; + + // for relative modules, check the cache first + var sub = p.substr(0, 2); + var isRelative = (sub === './' || sub === '..'); + + if(isRelative) { + var resolvedPath = path.resolve(path.dirname(this.filename), p); + + // check cache first (node's implementation checks the file first) + var cached = Module._cache[resolvedPath]; + if(cached) { + return cached.exports; + } + } + + return Module.prototype.require.apply(this, arguments); +} + +/** + * Alias functions in case they needs to be patched in the future. + */ + +PatchedModule.resolveFilename = Module._resolveFilename; +PatchedModule.load = Module._load; +PatchedModule.resolveLookupPaths = Module._resolveLookupPaths; +PatchedModule.nodeModulePaths = Module._nodeModulePaths; \ No newline at end of file diff --git a/node_modules/sl-module-loader/package.json b/node_modules/sl-module-loader/package.json new file mode 100644 index 00000000..a98a787d --- /dev/null +++ b/node_modules/sl-module-loader/package.json @@ -0,0 +1,23 @@ +{ + "name": "sl-module-loader", + "description": "sl-module-loader", + "version": "0.0.1", + "scripts": { + "test": "mocha" + }, + "dependencies": { + "debug": "latest", + "sl-config-loader": "git+ssh://git@github.com:strongloop/sl-config-loader.git" + }, + "devDependencies": { + "mocha": "latest" + }, + "readme": "# sl-module-loader\nv0.0.1\n\n## Purpose\n\nThe `sl-module-loader` allows your program to register classes (or types) that are instantiated via configuration files. Configuration files point to an implementation `module` constructor. The `module`'s job is to construct a useful instance with the given configuration options. This allows programs to be free from bootstrapping code and manageable via config.\n\n## Install\n\n slnode install sl-module-loader\n \n## Example\n\nGiven a simple `Dog` module:\n\n function Dog(options) {\n this.options = options;\n }\n \n Dog.prototype.speak = function() {\n console.log('roof', 'my name is', this.options.name);\n }\n \n module.exports = Dog;\n\nAnd a set of `config.json` files:\n\n /my-app\n /fido\n config.json\n /santas-little-helper\n config.json\n /rex\n config.json\n /node_modules\n /dog\n index.js\n package.json\n\nWhere a `config.json` looks like this:\n\n {\n \"module\": \"dog\", // the \"dog\" module\n \"options\": {\n \"name\": \"fido\"\n }\n }\n\nWe can load up all the dogs like so (app.js):\n\n var moduleLoader = require('sl-module-loader').create('my-app');\n\n moduleLoader.load(function (err, modules) {\n if(err) throw err;\n \n moduleLoader\n .instanceOf('dog') // a module in node_modules or declared as a dependency in package.json\n .forEach(function (m) {\n m.speak();\n });\n });\n \nThe above calls a method on all module instances that inherit from `Dog` and outputs:\n\n roof my name is fido\n roof my name is santa's little helper\n roof my name is rex\n\n## Creating Module Classes\n\nThe purpose of a module class is to take meaningful input (configuration, options, dependencies) and create a useful output: a module instance.\n\n### Module Classes\n\nA module class is a `node_module` that exports a constructor inheriting from the `Module` class.\n\n var inherits = require('util').inherits;\n var Module = require('sl-module-loader').Module;\n\n module.exports = Dog;\n\n function Dog(options) {\n this.options = options;\n }\n\n inherits(Dog, Module);\n\n Dog.prototype.speak = function() {\n console.log('roof', 'my name is', this.options.name);\n }\n\nModule classes may define dependency contracts that tell the module loader to provide dependencies of a given module class during construction.\n\n function MyComplexModule() {\n Module.apply(this, arguments);\n console.log('loaded dependencies', this.dependencies); // {'my-dependency': }\n }\n\n MyComplexModule.dependencies = {\n 'my-dependency': 'another-module-class'\n }\n\n#### Module Class Options\n\nModule classes may also describe the options they accept. This will validate the configuration of module instance and guarantee the module class constructor has enough information to construct an instance.\n\nHere is an example options description for a database connection module class.\n\n DatabaseConnection.options = {\n 'hostname': {type: 'string', required: true},\n 'port': {type: 'number', min: 10, max: 99999},\n 'username': {type: 'string'},\n 'password': {type: 'string'}\n };\n\n**key** the option name given in `config.json`.\n\n**type** must be one of:\n\n - string\n - boolean\n - number\n - array\n\n**min/max** depend on the option type\n\n {\n min: 10, // minimum length or value\n max: 100, // max length or value\n }\n\n#### Module Events\n\nModule classes may also emit and listen to events. By default a Module will emit the following events:\n\n**destroy**\n\nEmitted when a module instance is being destroyed during a `moduleLoader.reset()`. Modules should cleanup any connections and unbind all event listeners.\n\n### Configuration\n\nEach module instance is defined by creating a `config.json` file in a directory with the module's name.\n\n /my-module-instance\n config.json\n other-files.txt\n index.js\n \nThis directory should contain files related to the module instance. For example it might contain a script that `require()`s the module instance. \n\n#### config.module\n\nThe node module name that exports the module class that constructs the config's module instance.\n\n {\n \"module\": \"my-module-class\"\n }\n\n#### config.options\n\nDefines arbitrary options. A `file-upload` module might have an `uploads` option.\n\n {\n \"module\": \"file-upload\",\n \"options\": {\n \"uploads\": \"/tmp\"\n }\n }\n\n#### config.dependencies\n\nDefines other module instances the configured instance depends on.\n\n {\n \"module\": \"collection\"\n \"dependencies\": {\n \"db\": \"my-db-module\"\n },\n \"options\": {\n \"collection-name\": \"my-collection\"\n }\n }\n\nWhere `my-db-module`'s config looks like this:\n\n {\n \"module\": \"couchdb-connector\",\n \"options\": {\n \"database\": \"my-db\"\n }\n }\n \n#### config.env.json\n\nSeparate file that overrides `config.json` depending on the current `NODE_ENV`. Useful for including config information that is environment specific or should not be committed to source control.\n\n {\n // overrides for all envs\n \"*\": {\n \"options\": {\n \"upload-dir\": \"/uploads\"\n }\n },\n \"dev\": {\n \"options\": {\n \"upload-dir\": \"/dev-uploads\"\n }\n }\n }\n\n## Requiring Modules\n\nTo use module instances, you can `require()` them anywhere in your program like you normally would require a node module. For example, you can get a reference to the `fido` object like this:\n\n var fido = require('fido'); // `fido` is the directory name containing the fido module config\n\n### Require Behavior\n\nAfter your program runs `require('sl-module-loader')` the `require()` function's behavior will change slightly to make referencing module instances simpler. Since some module instances may not have any program specific code, they can't be `require()`d with `node`'s existing require() implementation.\n \n## Config Loader\n\n`sl-module-loader` inherits from [sl-config-loader](https://github.com/strongloop/sl-config-loader).\n\n\n## Bundled Modules / Aliasing\n\nSome modules need to be distributed together. For example, you have a set of related modules that all live under a single version number since they depend on features from each other. In this case you should bundle your sub modules using the package.json `bundledDependencies` array.\n\nReference bundled modules by relative location (just like require).\n\n // config.json\n {\n \"module\": \"myBundle/node_modules/foo\"\n }\n\nYou may also provide aliases to any module path when creating a `ModuleLoader`.\n\n var moduleLoader = require('sl-module-loader').create('my-app', {alias: {'foo': 'myBundle/node_modules/foo'}});\n\nNow the config can reference `foo` instead of the qualified path.\n\n // config.json\n {\n \"module\": \"foo\"\n }", + "readmeFilename": "README.md", + "_id": "sl-module-loader@0.0.1", + "dist": { + "shasum": "f51c63bcb10ea2a21b672346d0341b51d1ef8fb1" + }, + "_resolved": "git+ssh://git@github.com/:strongloop/sl-module-loader.git#0484e56ee411b6e97aab5b5b9548854784b0781c", + "_from": "sl-module-loader@git+ssh://git@github.com:strongloop/sl-module-loader.git" +} diff --git a/node_modules/sl-module-loader/test/module-loader.test.js b/node_modules/sl-module-loader/test/module-loader.test.js new file mode 100644 index 00000000..1e8d4d54 --- /dev/null +++ b/node_modules/sl-module-loader/test/module-loader.test.js @@ -0,0 +1,24 @@ +var ModuleLoader = require('../'); + +// describe('ModuleLoader', function(){ +// var moduleLoader; +// +// beforeEach(function(){ +// moduleLoader = new ModuleLoader; +// }); +// +// describe('.myMethod', function(){ +// // example sync test +// it('should ', function() { +// moduleLoader.myMethod(); +// }); +// +// // example async test +// it('should ', function(done) { +// setTimeout(function () { +// moduleLoader.myMethod(); +// done(); +// }, 0); +// }); +// }); +// }); \ No newline at end of file diff --git a/node_modules/sl-module-loader/test/module.test.js b/node_modules/sl-module-loader/test/module.test.js new file mode 100644 index 00000000..ae306f25 --- /dev/null +++ b/node_modules/sl-module-loader/test/module.test.js @@ -0,0 +1,48 @@ +var Module = require('../lib/module.js'); +var inherits = require('util').inherits; + +describe('Module', function(){ + var module; + + beforeEach(function(){ + module = new Module; + }); + + it('should merge all dependency objects', function() { + function FooModule() { + Module.apply(this, arguments); + } + + inherits(FooModule, Module); + + FooModule.dependencies = { + 'foo': 'foo', + 'baz': 'baz' + } + + function BarModule() { + FooModule.apply(this, arguments); + } + + inherits(BarModule, FooModule); + + BarModule.dependencies = { + 'foo': 'complex-foo', + 'bar': 'bar' + }; + + var called = false; + + BarModule.prototype._resolveDependencies = function (depsConfig, desc) { + assert(desc.foo == 'complex-foo'); + assert(desc.bar == 'bar'); + assert(desc.baz == 'baz'); + called = true; + } + + var bar = new BarModule({}, {}); + + assert(called); + }); + +}); \ No newline at end of file diff --git a/node_modules/sl-module-loader/test/support.js b/node_modules/sl-module-loader/test/support.js new file mode 100644 index 00000000..c15759f1 --- /dev/null +++ b/node_modules/sl-module-loader/test/support.js @@ -0,0 +1,5 @@ +/** + * sl-module-loader test setup and support. + */ + +assert = require('assert'); \ No newline at end of file diff --git a/test/asteroid.test.js b/test/asteroid.test.js index 99bcfe2a..9979110d 100644 --- a/test/asteroid.test.js +++ b/test/asteroid.test.js @@ -1,24 +1,2 @@ var Asteroid = require('../'); -describe('Asteroid', function(){ - var asteroid; - - beforeEach(function(){ - asteroid = new Asteroid; - }); - - describe('.myMethod', function(){ - // example sync test - it('should ', function() { - asteroid.myMethod(); - }); - - // example async test - it('should ', function(done) { - setTimeout(function () { - asteroid.myMethod(); - done(); - }, 0); - }); - }); -}); \ No newline at end of file