From 75ab597dd3c708020d510dafc5b4ee61e4bba5c7 Mon Sep 17 00:00:00 2001 From: Alasdair Mercer Date: Thu, 15 Dec 2016 14:06:07 +0000 Subject: [PATCH 01/12] simplified mounter and registrar configuration --- README.md | 56 +++----- .../{express.js => express-mounter.js} | 6 +- src/mounter/{mounter.js => index.js} | 39 ++++- .../{restify.js => restify-mounter.js} | 6 +- .../index-registrar.js} | 54 ++++--- src/registrar/index.js | 136 ++++++++++++++---- src/registrar/registrar.js | 115 --------------- src/registrar/{verb.js => verb-registrar.js} | 6 +- src/registrars.js | 48 ------- src/routerify.js | 38 ++--- 10 files changed, 220 insertions(+), 284 deletions(-) rename src/mounter/{express.js => express-mounter.js} (94%) rename src/mounter/{mounter.js => index.js} (71%) rename src/mounter/{restify.js => restify-mounter.js} (94%) rename src/{mounters.js => registrar/index-registrar.js} (53%) delete mode 100644 src/registrar/registrar.js rename src/registrar/{verb.js => verb-registrar.js} (94%) delete mode 100644 src/registrars.js diff --git a/README.md b/README.md index 37181cf..98d4777 100644 --- a/README.md +++ b/README.md @@ -142,11 +142,11 @@ In order to create your own registrar you simply need to extend the `Registrar` ``` javascript const routerify = require('routerify') -const Registrar = require('routerify/src/registrar/registrar') +const Registrar = require('routerify/src/registrar') class CustomRegistrar extends Registrar { - static name() { + static getName() { return 'custom' } @@ -156,9 +156,7 @@ class CustomRegistrar extends Registrar { } } -routerify.registrars[CustomRegistrar.name()] = CustomRegistrar - -module.exports = CustomRegistrar +module.exports = Registrar.define(CustomRegistrar) ``` Now your new registrar can be used by simply specifying its name in the options: @@ -212,11 +210,11 @@ In order to create your own mounter you simply need to extend the `Mounter` clas ``` javascript const routerify = require('routerify') -const Mounter = require('routerify/src/mounter/mounter') +const Mounter = require('routerify/src/mounter') class CustomMounter extends Mounter { - static name() { + static getName() { return 'custom' } @@ -236,9 +234,7 @@ class CustomMounter extends Mounter { } } -routerify.mounters[CustomMounter.name()] = CustomMounter - -module.exports = CustomMounter +module.exports = Mounter.define(CustomMounter) ``` Now your new mounter can be used by simply specifying its name in the options: @@ -283,39 +279,19 @@ app.listen(3000, () => { The following options can be passed to Routerify: -| Option | Description | Default Value | -| -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------- | -| `dir` | The directory containing the routes to be loaded. | `process.cwd()` | -| `ext` | The extension of the source files to be loaded. | `".js"` | -| `glob` | Any options to be passed to the `glob` module when searching for source files within `dir`. | `{}` | -| `mounter` | The name (or constructor) of the `Mounter` to be used to mount the discovered routes on to the `server`. | `"express"` | -| `paramPattern` | The regular expression to be used to match path parameter variables. | `/^_(.+)/` | -| `registrar` | The name (or constructor) of the `Registrar` used to load routes from source files in a given structure and then mount them via the `mounter`. | `"verb"` | -| `server` | The server object (e.g. `express()`) to which the routes are to be mounted. | N/A | -| `verbs` | The verbs (corresponding to HTTP methods) to be supported. Defaults to those provided by the `mounter` if not specified. | `mounter.getDefaultValues()` | +| Option | Description | Default Value | +| -------------- | ----------------------------------------------------------------------------------------------------------------------------- | ---------------------------- | +| `dir` | The directory containing the routes to be loaded. | `process.cwd()` | +| `ext` | The extension of the source files to be loaded. | `".js"` | +| `glob` | Any options to be passed to the `glob` module when searching for source files within `dir`. | `{}` | +| `mounter` | The name of the `Mounter` to be used to mount the discovered routes on to the `server`. | `"express"` | +| `paramPattern` | The regular expression to be used to match path parameter variables. | `/^_(.+)/` | +| `registrar` | The name of the `Registrar` used to load routes from source files in a given structure and then mount them via the `mounter`. | `"verb"` | +| `server` | The server object (e.g. `express()`) to which the routes are to be mounted. | N/A | +| `verbs` | The verbs (corresponding to HTTP methods) to be supported. Defaults to those provided by the `mounter` if not specified. | `mounter.getDefaultValues()` | Only the `server` option is required. All others have defaults. -### `routerify.mounters` - -A mapping of available [mounters](#mounters) where the key is the mounter name and the value is its constructor. This is -primarily intended for those looking to create their own mounter and hook it into Routerify. - -``` javascript -routerify.mounters -=> { express: ExpressMounter, restify: RestifyMounter } -``` - -### `routerify.registrars` - -A mapping of available [registrars](#registrars) where the key is the registrar name and the value is its constructor. -This is primarily intended for those looking to create their own registrar and hook it into Routerify. - -``` javascript -routerify.registrars -=> { index: IndexRegistrar, verb: VerbRegistrar } -``` - ### `routerify.version` The current version of Routerify. diff --git a/src/mounter/express.js b/src/mounter/express-mounter.js similarity index 94% rename from src/mounter/express.js rename to src/mounter/express-mounter.js index ba235f2..9f4d058 100644 --- a/src/mounter/express.js +++ b/src/mounter/express-mounter.js @@ -22,7 +22,7 @@ 'use strict' -const Mounter = require('./mounter') +const Mounter = require('./') /** * An implementation of {@link Mounter} that is intended to be used for Express applications. @@ -36,7 +36,7 @@ class ExpressMounter extends Mounter { * @override * @inheritDoc */ - static name() { + static getName() { return 'express' } @@ -68,4 +68,4 @@ class ExpressMounter extends Mounter { } -module.exports = ExpressMounter +module.exports = Mounter.define(ExpressMounter) diff --git a/src/mounter/mounter.js b/src/mounter/index.js similarity index 71% rename from src/mounter/mounter.js rename to src/mounter/index.js index 0920ba3..30a3788 100644 --- a/src/mounter/mounter.js +++ b/src/mounter/index.js @@ -22,6 +22,14 @@ 'use strict' +/** + * The available {@link Mounter} implementation constructors mapped against their name. + * + * @private + * @type {Map.} + */ +const types = new Map() + /** * Responsible for mounting a route, whose information is provided by a {@link Registrar}, onto the server. * @@ -29,6 +37,20 @@ */ class Mounter { + /** + * Defines the specified type of {@link Mounter} so that it can be looked up using its name. + * + * @param {Function} type - the constructor of the {@link Mounter} implementation to be defined + * @return {Function} A reference to type. + * @public + * @static + */ + static define(type) { + types.set(type.getName(), type) + + return type + } + /** * Returns the name of the {@link Mounter} which can be used to lookup constructors. * @@ -37,7 +59,19 @@ class Mounter { * @static * @abstract */ - static name() { + static getName() { + } + + /** + * Looks up the type of {@link Mounter} associated with the specified name. + * + * @param {string} name - the name associated with the {@link Mounter} implementation to be looked up + * @return {Function} The constructor of the {@link Mounter} implementation associated with name. + * @public + * @static + */ + static lookup(name) { + return types.get(name) } /** @@ -78,3 +112,6 @@ class Mounter { } module.exports = Mounter + +require('./express-mounter') +require('./restify-mounter') diff --git a/src/mounter/restify.js b/src/mounter/restify-mounter.js similarity index 94% rename from src/mounter/restify.js rename to src/mounter/restify-mounter.js index 9dab08a..8dcc839 100644 --- a/src/mounter/restify.js +++ b/src/mounter/restify-mounter.js @@ -22,7 +22,7 @@ 'use strict' -const ExpressMounter = require('./express') +const ExpressMounter = require('./express-mounter') /** * An extension of {@link ExpressMounter} which provides further compatibility with Restify by reading @@ -41,7 +41,7 @@ class RestifyMounter extends ExpressMounter { * @override * @inheritDoc */ - static name() { + static getName() { return 'restify' } @@ -65,4 +65,4 @@ class RestifyMounter extends ExpressMounter { } -module.exports = RestifyMounter +module.exports = ExpressMounter.define(RestifyMounter) diff --git a/src/mounters.js b/src/registrar/index-registrar.js similarity index 53% rename from src/mounters.js rename to src/registrar/index-registrar.js index 253155d..de215c7 100644 --- a/src/mounters.js +++ b/src/registrar/index-registrar.js @@ -22,27 +22,49 @@ 'use strict' -const ExpressMounter = require('./mounter/express') -const RestifyMounter = require('./mounter/restify') +const path = require('path') -/** - * A list of available {@link Mounter} constructors. - * - * @private - * @type {Function[]} - */ -const constructors = [ ExpressMounter, RestifyMounter ] +const Registrar = require('./') /** - * A map of {@link Mounter} names to their constructors. + * An implementation of {@link Registrar} where the index files in each directory is loaded as a module and the result + * should be an object and containing properties whose names are mapped to supported verbs and whose values are one or + * more handlers which are then mounted against that verb. * * @public - * @type {Object.} + * @extends Registrar */ -const mounters = {} +class IndexRegistrar extends Registrar { + + /** + * @inheritDoc + * @override + */ + static getName() { + return 'index' + } + + /** + * @inheritDoc + * @override + */ + register(file, options) { + const name = path.basename(file, options.ext) + if (name !== 'index') { + return + } + + const router = this.loadRouter(file, options) + const url = this.buildUrl(file, options) + + options.verbs.forEach((verb) => { + const handlers = router[verb] + if (handlers) { + this.mounter.mount(url, verb, handlers, options) + } + }) + } -constructors.forEach((Constructor) => { - mounters[Constructor.name()] = Constructor -}) +} -module.exports = mounters +module.exports = Registrar.define(IndexRegistrar) diff --git a/src/registrar/index.js b/src/registrar/index.js index 0f6bf4e..7ff3cec 100644 --- a/src/registrar/index.js +++ b/src/registrar/index.js @@ -24,47 +24,129 @@ const path = require('path') -const Registrar = require('./registrar') +/** + * The available {@link Registrar} implementation constructors mapped against their name. + * + * @private + * @type {Map.} + */ +const types = new Map() /** - * An implementation of {@link Registrar} where the index files in each directory is loaded as a module and the result - * should be an object and containing properties whose names are mapped to supported verbs and whose values are one or - * more handlers which are then mounted against that verb. + * Responsible for loading routes from a specific structure of modules and mounting them onto the server using a given + * {@link Mounter}. * * @public - * @extends Registrar */ -class IndexRegistrar extends Registrar { +class Registrar { + + /** + * Defines the specified type of {@link Registrar} so that it can be looked up using its name. + * + * @param {Function} type - the constructor of the {@link Registrar} implementation to be defined + * @return {Function} A reference to type. + * @public + * @static + */ + static define(type) { + types.set(type.getName(), type) + + return type + } + + /** + * Returns the name of the {@link Registrar} which can be used to lookup constructors. + * + * @return {string} The name. + * @public + * @static + * @abstract + */ + static getName() { + } + + /** + * Looks up the type of {@link Registrar} associated with the specified name. + * + * @param {string} name - the name associated with the {@link Registrar} implementation to be looked up + * @return {Function} The constructor of the {@link Registrar} implementation associated with name. + * @public + * @static + */ + static lookup(name) { + return types.get(name) + } /** - * @inheritDoc - * @override + * Creates an instance of {@link Registrar} with the specified mounter. + * + * @param {Mounter} mounter - the {@link Mounter} to be used + * @public */ - static name() { - return 'index' + constructor(mounter) { + /** + * The {@link Mounter} to be used by this {@link Registrar} to mount discovered routes onto the server. + * + * @protected + * @type {Mounter} + */ + this.mounter = mounter } /** - * @inheritDoc - * @override + * Builds the route URL from the file path provided. + * + * This includes detecting files/directories that represent parameter path variables and inserting the names into the + * route URL correctly based on the {@link Mounter}. + * + * By default, this method simply concatenates the file path segments into the URL while dropping the + * last path segment. + * + * @param {string} file - the file path of the module from which the routes are being loaded + * @param {routerify~options} options - the options to be used + * @return {string} The route URL built from the file path. + * @protected + */ + buildUrl(file, options) { + return file.split(path.sep) + .slice(0, -1) + .reduce((memo, segment) => { + const match = segment.match(options.paramPattern) + if (match) { + segment = this.mounter.formatParamPath(match[1]) + } + + return `${memo}/${segment}` + }, '') + } + + /** + * Loads the router(s) from the module at the specified file path. + * + * @param {string} file - the file path of the module from which the routes are being loaded + * @param {routerify~options} options - the options to be used + * @return {*} The router loaded from the module at the file path. + * @protected + */ + loadRouter(file, options) { + return require(path.resolve(options.dir, file)) + } + + /** + * Loads routes from the module at the specified file path and then mounts them onto the server. + * + * @param {string} file - the file path of the module from which the routes are to be loaded + * @param {routerify~options} options - the options to be used + * @return {void} + * @public + * @abstract */ register(file, options) { - const name = path.basename(file, options.ext) - if (name !== 'index') { - return - } - - const router = this.loadRouter(file, options) - const url = this.buildUrl(file, options) - - options.verbs.forEach((verb) => { - const handlers = router[verb] - if (handlers) { - this.mounter.mount(url, verb, handlers, options) - } - }) } } -module.exports = IndexRegistrar +module.exports = Registrar + +require('./index-registrar') +require('./verb-registrar') diff --git a/src/registrar/registrar.js b/src/registrar/registrar.js deleted file mode 100644 index a7795c0..0000000 --- a/src/registrar/registrar.js +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright (C) 2016 Alasdair Mercer, Skelp - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -'use strict' - -const path = require('path') - -/** - * Responsible for loading routes from a specific structure of modules and mounting them onto the server using a given - * {@link Mounter}. - * - * @public - */ -class Registrar { - - /** - * Returns the name of the {@link Registrar} which can be used to lookup constructors. - * - * @return {string} The name. - * @public - * @static - * @abstract - */ - static name() { - } - - /** - * Creates an instance of {@link Registrar} with the specified mounter. - * - * @param {Mounter} mounter - the {@link Mounter} to be used - * @public - */ - constructor(mounter) { - /** - * The {@link Mounter} to be used by this {@link Registrar} to mount discovered routes onto the server. - * - * @protected - * @type {Mounter} - */ - this.mounter = mounter - } - - /** - * Builds the route URL from the file path provided. - * - * This includes detecting files/directories that represent parameter path variables and inserting the names into the - * route URL correctly based on the {@link Mounter}. - * - * By default, this method simply concatenates the file path segments into the URL while dropping the - * last path segment. - * - * @param {string} file - the file path of the module from which the routes are being loaded - * @param {routerify~options} options - the options to be used - * @return {string} The route URL built from the file path. - * @protected - */ - buildUrl(file, options) { - return file.split(path.sep) - .slice(0, -1) - .reduce((memo, segment) => { - const match = segment.match(options.paramPattern) - if (match) { - segment = this.mounter.formatParamPath(match[1]) - } - - return `${memo}/${segment}` - }, '') - } - - /** - * Loads the router(s) from the module at the specified file path. - * - * @param {string} file - the file path of the module from which the routes are being loaded - * @param {routerify~options} options - the options to be used - * @return {*} The router loaded from the module at the file path. - * @protected - */ - loadRouter(file, options) { - return require(path.resolve(options.dir, file)) - } - - /** - * Loads routes from the module at the specified file path and then mounts them onto the server. - * - * @param {string} file - the file path of the module from which the routes are to be loaded - * @param {routerify~options} options - the options to be used - * @return {void} - * @public - * @abstract - */ - register(file, options) { - } - -} - -module.exports = Registrar diff --git a/src/registrar/verb.js b/src/registrar/verb-registrar.js similarity index 94% rename from src/registrar/verb.js rename to src/registrar/verb-registrar.js index 176d15a..e9279bf 100644 --- a/src/registrar/verb.js +++ b/src/registrar/verb-registrar.js @@ -24,7 +24,7 @@ const path = require('path') -const Registrar = require('./registrar') +const Registrar = require('./') /** * An implementation of {@link Registrar} where the name of the files in each directory is checked and, if it's a @@ -40,7 +40,7 @@ class VerbRegistrar extends Registrar { * @inheritDoc * @override */ - static name() { + static getName() { return 'verb' } @@ -62,4 +62,4 @@ class VerbRegistrar extends Registrar { } -module.exports = VerbRegistrar +module.exports = Registrar.define(VerbRegistrar) diff --git a/src/registrars.js b/src/registrars.js deleted file mode 100644 index d07133a..0000000 --- a/src/registrars.js +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2016 Alasdair Mercer, Skelp - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -'use strict' - -const IndexRegistrar = require('./registrar/index') -const VerbRegistrar = require('./registrar/verb') - -/** - * A list of available {@link Registrar} constructors. - * - * @private - * @type {Function[]} - */ -const constructors = [ IndexRegistrar, VerbRegistrar ] - -/** - * A map of {@link Registrar} names to their constructors. - * - * @public - * @type {Object.} - */ -const registrars = {} - -constructors.forEach((Constructor) => { - registrars[Constructor.name()] = Constructor -}) - -module.exports = registrars diff --git a/src/routerify.js b/src/routerify.js index f3fbbcf..2f90170 100644 --- a/src/routerify.js +++ b/src/routerify.js @@ -25,9 +25,9 @@ const defaultsDeep = require('lodash.defaultsdeep') const glob = require('glob') -const mounters = require('./mounters') +const Mounter = require('./mounter') const pkg = require('../package.json') -const registrars = require('./registrars') +const Registrar = require('./registrar') /** * Mounts routes onto a given server by loading modules within a specific directory. @@ -51,14 +51,14 @@ function routerify(options) { verbs: null }) - const MounterImpl = typeof options.mounter === 'string' ? mounters[options.mounter] : options.mounter + const MounterImpl = Mounter.lookup(options.mounter) if (typeof MounterImpl !== 'function') { - throw new Error(`Invalid mounter: ${options.mounter}`) + throw new Error(`Unable to lookup mounter: ${options.mounter}`) } - const RegistrarImpl = typeof options.registrar === 'string' ? registrars[options.registrar] : options.registrar + const RegistrarImpl = Registrar.lookup(options.registrar) if (typeof RegistrarImpl !== 'function') { - throw new Error(`Invalid registrar: ${options.registrar}`) + throw new Error(`Unable to lookup registrar: ${options.registrar}`) } const mounter = options.mounter = new MounterImpl() @@ -74,24 +74,6 @@ function routerify(options) { files.forEach((file) => registrar.register(file, options)) } -/** - * A map of {@link Mounter} names to their constructors. - * - * @public - * @static - * @type {Object.} - */ -routerify.mounters = mounters - -/** - * A map of {@link Registrar} names to their constructors. - * - * @public - * @static - * @type {Object.} - */ -routerify.registrars = registrars - /** * The current version of routerify. * @@ -111,11 +93,11 @@ module.exports = routerify * @property {string} [ext=".js"] - The extension of the source files to be loaded. * @property {Object} [glob] - Any options to be passed to the glob module when searching for source files * within dir. - * @property {string|Function} [mounter="express"] - The name (or constructor) of the {@link Mounter} to be used to - * mount the discovered routes on to the server. + * @property {string} [mounter="express"] - The name of the {@link Mounter} to be used to mount the discovered routes on + * to the server. * @property {RegExp} [paramPattern=/^_(.+)/] - The regular expression to be used to match path parameter variables. - * @property {string|Function} [registrar="verb"] - The name (or constructor) of the {@link Registrar} used to load - * routes from source files in a given structure and then mount them via the mounter. + * @property {string} [registrar="verb"] - The name of the {@link Registrar} used to load routes from source files in a + * given structure and then mount them via the mounter. * @property {Object} server - The server object (e.g. express()) to which the routes are to be mounted. * @property {string[]} [verbs] - The verbs (corresponding to HTTP methods) to be supported. Defaults to those provided * by the mounter if not specified. From fc16a924aaeaf6e11687cbc3c46671efbfd589c8 Mon Sep 17 00:00:00 2001 From: Alasdair Mercer Date: Thu, 15 Dec 2016 15:17:41 +0000 Subject: [PATCH 02/12] removed circular dependency introduced by #4 --- src/mounter/index.js | 3 --- src/registrar/index.js | 3 --- src/routerify.js | 4 ++++ 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/mounter/index.js b/src/mounter/index.js index 30a3788..0b6b025 100644 --- a/src/mounter/index.js +++ b/src/mounter/index.js @@ -112,6 +112,3 @@ class Mounter { } module.exports = Mounter - -require('./express-mounter') -require('./restify-mounter') diff --git a/src/registrar/index.js b/src/registrar/index.js index 7ff3cec..a0c222b 100644 --- a/src/registrar/index.js +++ b/src/registrar/index.js @@ -147,6 +147,3 @@ class Registrar { } module.exports = Registrar - -require('./index-registrar') -require('./verb-registrar') diff --git a/src/routerify.js b/src/routerify.js index 2f90170..810b64c 100644 --- a/src/routerify.js +++ b/src/routerify.js @@ -26,8 +26,12 @@ const defaultsDeep = require('lodash.defaultsdeep') const glob = require('glob') const Mounter = require('./mounter') +require('./mounter/express-mounter') +require('./mounter/restify-mounter') const pkg = require('../package.json') const Registrar = require('./registrar') +require('./registrar/index-registrar') +require('./registrar/verb-registrar') /** * Mounts routes onto a given server by loading modules within a specific directory. From 9ca9ad44fb9e06be7af827743b887a2ffe776625 Mon Sep 17 00:00:00 2001 From: Alasdair Mercer Date: Fri, 16 Dec 2016 01:42:18 +0000 Subject: [PATCH 03/12] initial unit test files added code coverage --- .gitignore | 1 + .npmignore | 3 + .travis.yml | 4 +- CONTRIBUTING.md | 2 +- README.md | 1 + package.json | 13 ++- test/.eslintrc.json | 10 +++ test/mounter/express-mounter.spec.js | 91 ++++++++++++++++++++ test/mounter/index.spec.js | 67 +++++++++++++++ test/mounter/restify-mounter.spec.js | 112 +++++++++++++++++++++++++ test/registrar/index-registrar.spec.js | 32 +++++++ test/registrar/index.spec.js | 93 ++++++++++++++++++++ test/registrar/verb-registrar.spec.js | 32 +++++++ test/routerify.spec.js | 31 +++++++ 14 files changed, 487 insertions(+), 5 deletions(-) create mode 100644 test/.eslintrc.json create mode 100644 test/mounter/express-mounter.spec.js create mode 100644 test/mounter/index.spec.js create mode 100644 test/mounter/restify-mounter.spec.js create mode 100644 test/registrar/index-registrar.spec.js create mode 100644 test/registrar/index.spec.js create mode 100644 test/registrar/verb-registrar.spec.js create mode 100644 test/routerify.spec.js diff --git a/.gitignore b/.gitignore index 552f221..fa077bd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ +coverage/ node_modules/ *.log diff --git a/.npmignore b/.npmignore index 24690e0..ad56a2c 100644 --- a/.npmignore +++ b/.npmignore @@ -1,4 +1,7 @@ +coverage/ +test/ .* +*.log AUTHORS.md CONTRIBUTING.md README.md diff --git a/.travis.yml b/.travis.yml index 9076f22..41f2138 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,8 @@ language: node_js node_js: - - "4" - "6" + - "7" script: - npm test +after_script: + - npm run coveralls diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3dc2a83..1550e55 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -7,7 +7,7 @@ Please [search existing issues](https://github.com/Skelp/routerify/issues) for t raising a new issue. Commenting on an existing issue is usually preferred over raising duplicate issues. Please ensure that all files conform to the coding standards, using the same coding style as the rest of the code base. -This can easily be checked via command-line: +All unit tests should be updated and passing as well. All of this can easily be checked via command-line: ``` bash # install/update package dependencies diff --git a/README.md b/README.md index 98d4777..3c96880 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,7 @@ [Routerify](https://github.com/Skelp/routerify) is an opinionated router loader for Express-like applications. [![Build](https://img.shields.io/travis/Skelp/routerify/develop.svg?style=flat-square)](https://travis-ci.org/Skelp/routerify) +[![Coverage](https://img.shields.io/coveralls/Skelp/routerify/develop.svg?style=flat-square)](https://coveralls.io/github/Skelp/routerify) [![Dependencies](https://img.shields.io/david/Skelp/routerify.svg?style=flat-square)](https://david-dm.org/Skelp/routerify) [![Dev Dependencies](https://img.shields.io/david/dev/Skelp/routerify.svg?style=flat-square)](https://david-dm.org/Skelp/routerify#info=devDependencies) [![License](https://img.shields.io/npm/l/routerify.svg?style=flat-square)](https://github.com/Skelp/routerify/blob/master/LICENSE.md) diff --git a/package.json b/package.json index 7bb949f..68dfd66 100644 --- a/package.json +++ b/package.json @@ -26,13 +26,20 @@ "lodash.defaultsdeep": "^4.6.0" }, "devDependencies": { + "chai": "^3.5.0", + "coveralls": "^2.11.15", "eslint": "^3.12.1", - "eslint-config-skelp": "^0.1.5" + "eslint-config-skelp": "^0.1.5", + "istanbul": "^0.4.5", + "mocha": "^3.2.0", + "sinon": "^1.17.6" }, "main": "src/routerify.js", "scripts": { - "test": "npm run test-lint", - "test-lint": "eslint src" + "coveralls": "istanbul cover _mocha --report lcovonly -- -R spec \"test/**/*.spec.js\" && coveralls < coverage/lcov.info", + "test": "npm run test-lint && npm run test-suite", + "test-lint": "eslint \"src/**/*.js\" \"test/**/*.js\"", + "test-suite": "istanbul cover _mocha -- -R spec \"test/**/*.spec.js\"" }, "engines": { "node": ">=6" diff --git a/test/.eslintrc.json b/test/.eslintrc.json new file mode 100644 index 0000000..2c226ea --- /dev/null +++ b/test/.eslintrc.json @@ -0,0 +1,10 @@ +{ + "extends": "../.eslintrc.json", + "env": { + "mocha": true + }, + "rules": { + "func-names": "off", + "no-unused-expressions": "off" + } +} diff --git a/test/mounter/express-mounter.spec.js b/test/mounter/express-mounter.spec.js new file mode 100644 index 0000000..643c7ab --- /dev/null +++ b/test/mounter/express-mounter.spec.js @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2016 Alasdair Mercer, Skelp + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +'use strict' + +const { expect } = require('chai') +const sinon = require('sinon') + +const Mounter = require('../../src/mounter') +const ExpressMounter = require('../../src/mounter/express-mounter') + +describe('mounter/express-mounter', () => { + describe('ExpressMounter', () => { + it('should extend Mounter', () => { + expect(new ExpressMounter()).to.be.an.instanceof(Mounter) + }) + + describe('.getName', () => { + it('should return correct name', () => { + expect(ExpressMounter.getName()).to.equal('express') + }) + }) + }) + + describe('ExpressMounter.prototype', () => { + let mounter + + beforeEach(() => { + mounter = new ExpressMounter() + }) + + describe('.formatParamPath', () => { + it('should prefix the parameter with a colon', () => { + expect(mounter.formatParamPath('foo')).to.equal(':foo') + }) + }) + + describe('.getDefaultVerbs', () => { + it('should contain all verbs supported by Express', () => { + expect(mounter.getDefaultVerbs()).to.eql([ 'del', 'get', 'head', 'opts', 'patch', 'post', 'put' ]) + }) + }) + + describe('.mount', () => { + context('when a single handler is provided', () => { + it('should mount the handler onto the server', () => { + const url = '/foo' + const handler = function a() {} + const server = sinon.stub({ get() {} }) + + mounter.mount(url, 'get', handler, { server }) + + expect(server.get.calledOnce).to.be.true + expect(server.get.args[0]).to.eql([ url, handler ]) + }) + }) + + context('when multiple handlers are provided in an array', () => { + it('should mount all handlers onto the server', () => { + const url = '/foo' + const handlers = [ function a() {}, function b() {} ] + const server = sinon.stub({ get() {} }) + + mounter.mount(url, 'get', handlers, { server }) + + expect(server.get.calledOnce).to.be.true + expect(server.get.args[0]).to.eql([ url ].concat(handlers)) + }) + }) + }) + }) +}) diff --git a/test/mounter/index.spec.js b/test/mounter/index.spec.js new file mode 100644 index 0000000..760a3ab --- /dev/null +++ b/test/mounter/index.spec.js @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2016 Alasdair Mercer, Skelp + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +'use strict' + +const { expect } = require('chai') + +const Mounter = require('../../src/mounter') + +describe('mounter/index', () => { + describe('Mounter', () => { + const testName = 'test' + + class TestMounter extends Mounter { + + static getName() { + return testName + } + + } + + describe('.define', () => { + it('should map type based on its name', () => { + expect(Mounter.define(TestMounter)).to.equal(TestMounter) + expect(Mounter.lookup(testName)).to.equal(TestMounter) + }) + }) + + describe('.lookup', () => { + it('should return type based with name', () => { + expect(Mounter.define(TestMounter)).to.equal(TestMounter) + expect(Mounter.lookup(testName)).to.equal(TestMounter) + }) + + it('should be case sensitive', () => { + expect(Mounter.define(TestMounter)).to.equal(TestMounter) + expect(Mounter.lookup(testName)).to.equal(TestMounter) + expect(Mounter.lookup(testName.toUpperCase())).to.be.undefined + }) + + context('when there is not matching type', () => { + it('should return nothing', () => { + expect(Mounter.lookup('foo')).to.be.undefined + }) + }) + }) + }) +}) diff --git a/test/mounter/restify-mounter.spec.js b/test/mounter/restify-mounter.spec.js new file mode 100644 index 0000000..2010f4f --- /dev/null +++ b/test/mounter/restify-mounter.spec.js @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2016 Alasdair Mercer, Skelp + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +'use strict' + +const { expect } = require('chai') +const sinon = require('sinon') + +const ExpressMounter = require('../../src/mounter/express-mounter') +const RestifyMounter = require('../../src/mounter/restify-mounter') + +describe('mounter/restify-mounter', () => { + describe('RestifyMounter', () => { + it('should extend ExpressMounter', () => { + expect(new RestifyMounter()).to.be.an.instanceof(ExpressMounter) + }) + + describe('.getName', () => { + it('should return correct name', () => { + expect(RestifyMounter.getName()).to.equal('restify') + }) + }) + }) + + describe('RestifyMounter.prototype', () => { + let mounter + + beforeEach(() => { + mounter = new RestifyMounter() + }) + + describe('.formatParamPath', () => { + it('should prefix the parameter with a colon', () => { + expect(mounter.formatParamPath('foo')).to.equal(':foo') + }) + }) + + describe('.getDefaultVerbs', () => { + it('should contain all verbs supported by Restify', () => { + expect(mounter.getDefaultVerbs()).to.eql([ 'del', 'get', 'head', 'opts', 'patch', 'post', 'put' ]) + }) + }) + + describe('.mount', () => { + context('when a single handler is provided', () => { + it('should mount the handler onto the server', () => { + const url = '/foo' + const handler = function a() {} + const server = sinon.stub({ get() {} }) + + mounter.mount(url, 'get', handler, { server }) + + expect(server.get.calledOnce).to.be.true + expect(server.get.args[0]).to.eql([ url, handler ]) + }) + }) + + context('when multiple handlers are provided in an array', () => { + it('should mount all handlers onto the server', () => { + const url = '/foo' + const handlers = [ function a() {}, function b() {} ] + const server = sinon.stub({ get() {} }) + + mounter.mount(url, 'get', handlers, { server }) + + expect(server.get.calledOnce).to.be.true + expect(server.get.args[0]).to.eql([ url ].concat(handlers)) + }) + }) + + context('when any handler has a options attached', () => { + it('should mount the handlers to the server with those options', () => { + const url = '/foo' + const verb = 'get' + const handlers = [ function a() {}, function b() {} ] + const server = sinon.stub({ get() {} }) + const version = '1.0.0' + + handlers[0].options = { version } + + mounter.mount(url, verb, handlers, { server }) + + expect(server.get.calledOnce).to.be.true + expect(server.get.args[0]).to.eql([ { + method: verb, + path: url, + version + } ].concat(handlers)) + }) + }) + }) + }) +}) diff --git a/test/registrar/index-registrar.spec.js b/test/registrar/index-registrar.spec.js new file mode 100644 index 0000000..1906985 --- /dev/null +++ b/test/registrar/index-registrar.spec.js @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2016 Alasdair Mercer, Skelp + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +'use strict' + +const { expect } = require('chai') + +const Registrar = require('../../src/registrar') +const IndexRegistrar = require('../../src/registrar/index-registrar') + +describe('registrar/index-registrar', () => { + // TODO: Complete +}) diff --git a/test/registrar/index.spec.js b/test/registrar/index.spec.js new file mode 100644 index 0000000..a829b6e --- /dev/null +++ b/test/registrar/index.spec.js @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2016 Alasdair Mercer, Skelp + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +'use strict' + +const { expect } = require('chai') +const sinon = require('sinon') + +const Mounter = require('../../src/mounter') +const Registrar = require('../../src/registrar') + +describe('registrar/index', () => { + describe('Registrar', () => { + const testName = 'test' + + class TestRegistrar extends Registrar { + + static getName() { + return testName + } + + } + + describe('.define', () => { + it('should map type based on its name', () => { + expect(Registrar.define(TestRegistrar)).to.equal(TestRegistrar) + expect(Registrar.lookup(testName)).to.equal(TestRegistrar) + }) + }) + + describe('.lookup', () => { + it('should return type based with name', () => { + expect(Registrar.define(TestRegistrar)).to.equal(TestRegistrar) + expect(Registrar.lookup(testName)).to.equal(TestRegistrar) + }) + + it('should be case sensitive', () => { + expect(Registrar.define(TestRegistrar)).to.equal(TestRegistrar) + expect(Registrar.lookup(testName)).to.equal(TestRegistrar) + expect(Registrar.lookup(testName.toUpperCase())).to.be.undefined + }) + + context('when there is not matching type', () => { + it('should return nothing', () => { + expect(Registrar.lookup('foo')).to.be.undefined + }) + }) + }) + }) + + describe('Registrar.prototype', () => { + let mounter + let registrar + + beforeEach(() => { + mounter = sinon.createStubInstance(Mounter) + registrar = new Registrar(mounter) + }) + + describe('.buildUrl', () => { + // TODO: Complete + }) + + describe('.mounter', () => { + it('should be the mounter passed to the constructor', () => { + expect(registrar.mounter).to.equal(mounter) + }) + }) + + describe('.loadRouter', () => { + // TODO: Complete + }) + }) +}) diff --git a/test/registrar/verb-registrar.spec.js b/test/registrar/verb-registrar.spec.js new file mode 100644 index 0000000..a6e6f9e --- /dev/null +++ b/test/registrar/verb-registrar.spec.js @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2016 Alasdair Mercer, Skelp + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +'use strict' + +const { expect } = require('chai') + +const Registrar = require('../../src/registrar') +const VerbRegistrar = require('../../src/registrar/verb-registrar') + +describe('registrar/verb-registrar', () => { + // TODO: Complete +}) diff --git a/test/routerify.spec.js b/test/routerify.spec.js new file mode 100644 index 0000000..78d0042 --- /dev/null +++ b/test/routerify.spec.js @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2016 Alasdair Mercer, Skelp + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +'use strict' + +const { expect } = require('chai') + +const routerify = require('../src/routerify') + +describe('routerify', () => { + // TODO: Complete +}) From 5f3c32cf50b74455f80dbf6d2432a8eec48df182 Mon Sep 17 00:00:00 2001 From: Alasdair Mercer Date: Sat, 17 Dec 2016 16:11:39 +0000 Subject: [PATCH 04/12] #6 simplified configurations even further --- src/mounter/express-mounter.js | 16 ++-- src/mounter/index.js | 52 ++----------- src/mounter/restify-mounter.js | 4 +- src/plugin.js | 49 +++++++++++++ src/registrar/index-registrar.js | 8 +- src/registrar/index.js | 70 ++---------------- src/registrar/verb-registrar.js | 8 +- src/routerify.js | 106 ++++++++++++++++++++------- src/utilities.js | 49 +++++++++++++ test/mounter/express-mounter.spec.js | 18 ++--- test/mounter/index.spec.js | 42 ++++------- test/mounter/restify-mounter.spec.js | 18 ++--- test/plugin.spec.js | 43 +++++++++++ test/registrar/index.spec.js | 55 ++------------ test/routerify.spec.js | 12 +++ test/utilities.spec.js | 45 ++++++++++++ 16 files changed, 345 insertions(+), 250 deletions(-) create mode 100644 src/plugin.js create mode 100644 src/utilities.js create mode 100644 test/plugin.spec.js create mode 100644 test/utilities.spec.js diff --git a/src/mounter/express-mounter.js b/src/mounter/express-mounter.js index 9f4d058..a7fed9d 100644 --- a/src/mounter/express-mounter.js +++ b/src/mounter/express-mounter.js @@ -22,7 +22,7 @@ 'use strict' -const Mounter = require('./') +const Mounter = require('./index') /** * An implementation of {@link Mounter} that is intended to be used for Express applications. @@ -36,24 +36,24 @@ class ExpressMounter extends Mounter { * @override * @inheritDoc */ - static getName() { - return 'express' + formatParamPath(param) { + return `:${param}` } /** * @override * @inheritDoc */ - formatParamPath(param) { - return `:${param}` + getDefaultVerbs() { + return [ 'del', 'get', 'head', 'opts', 'patch', 'post', 'put' ] } /** * @override * @inheritDoc */ - getDefaultVerbs() { - return [ 'del', 'get', 'head', 'opts', 'patch', 'post', 'put' ] + getPluginName() { + return 'express' } /** @@ -68,4 +68,4 @@ class ExpressMounter extends Mounter { } -module.exports = Mounter.define(ExpressMounter) +module.exports = ExpressMounter diff --git a/src/mounter/index.js b/src/mounter/index.js index 0b6b025..936bf68 100644 --- a/src/mounter/index.js +++ b/src/mounter/index.js @@ -22,57 +22,16 @@ 'use strict' -/** - * The available {@link Mounter} implementation constructors mapped against their name. - * - * @private - * @type {Map.} - */ -const types = new Map() +const Plugin = require('../plugin') +const Utilities = require('../utilities') /** * Responsible for mounting a route, whose information is provided by a {@link Registrar}, onto the server. * * @public + * @extends Plugin */ -class Mounter { - - /** - * Defines the specified type of {@link Mounter} so that it can be looked up using its name. - * - * @param {Function} type - the constructor of the {@link Mounter} implementation to be defined - * @return {Function} A reference to type. - * @public - * @static - */ - static define(type) { - types.set(type.getName(), type) - - return type - } - - /** - * Returns the name of the {@link Mounter} which can be used to lookup constructors. - * - * @return {string} The name. - * @public - * @static - * @abstract - */ - static getName() { - } - - /** - * Looks up the type of {@link Mounter} associated with the specified name. - * - * @param {string} name - the name associated with the {@link Mounter} implementation to be looked up - * @return {Function} The constructor of the {@link Mounter} implementation associated with name. - * @public - * @static - */ - static lookup(name) { - return types.get(name) - } +class Mounter extends Plugin { /** * Formats the given param so that it can be inserted into the route URL and interpreted by the server. @@ -83,6 +42,7 @@ class Mounter { * @abstract */ formatParamPath(param) { + Utilities.abstracted(Mounter, 'formatParamPath') } /** @@ -93,6 +53,7 @@ class Mounter { * @abstract */ getDefaultVerbs() { + Utilities.abstracted(Mounter, 'getDefaultVerbs') } /** @@ -107,6 +68,7 @@ class Mounter { * @abstract */ mount(url, verb, handlers, options) { + Utilities.abstracted(Mounter, 'mount') } } diff --git a/src/mounter/restify-mounter.js b/src/mounter/restify-mounter.js index 8dcc839..9ecad8a 100644 --- a/src/mounter/restify-mounter.js +++ b/src/mounter/restify-mounter.js @@ -41,7 +41,7 @@ class RestifyMounter extends ExpressMounter { * @override * @inheritDoc */ - static getName() { + getPluginName() { return 'restify' } @@ -65,4 +65,4 @@ class RestifyMounter extends ExpressMounter { } -module.exports = ExpressMounter.define(RestifyMounter) +module.exports = RestifyMounter diff --git a/src/plugin.js b/src/plugin.js new file mode 100644 index 0000000..9d73dee --- /dev/null +++ b/src/plugin.js @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2016 Alasdair Mercer, Skelp + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +'use strict' + +const Utilities = require('./utilities') + +/** + * A plugin can be registered to be used and then looked up later based on the plugin name. + * + * @public + */ +class Plugin { + + /** + * The name for this {@link Plugin}. + * + * This name should be unique for the type of plugin (determined by the hierarchy of the plugin looked up). + * + * @return {string} The plugin name. + * @public + * @abstract + */ + getPluginName() { + Utilities.abstracted(Plugin, 'getPluginName') + } + +} + +module.exports = Plugin diff --git a/src/registrar/index-registrar.js b/src/registrar/index-registrar.js index de215c7..b177878 100644 --- a/src/registrar/index-registrar.js +++ b/src/registrar/index-registrar.js @@ -24,7 +24,7 @@ const path = require('path') -const Registrar = require('./') +const Registrar = require('./index') /** * An implementation of {@link Registrar} where the index files in each directory is loaded as a module and the result @@ -40,7 +40,7 @@ class IndexRegistrar extends Registrar { * @inheritDoc * @override */ - static getName() { + getPluginName() { return 'index' } @@ -60,11 +60,11 @@ class IndexRegistrar extends Registrar { options.verbs.forEach((verb) => { const handlers = router[verb] if (handlers) { - this.mounter.mount(url, verb, handlers, options) + options.mounter.mount(url, verb, handlers, options) } }) } } -module.exports = Registrar.define(IndexRegistrar) +module.exports = IndexRegistrar diff --git a/src/registrar/index.js b/src/registrar/index.js index a0c222b..186fb0c 100644 --- a/src/registrar/index.js +++ b/src/registrar/index.js @@ -24,74 +24,17 @@ const path = require('path') -/** - * The available {@link Registrar} implementation constructors mapped against their name. - * - * @private - * @type {Map.} - */ -const types = new Map() +const Plugin = require('../plugin') +const Utilities = require('../utilities') /** - * Responsible for loading routes from a specific structure of modules and mounting them onto the server using a given + * Responsible for loading routes from a specific structure of modules and mounting them onto the server using a * {@link Mounter}. * * @public + * @extends Plugin */ -class Registrar { - - /** - * Defines the specified type of {@link Registrar} so that it can be looked up using its name. - * - * @param {Function} type - the constructor of the {@link Registrar} implementation to be defined - * @return {Function} A reference to type. - * @public - * @static - */ - static define(type) { - types.set(type.getName(), type) - - return type - } - - /** - * Returns the name of the {@link Registrar} which can be used to lookup constructors. - * - * @return {string} The name. - * @public - * @static - * @abstract - */ - static getName() { - } - - /** - * Looks up the type of {@link Registrar} associated with the specified name. - * - * @param {string} name - the name associated with the {@link Registrar} implementation to be looked up - * @return {Function} The constructor of the {@link Registrar} implementation associated with name. - * @public - * @static - */ - static lookup(name) { - return types.get(name) - } - - /** - * Creates an instance of {@link Registrar} with the specified mounter. - * - * @param {Mounter} mounter - the {@link Mounter} to be used - * @public - */ - constructor(mounter) { - /** - * The {@link Mounter} to be used by this {@link Registrar} to mount discovered routes onto the server. - * - * @protected - * @type {Mounter} - */ - this.mounter = mounter - } +class Registrar extends Plugin { /** * Builds the route URL from the file path provided. @@ -113,7 +56,7 @@ class Registrar { .reduce((memo, segment) => { const match = segment.match(options.paramPattern) if (match) { - segment = this.mounter.formatParamPath(match[1]) + segment = options.mounter.formatParamPath(match[1]) } return `${memo}/${segment}` @@ -142,6 +85,7 @@ class Registrar { * @abstract */ register(file, options) { + Utilities.abstracted(Registrar, 'register') } } diff --git a/src/registrar/verb-registrar.js b/src/registrar/verb-registrar.js index e9279bf..80dc305 100644 --- a/src/registrar/verb-registrar.js +++ b/src/registrar/verb-registrar.js @@ -24,7 +24,7 @@ const path = require('path') -const Registrar = require('./') +const Registrar = require('./index') /** * An implementation of {@link Registrar} where the name of the files in each directory is checked and, if it's a @@ -40,7 +40,7 @@ class VerbRegistrar extends Registrar { * @inheritDoc * @override */ - static getName() { + getPluginName() { return 'verb' } @@ -57,9 +57,9 @@ class VerbRegistrar extends Registrar { const handlers = this.loadRouter(file, options) const url = this.buildUrl(file, options) - this.mounter.mount(url, name, handlers, options) + options.mounter.mount(url, name, handlers, options) } } -module.exports = Registrar.define(VerbRegistrar) +module.exports = VerbRegistrar diff --git a/src/routerify.js b/src/routerify.js index 810b64c..d701f36 100644 --- a/src/routerify.js +++ b/src/routerify.js @@ -25,13 +25,21 @@ const defaultsDeep = require('lodash.defaultsdeep') const glob = require('glob') +const ExpressMounter = require('./mounter/express-mounter') +const IndexRegistrar = require('./registrar/index-registrar') const Mounter = require('./mounter') -require('./mounter/express-mounter') -require('./mounter/restify-mounter') -const pkg = require('../package.json') +const { version } = require('../package.json') const Registrar = require('./registrar') -require('./registrar/index-registrar') -require('./registrar/verb-registrar') +const RestifyMounter = require('./mounter/restify-mounter') +const VerbRegistrar = require('./registrar/verb-registrar') + +/** + * A set containing all registered {@link Plugin} instances. + * + * @private + * @type {Set.} + */ +const plugins = new Set() /** * Mounts routes onto a given server by loading modules within a specific directory. @@ -55,18 +63,8 @@ function routerify(options) { verbs: null }) - const MounterImpl = Mounter.lookup(options.mounter) - if (typeof MounterImpl !== 'function') { - throw new Error(`Unable to lookup mounter: ${options.mounter}`) - } - - const RegistrarImpl = Registrar.lookup(options.registrar) - if (typeof RegistrarImpl !== 'function') { - throw new Error(`Unable to lookup registrar: ${options.registrar}`) - } - - const mounter = options.mounter = new MounterImpl() - const registrar = options.registrar = new RegistrarImpl(mounter) + const mounter = options.mounter = routerify.lookup(Mounter, options.mounter) + const registrar = options.registrar = routerify.lookup(Registrar, options.registrar) if (!options.verbs) { options.verbs = mounter.getDefaultVerbs() @@ -78,14 +76,72 @@ function routerify(options) { files.forEach((file) => registrar.register(file, options)) } -/** - * The current version of routerify. - * - * @public - * @static - * @type {string} - */ -routerify.version = pkg.version +Object.assign(routerify, { + + /** + * Looks up all registered {@link Plugin} instances that inherit from a given type. + * + * Optionally, name can be provided so that only the {@link Plugin} with that name is returned, by + * itself. If name is provided and {@link Plugin} is found with that name that also inherits from + * type, then this method will throw an error. + * + * @param {Function} type - the type of {@link Plugin} to be looked up + * @param {string} [name] - the name of the specific {@link Plugin} to be looked up (all instances of + * type will be returned if omitted) + * @return {Plugin|Plugins[]} All plugins that inherit from type if no name is specified or, + * if it is specified, the {@link Plugin} with name of the given type. + * @throws {Error} If name is provided but no matching {@link Plugin} could be found. + * @public + * @static + */ + lookup(type, name) { + if (typeof name === 'undefined') { + return Array.from(plugins) + .filter((plugin) => plugin instanceof type) + } + + const match = Array.from(plugins) + .find((plugin) => plugin instanceof type && plugin.getPluginName() === name) + if (match == null) { + throw new Error(`Unable to lookup ${type.name}: ${name}`) + } + + return match + }, + + /** + * Registers the specified plugin so that it can be looked up later. + * + * Nothing happens if plugin is null. + * + * @param {Plugin} plugin - the {@link Plugin} to be registered + * @return {Plugin} A reference to plugin. + * @public + * @static + */ + use(plugin) { + if (plugin) { + plugins.add(plugin) + } + + return plugin + }, + + /** + * The current version of routerify. + * + * @public + * @static + * @type {string} + */ + version + +}) + +routerify.use(new ExpressMounter()) +routerify.use(new RestifyMounter()) +routerify.use(new IndexRegistrar()) +routerify.use(new VerbRegistrar()) module.exports = routerify diff --git a/src/utilities.js b/src/utilities.js new file mode 100644 index 0000000..3cfb4f1 --- /dev/null +++ b/src/utilities.js @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2016 Alasdair Mercer, Skelp + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +'use strict' + +/** + * Contains utility methods. + * + * @public + */ +class Utilities { + + /** + * Throws an error indicating that an abstract method with the given name has not been implemented by a child of + * type. + * + * @param {Function} type - the class to which the method belongs + * @param {string} methodName - the name of abstract method + * @return {void} + * @throws {Error} If this method is ever called. + * @public + * @static + */ + static abstracted(type, methodName) { + throw new Error(`${type.name}#${methodName} has not been implemented`) + } + +} + +module.exports = Utilities diff --git a/test/mounter/express-mounter.spec.js b/test/mounter/express-mounter.spec.js index 643c7ab..8cb3e4d 100644 --- a/test/mounter/express-mounter.spec.js +++ b/test/mounter/express-mounter.spec.js @@ -29,18 +29,6 @@ const Mounter = require('../../src/mounter') const ExpressMounter = require('../../src/mounter/express-mounter') describe('mounter/express-mounter', () => { - describe('ExpressMounter', () => { - it('should extend Mounter', () => { - expect(new ExpressMounter()).to.be.an.instanceof(Mounter) - }) - - describe('.getName', () => { - it('should return correct name', () => { - expect(ExpressMounter.getName()).to.equal('express') - }) - }) - }) - describe('ExpressMounter.prototype', () => { let mounter @@ -60,6 +48,12 @@ describe('mounter/express-mounter', () => { }) }) + describe('.getPluginName', () => { + it('should return correct name', () => { + expect(mounter.getPluginName()).to.equal('express') + }) + }) + describe('.mount', () => { context('when a single handler is provided', () => { it('should mount the handler onto the server', () => { diff --git a/test/mounter/index.spec.js b/test/mounter/index.spec.js index 760a3ab..9182002 100644 --- a/test/mounter/index.spec.js +++ b/test/mounter/index.spec.js @@ -27,40 +27,28 @@ const { expect } = require('chai') const Mounter = require('../../src/mounter') describe('mounter/index', () => { - describe('Mounter', () => { - const testName = 'test' + describe('Mounter.prototype', () => { + let mounter - class TestMounter extends Mounter { - - static getName() { - return testName - } - - } - - describe('.define', () => { - it('should map type based on its name', () => { - expect(Mounter.define(TestMounter)).to.equal(TestMounter) - expect(Mounter.lookup(testName)).to.equal(TestMounter) - }) + beforeEach(() => { + mounter = new Mounter() }) - describe('.lookup', () => { - it('should return type based with name', () => { - expect(Mounter.define(TestMounter)).to.equal(TestMounter) - expect(Mounter.lookup(testName)).to.equal(TestMounter) + describe('.formatParamPath', () => { + it('should be abstract', () => { + expect(mounter.formatParamPath.bind(mounter)).to.throw('Mounter#formatParamPath has not been implemented') }) + }) - it('should be case sensitive', () => { - expect(Mounter.define(TestMounter)).to.equal(TestMounter) - expect(Mounter.lookup(testName)).to.equal(TestMounter) - expect(Mounter.lookup(testName.toUpperCase())).to.be.undefined + describe('.getDefaultVerbs', () => { + it('should be abstract', () => { + expect(mounter.getDefaultVerbs.bind(mounter)).to.throw('Mounter#getDefaultVerbs has not been implemented') }) + }) - context('when there is not matching type', () => { - it('should return nothing', () => { - expect(Mounter.lookup('foo')).to.be.undefined - }) + describe('.mount', () => { + it('should be abstract', () => { + expect(mounter.mount.bind(mounter)).to.throw('Mounter#mount has not been implemented') }) }) }) diff --git a/test/mounter/restify-mounter.spec.js b/test/mounter/restify-mounter.spec.js index 2010f4f..4a7dcaf 100644 --- a/test/mounter/restify-mounter.spec.js +++ b/test/mounter/restify-mounter.spec.js @@ -29,18 +29,6 @@ const ExpressMounter = require('../../src/mounter/express-mounter') const RestifyMounter = require('../../src/mounter/restify-mounter') describe('mounter/restify-mounter', () => { - describe('RestifyMounter', () => { - it('should extend ExpressMounter', () => { - expect(new RestifyMounter()).to.be.an.instanceof(ExpressMounter) - }) - - describe('.getName', () => { - it('should return correct name', () => { - expect(RestifyMounter.getName()).to.equal('restify') - }) - }) - }) - describe('RestifyMounter.prototype', () => { let mounter @@ -60,6 +48,12 @@ describe('mounter/restify-mounter', () => { }) }) + describe('.getPluginName', () => { + it('should return correct name', () => { + expect(mounter.getPluginName()).to.equal('restify') + }) + }) + describe('.mount', () => { context('when a single handler is provided', () => { it('should mount the handler onto the server', () => { diff --git a/test/plugin.spec.js b/test/plugin.spec.js new file mode 100644 index 0000000..e64865a --- /dev/null +++ b/test/plugin.spec.js @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2016 Alasdair Mercer, Skelp + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +'use strict' + +const { expect } = require('chai') + +const Plugin = require('../src/plugin') + +describe('plugin', () => { + describe('Plugin.prototype', () => { + let plugin + + beforeEach(() => { + plugin = new Plugin() + }) + + describe('.getPluginName', () => { + it('should be abstract', () => { + expect(plugin.getPluginName.bind(plugin)).to.throw('Plugin#getPluginName has not been implemented') + }) + }) + }) +}) diff --git a/test/registrar/index.spec.js b/test/registrar/index.spec.js index a829b6e..72942b0 100644 --- a/test/registrar/index.spec.js +++ b/test/registrar/index.spec.js @@ -25,69 +25,28 @@ const { expect } = require('chai') const sinon = require('sinon') -const Mounter = require('../../src/mounter') const Registrar = require('../../src/registrar') describe('registrar/index', () => { - describe('Registrar', () => { - const testName = 'test' - - class TestRegistrar extends Registrar { - - static getName() { - return testName - } - - } - - describe('.define', () => { - it('should map type based on its name', () => { - expect(Registrar.define(TestRegistrar)).to.equal(TestRegistrar) - expect(Registrar.lookup(testName)).to.equal(TestRegistrar) - }) - }) - - describe('.lookup', () => { - it('should return type based with name', () => { - expect(Registrar.define(TestRegistrar)).to.equal(TestRegistrar) - expect(Registrar.lookup(testName)).to.equal(TestRegistrar) - }) - - it('should be case sensitive', () => { - expect(Registrar.define(TestRegistrar)).to.equal(TestRegistrar) - expect(Registrar.lookup(testName)).to.equal(TestRegistrar) - expect(Registrar.lookup(testName.toUpperCase())).to.be.undefined - }) - - context('when there is not matching type', () => { - it('should return nothing', () => { - expect(Registrar.lookup('foo')).to.be.undefined - }) - }) - }) - }) - describe('Registrar.prototype', () => { - let mounter let registrar beforeEach(() => { - mounter = sinon.createStubInstance(Mounter) - registrar = new Registrar(mounter) + registrar = new Registrar() }) describe('.buildUrl', () => { // TODO: Complete }) - describe('.mounter', () => { - it('should be the mounter passed to the constructor', () => { - expect(registrar.mounter).to.equal(mounter) - }) - }) - describe('.loadRouter', () => { // TODO: Complete }) + + describe('.register', () => { + it('should be abstract', () => { + expect(registrar.register.bind(registrar)).to.throw('Registrar#register has not been implemented') + }) + }) }) }) diff --git a/test/routerify.spec.js b/test/routerify.spec.js index 78d0042..fb8a4ab 100644 --- a/test/routerify.spec.js +++ b/test/routerify.spec.js @@ -28,4 +28,16 @@ const routerify = require('../src/routerify') describe('routerify', () => { // TODO: Complete + + describe('.lookup', () => { + // TODO: Complete + }) + + describe('.use', () => { + // TODO: Complete + }) + + describe('.version', () => { + // TODO: Complete + }) }) diff --git a/test/utilities.spec.js b/test/utilities.spec.js new file mode 100644 index 0000000..08e1b86 --- /dev/null +++ b/test/utilities.spec.js @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2016 Alasdair Mercer, Skelp + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +'use strict' + +const { expect } = require('chai') + +const Utilities = require('../src/utilities') + +describe('utilities', () => { + describe('Utilities', () => { + describe('.abstracted', () => { + it('should throw an error', () => { + class Foo { + + static bar() { + Utilities.abstracted(Foo, 'bar') + } + + } + + expect(Foo.bar).to.throw(Error, 'Foo#bar has not been implemented') + }) + }) + }) +}) From 23199698193d599c6791f2d6eb419a0a0ace781f Mon Sep 17 00:00:00 2001 From: Alasdair Mercer Date: Sat, 17 Dec 2016 16:39:03 +0000 Subject: [PATCH 05/12] #6 updated documentation with new approach to configurations --- README.md | 53 +++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 43 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 3c96880..140a3f2 100644 --- a/README.md +++ b/README.md @@ -36,9 +36,9 @@ You'll need to have at least [Node.js](https://nodejs.org) 6 or newer. ## Configurations -Routerify is opinionated. Routerify is also configurable and extensible. The two core concepts that Routerify has are -*Registrars* and *Mounters*. Through these, Routerify can be configured to load routes from modules in any pattern and -mount them onto any server. +Routerify is opinionated. Routerify is also configurable and extensible via plugins. The two core plugin types that +Routerify has are *Registrars* and *Mounters*. Through these, Routerify can be configured to load routes from modules in +any pattern and mount them onto any server. ### Registrars @@ -147,7 +147,7 @@ const Registrar = require('routerify/src/registrar') class CustomRegistrar extends Registrar { - static getName() { + getPluginName() { return 'custom' } @@ -157,7 +157,9 @@ class CustomRegistrar extends Registrar { } } -module.exports = Registrar.define(CustomRegistrar) +routerify.use(new CustomRegistrar()) + +module.exports = CustomRegistrar ``` Now your new registrar can be used by simply specifying its name in the options: @@ -214,10 +216,6 @@ const routerify = require('routerify') const Mounter = require('routerify/src/mounter') class CustomMounter extends Mounter { - - static getName() { - return 'custom' - } formatParamPath(param) { // Format param for insertion into the route URL @@ -229,13 +227,19 @@ class CustomMounter extends Mounter { return [...] } + getPluginName() { + return 'custom' + } + mount(url, verb, handlers, options) { // Mount the route onto options.server ... } } -module.exports = Mounter.define(CustomMounter) +routerify.use(new CustomMounter()) + +module.exports = CustomMounter ``` Now your new mounter can be used by simply specifying its name in the options: @@ -293,6 +297,35 @@ The following options can be passed to Routerify: Only the `server` option is required. All others have defaults. +### `routerify.lookup(type[, name])` + +This method is primarily intended for internal use and provides a means of looking up register `Plugin` instances of a +given `type`. If no `name` is provided, it will return all instances that inherit from `type`. Otherwise, this method +will return the first instance that inherits from `type` that has the given `name` and will throw an error if no +matching plugin could be found. + +``` javascript +const Plugin = require('routerify/src/plugin') +const Registrar = require('routerify/src/registrar') + +routerify.lookup(Plugin) +=> [ ExpressMounter {}, RestifyMounter {}, IndexRegistrar {}, VerbRegistrar {} ] +routerify.lookup(Registrar) +=> [ IndexRegistrar {}, VerbRegistrar {} ] +routerify.lookup(Registrar, 'verb') +=> VerbRegistrar {} +routerify.lookup(Registrar, 'foo') +=> throws Error +``` + +### `routerify.use(plugin)` + +Routerify can be configured via the use of plugins, which can be registered by this method. It takes a given instance of +`Plugin`. + +The previous examples for creating your own [mounters](#create-your-own-mounter) and +[registrars](#create-your-own-registrar) cover how to use this method. + ### `routerify.version` The current version of Routerify. From 2354053a19ab77027a3b6aeb589b3de1cd9ad46e Mon Sep 17 00:00:00 2001 From: Alasdair Mercer Date: Sat, 17 Dec 2016 17:03:15 +0000 Subject: [PATCH 06/12] #3 build now fails if coverage is below configured threshold --- .istanbul.yml | 6 ++++++ package.json | 3 ++- 2 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 .istanbul.yml diff --git a/.istanbul.yml b/.istanbul.yml new file mode 100644 index 0000000..4d6d01a --- /dev/null +++ b/.istanbul.yml @@ -0,0 +1,6 @@ +check: + global: + statements: 95 + lines: 95 + branches: 95 + functions: 95 diff --git a/package.json b/package.json index 68dfd66..e8a4d12 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,8 @@ "main": "src/routerify.js", "scripts": { "coveralls": "istanbul cover _mocha --report lcovonly -- -R spec \"test/**/*.spec.js\" && coveralls < coverage/lcov.info", - "test": "npm run test-lint && npm run test-suite", + "test": "npm run test-lint && npm run test-suite && npm run test-coverage", + "test-coverage": "istanbul check-coverage", "test-lint": "eslint \"src/**/*.js\" \"test/**/*.js\"", "test-suite": "istanbul cover _mocha -- -R spec \"test/**/*.spec.js\"" }, From e8cbc77ee9016cfa491589bceb05489ae18d6237 Mon Sep 17 00:00:00 2001 From: Alasdair Mercer Date: Sun, 18 Dec 2016 02:13:43 +0000 Subject: [PATCH 07/12] #3 completed unit tests and increased code coverage threshold to 100% --- .istanbul.yml | 8 +- src/registrar/index-registrar.js | 11 +- test/fixtures/registrar/index/ignore.js | 27 +++ test/fixtures/registrar/index/index.js | 34 +++ .../registrar/index/level1/_id/index.js | 35 ++++ .../index/level1/_id/level2/index.js | 27 +++ test/fixtures/registrar/index/level1/index.js | 33 +++ test/fixtures/registrar/verb/get.js | 27 +++ test/fixtures/registrar/verb/head.js | 28 +++ test/fixtures/registrar/verb/ignore.js | 27 +++ .../fixtures/registrar/verb/level1/_id/del.js | 27 +++ .../fixtures/registrar/verb/level1/_id/get.js | 27 +++ .../registrar/verb/level1/_id/level2/get.js | 27 +++ .../fixtures/registrar/verb/level1/_id/put.js | 27 +++ test/fixtures/registrar/verb/level1/get.js | 27 +++ test/fixtures/registrar/verb/level1/post.js | 27 +++ test/mounter/express-mounter.spec.js | 1 - test/mounter/restify-mounter.spec.js | 1 - test/registrar/index-registrar.spec.js | 26 ++- test/registrar/index.spec.js | 21 +- test/registrar/registrar-test-case.js | 95 +++++++++ test/registrar/verb-registrar.spec.js | 26 ++- test/routerify.spec.js | 197 +++++++++++++++++- 23 files changed, 755 insertions(+), 31 deletions(-) create mode 100644 test/fixtures/registrar/index/ignore.js create mode 100644 test/fixtures/registrar/index/index.js create mode 100644 test/fixtures/registrar/index/level1/_id/index.js create mode 100644 test/fixtures/registrar/index/level1/_id/level2/index.js create mode 100644 test/fixtures/registrar/index/level1/index.js create mode 100644 test/fixtures/registrar/verb/get.js create mode 100644 test/fixtures/registrar/verb/head.js create mode 100644 test/fixtures/registrar/verb/ignore.js create mode 100644 test/fixtures/registrar/verb/level1/_id/del.js create mode 100644 test/fixtures/registrar/verb/level1/_id/get.js create mode 100644 test/fixtures/registrar/verb/level1/_id/level2/get.js create mode 100644 test/fixtures/registrar/verb/level1/_id/put.js create mode 100644 test/fixtures/registrar/verb/level1/get.js create mode 100644 test/fixtures/registrar/verb/level1/post.js create mode 100644 test/registrar/registrar-test-case.js diff --git a/.istanbul.yml b/.istanbul.yml index 4d6d01a..5ea2bdd 100644 --- a/.istanbul.yml +++ b/.istanbul.yml @@ -1,6 +1,6 @@ check: global: - statements: 95 - lines: 95 - branches: 95 - functions: 95 + statements: 100 + lines: 100 + branches: 100 + functions: 100 diff --git a/src/registrar/index-registrar.js b/src/registrar/index-registrar.js index b177878..13bd021 100644 --- a/src/registrar/index-registrar.js +++ b/src/registrar/index-registrar.js @@ -57,12 +57,11 @@ class IndexRegistrar extends Registrar { const router = this.loadRouter(file, options) const url = this.buildUrl(file, options) - options.verbs.forEach((verb) => { - const handlers = router[verb] - if (handlers) { - options.mounter.mount(url, verb, handlers, options) - } - }) + Object.keys(router) + .filter(options.verbs.includes.bind(options.verbs)) + .forEach((verb) => { + options.mounter.mount(url, verb, router[verb], options) + }) } } diff --git a/test/fixtures/registrar/index/ignore.js b/test/fixtures/registrar/index/ignore.js new file mode 100644 index 0000000..09f57a9 --- /dev/null +++ b/test/fixtures/registrar/index/ignore.js @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2016 Alasdair Mercer, Skelp + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +'use strict' + +function ignorePost(req, res, next) {} + +module.exports = { post: ignorePost } diff --git a/test/fixtures/registrar/index/index.js b/test/fixtures/registrar/index/index.js new file mode 100644 index 0000000..60ea5bc --- /dev/null +++ b/test/fixtures/registrar/index/index.js @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2016 Alasdair Mercer, Skelp + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +'use strict' + +function rootGet(req, res, next) {} +function rootHead1(req, res, next) {} +function rootHead2(req, res, next) {} + +module.exports = { + + get: rootGet, + head: [ rootHead1, rootHead2 ] + +} diff --git a/test/fixtures/registrar/index/level1/_id/index.js b/test/fixtures/registrar/index/level1/_id/index.js new file mode 100644 index 0000000..29796e1 --- /dev/null +++ b/test/fixtures/registrar/index/level1/_id/index.js @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2016 Alasdair Mercer, Skelp + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +'use strict' + +function level1IdDel(req, res, next) {} +function level1IdGet(req, res, next) {} +function level1IdPost(req, res, next) {} + +module.exports = { + + del: level1IdDel, + get: level1IdGet, + post: level1IdPost + +} diff --git a/test/fixtures/registrar/index/level1/_id/level2/index.js b/test/fixtures/registrar/index/level1/_id/level2/index.js new file mode 100644 index 0000000..26fbac2 --- /dev/null +++ b/test/fixtures/registrar/index/level1/_id/level2/index.js @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2016 Alasdair Mercer, Skelp + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +'use strict' + +function level1IdLevel2Get(req, res, next) {} + +module.exports = { get: level1IdLevel2Get } diff --git a/test/fixtures/registrar/index/level1/index.js b/test/fixtures/registrar/index/level1/index.js new file mode 100644 index 0000000..9067f9e --- /dev/null +++ b/test/fixtures/registrar/index/level1/index.js @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2016 Alasdair Mercer, Skelp + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +'use strict' + +function level1Get(req, res, next) {} +function level1Post(req, res, next) {} + +module.exports = { + + get: level1Get, + post: level1Post + +} diff --git a/test/fixtures/registrar/verb/get.js b/test/fixtures/registrar/verb/get.js new file mode 100644 index 0000000..6c293fe --- /dev/null +++ b/test/fixtures/registrar/verb/get.js @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2016 Alasdair Mercer, Skelp + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +'use strict' + +function rootGet(req, res, next) {} + +module.exports = rootGet diff --git a/test/fixtures/registrar/verb/head.js b/test/fixtures/registrar/verb/head.js new file mode 100644 index 0000000..25c8c6c --- /dev/null +++ b/test/fixtures/registrar/verb/head.js @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2016 Alasdair Mercer, Skelp + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +'use strict' + +function rootHead1(req, res, next) {} +function rootHead2(req, res, next) {} + +module.exports = [ rootHead1, rootHead2 ] diff --git a/test/fixtures/registrar/verb/ignore.js b/test/fixtures/registrar/verb/ignore.js new file mode 100644 index 0000000..a09aa05 --- /dev/null +++ b/test/fixtures/registrar/verb/ignore.js @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2016 Alasdair Mercer, Skelp + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +'use strict' + +function ignore(req, res, next) {} + +module.exports = ignore diff --git a/test/fixtures/registrar/verb/level1/_id/del.js b/test/fixtures/registrar/verb/level1/_id/del.js new file mode 100644 index 0000000..4629a55 --- /dev/null +++ b/test/fixtures/registrar/verb/level1/_id/del.js @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2016 Alasdair Mercer, Skelp + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +'use strict' + +function level1IdDel(req, res, next) {} + +module.exports = level1IdDel diff --git a/test/fixtures/registrar/verb/level1/_id/get.js b/test/fixtures/registrar/verb/level1/_id/get.js new file mode 100644 index 0000000..1c7ba44 --- /dev/null +++ b/test/fixtures/registrar/verb/level1/_id/get.js @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2016 Alasdair Mercer, Skelp + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +'use strict' + +function level1IdGet(req, res, next) {} + +module.exports = level1IdGet diff --git a/test/fixtures/registrar/verb/level1/_id/level2/get.js b/test/fixtures/registrar/verb/level1/_id/level2/get.js new file mode 100644 index 0000000..9be6e3d --- /dev/null +++ b/test/fixtures/registrar/verb/level1/_id/level2/get.js @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2016 Alasdair Mercer, Skelp + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +'use strict' + +function level1IdLevel2Get(req, res, next) {} + +module.exports = level1IdLevel2Get diff --git a/test/fixtures/registrar/verb/level1/_id/put.js b/test/fixtures/registrar/verb/level1/_id/put.js new file mode 100644 index 0000000..f17da57 --- /dev/null +++ b/test/fixtures/registrar/verb/level1/_id/put.js @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2016 Alasdair Mercer, Skelp + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +'use strict' + +function level1IdPut(req, res, next) {} + +module.exports = level1IdPut diff --git a/test/fixtures/registrar/verb/level1/get.js b/test/fixtures/registrar/verb/level1/get.js new file mode 100644 index 0000000..5f4540c --- /dev/null +++ b/test/fixtures/registrar/verb/level1/get.js @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2016 Alasdair Mercer, Skelp + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +'use strict' + +function level1Get(req, res, next) {} + +module.exports = level1Get diff --git a/test/fixtures/registrar/verb/level1/post.js b/test/fixtures/registrar/verb/level1/post.js new file mode 100644 index 0000000..01a0ee1 --- /dev/null +++ b/test/fixtures/registrar/verb/level1/post.js @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2016 Alasdair Mercer, Skelp + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +'use strict' + +function level1Post(req, res, next) {} + +module.exports = level1Post diff --git a/test/mounter/express-mounter.spec.js b/test/mounter/express-mounter.spec.js index 8cb3e4d..ab2347d 100644 --- a/test/mounter/express-mounter.spec.js +++ b/test/mounter/express-mounter.spec.js @@ -25,7 +25,6 @@ const { expect } = require('chai') const sinon = require('sinon') -const Mounter = require('../../src/mounter') const ExpressMounter = require('../../src/mounter/express-mounter') describe('mounter/express-mounter', () => { diff --git a/test/mounter/restify-mounter.spec.js b/test/mounter/restify-mounter.spec.js index 4a7dcaf..6054145 100644 --- a/test/mounter/restify-mounter.spec.js +++ b/test/mounter/restify-mounter.spec.js @@ -25,7 +25,6 @@ const { expect } = require('chai') const sinon = require('sinon') -const ExpressMounter = require('../../src/mounter/express-mounter') const RestifyMounter = require('../../src/mounter/restify-mounter') describe('mounter/restify-mounter', () => { diff --git a/test/registrar/index-registrar.spec.js b/test/registrar/index-registrar.spec.js index 1906985..3000df1 100644 --- a/test/registrar/index-registrar.spec.js +++ b/test/registrar/index-registrar.spec.js @@ -22,11 +22,29 @@ 'use strict' -const { expect } = require('chai') - -const Registrar = require('../../src/registrar') const IndexRegistrar = require('../../src/registrar/index-registrar') +const RegistrarTestCase = require('./registrar-test-case') describe('registrar/index-registrar', () => { - // TODO: Complete + describe('IndexRegistrar.prototype', () => { + let registrar + let testCase + + beforeEach(() => { + registrar = new IndexRegistrar() + testCase = new RegistrarTestCase(registrar, 'index') + }) + + describe('.getPluginName', () => { + it('should return correct name', () => { + testCase.testGetPluginName() + }) + }) + + describe('.register', () => { + it('should register index file-based routes via mounter', () => { + testCase.testRegister() + }) + }) + }) }) diff --git a/test/registrar/index.spec.js b/test/registrar/index.spec.js index 72942b0..58a741f 100644 --- a/test/registrar/index.spec.js +++ b/test/registrar/index.spec.js @@ -23,24 +23,41 @@ 'use strict' const { expect } = require('chai') +const path = require('path') const sinon = require('sinon') +const Mounter = require('../../src/mounter') const Registrar = require('../../src/registrar') +const routerify = require('../../src/routerify') describe('registrar/index', () => { describe('Registrar.prototype', () => { + let mounter let registrar beforeEach(() => { + mounter = sinon.createStubInstance(Mounter) registrar = new Registrar() }) describe('.buildUrl', () => { - // TODO: Complete + it('should build the URL correctly', () => { + mounter.formatParamPath.withArgs('id').returns('{id}') + + const file = path.join('test', 'fixtures', 'registrar', 'verb', 'level1', '_id', 'level2', 'get.js') + const url = registrar.buildUrl(file, { + mounter, + paramPattern: /^_(.+)/ + }) + + expect(url).to.equal('/test/fixtures/registrar/verb/level1/{id}/level2') + }) }) describe('.loadRouter', () => { - // TODO: Complete + it('should load file as module based on "dir" option', () => { + expect(registrar.loadRouter('routerify.js', { dir: 'src' })).to.equal(routerify) + }) }) describe('.register', () => { diff --git a/test/registrar/registrar-test-case.js b/test/registrar/registrar-test-case.js new file mode 100644 index 0000000..01925f5 --- /dev/null +++ b/test/registrar/registrar-test-case.js @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2016 Alasdair Mercer, Skelp + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +'use strict' + +/* eslint "arrow-body-style": "off" */ + +const { expect } = require('chai') +const glob = require('glob') +const path = require('path') +const sinon = require('sinon') + +const Mounter = require('../../src/mounter/index') + +class Route { + + constructor(url, verb, handlers = 1) { + this.url = url + this.verb = verb + this.handlers = handlers + } + +} + +class RegistrarTestCase { + + constructor(registrar, name) { + this.registrar = registrar + this.name = name + this.directory = `test/fixtures/registrar/${name}` + this.mounter = sinon.createStubInstance(Mounter) + this.mounter.formatParamPath.withArgs('id').returns('{id}') + this.expected = [ + new Route('/', 'get'), + new Route('/', 'head', 2), + new Route('/level1', 'get'), + new Route('/level1', 'post'), + new Route('/level1/{id}', 'del'), + new Route('/level1/{id}', 'get'), + new Route('/level1/{id}', 'put'), + new Route('/level1/{id}/level2', 'get') + ] + } + + testGetPluginName() { + expect(this.registrar.getPluginName()).to.equal(this.name) + } + + testRegister() { + const directory = this.directory.replace(/\\|\//g, path.sep) + const fixtures = glob.sync('**/*.js', { cwd: directory }) + const options = { + dir: directory, + ext: '.js', + mounter: this.mounter, + paramPattern: /^_(.+)/, + registrar: this.registrar, + verbs: [ 'del', 'get', 'head', 'opts', 'patch', 'post', 'put' ] + } + + fixtures.forEach((fixture) => { + this.registrar.register(fixture, options) + }) + + expect(this.mounter.mount.callCount).to.equal(this.expected.length) + + this.expected.forEach((route) => { + this.mounter.mount.calledWith(route.url, route.verb, sinon.match((arg) => { + return route.handlers === 1 ? typeof arg === 'function' : Array.isArray(arg) && arg.length === route.handlers + }), options) + }) + } + +} + +module.exports = RegistrarTestCase diff --git a/test/registrar/verb-registrar.spec.js b/test/registrar/verb-registrar.spec.js index a6e6f9e..7d2e59b 100644 --- a/test/registrar/verb-registrar.spec.js +++ b/test/registrar/verb-registrar.spec.js @@ -22,11 +22,29 @@ 'use strict' -const { expect } = require('chai') - -const Registrar = require('../../src/registrar') +const RegistrarTestCase = require('./registrar-test-case') const VerbRegistrar = require('../../src/registrar/verb-registrar') describe('registrar/verb-registrar', () => { - // TODO: Complete + describe('VerbRegistrar.prototype', () => { + let registrar + let testCase + + beforeEach(() => { + registrar = new VerbRegistrar() + testCase = new RegistrarTestCase(registrar, 'verb') + }) + + describe('.getPluginName', () => { + it('should return correct name', () => { + testCase.testGetPluginName() + }) + }) + + describe('.register', () => { + it('should register verb file-based routes via mounter', () => { + testCase.testRegister() + }) + }) + }) }) diff --git a/test/routerify.spec.js b/test/routerify.spec.js index fb8a4ab..2b7a42b 100644 --- a/test/routerify.spec.js +++ b/test/routerify.spec.js @@ -23,21 +23,200 @@ 'use strict' const { expect } = require('chai') +const glob = require('glob') +const sinon = require('sinon') +const ExpressMounter = require('../src/mounter/express-mounter') +const IndexRegistrar = require('../src/registrar/index-registrar') +const Mounter = require('../src/mounter') +const { version } = require('../package.json') +const Plugin = require('../src/plugin') +const Registrar = require('../src/registrar') +const RestifyMounter = require('../src/mounter/restify-mounter') const routerify = require('../src/routerify') +const VerbRegistrar = require('../src/registrar/verb-registrar') describe('routerify', () => { - // TODO: Complete + describe('routerify', () => { + let mounter + const mounterName = 'routerify-test-mounter' + let registrar + const registrarName = 'routerify-test-registrar' - describe('.lookup', () => { - // TODO: Complete - }) + before(() => { + mounter = sinon.createStubInstance(Mounter) + mounter.getPluginName.returns(mounterName) + registrar = sinon.createStubInstance(Registrar) + registrar.getPluginName.returns(registrarName) - describe('.use', () => { - // TODO: Complete - }) + routerify.use(mounter) + routerify.use(registrar) + }) + + beforeEach(() => { + sinon.stub(glob, 'sync') + }) + + afterEach(() => { + mounter.getDefaultVerbs.reset() + registrar.register.reset() + + glob.sync.restore() + }) + + it('should find and register all route modules based on options', () => { + const files = [ + 'file1.coffee', + 'file2.coffee', + 'file3.coffee' + ] + const options = { + dir: 'test', + ext: '.coffee', + glob: { foo: 'bar' }, + mounter: mounterName, + paramPattern: /^\.(.+)/, + registrar: registrarName, + server: sinon.spy(), + verbs: [ 'get', 'head' ] + } + + glob.sync.withArgs('**/*.coffee', { cwd: 'test', foo: 'bar' }).returns(files) + + routerify(options) + + expect(registrar.register.callCount).to.equal(files.length) + files.forEach((file) => { + expect(registrar.register.calledWithExactly(file, options)) + }) + }) + + context('when no options are specifed', () => { + let defaultMounter + let defaultRegistrar + + beforeEach(() => { + defaultMounter = routerify.lookup(Mounter, 'express') + sinon.stub(defaultMounter, 'getDefaultVerbs') + defaultRegistrar = routerify.lookup(Registrar, 'verb') + sinon.stub(defaultRegistrar, 'register') + }) + + afterEach(() => { + defaultMounter.getDefaultVerbs.restore() + defaultRegistrar.register.restore() + }) + + it('should use correct default options', () => { + const files = [ + 'file1.js', + 'file2.js', + 'file3.js' + ] + const server = sinon.spy() + const verbs = [ 'get', 'head' ] + const options = { + dir: process.cwd(), + ext: '.js', + glob: {}, + mounter: 'express', + paramPattern: /^_(.+)/, + registrar: 'verb', + server, + verbs + } + + defaultMounter.getDefaultVerbs.returns(verbs) + glob.sync.withArgs('**/*.js', { cwd: options.dir }).returns(files) + + routerify({ server }) + + expect(defaultRegistrar.register.callCount).to.equal(files.length) + files.forEach((file) => { + expect(defaultRegistrar.register.calledWithExactly(file, options)) + }) + }) + }) + + describe('.lookup', () => { + context('when only a type is provided', () => { + it('should return all plugins when Plugin is passed', () => { + const plugins = routerify.lookup(Plugin) + + expect(plugins).to.have.length.of.at.least(4) + expect(plugins[0]).to.be.an.instanceOf(ExpressMounter) + expect(plugins[1]).to.be.an.instanceOf(RestifyMounter) + expect(plugins[2]).to.be.an.instanceOf(IndexRegistrar) + expect(plugins[3]).to.be.an.instanceOf(VerbRegistrar) + }) + + it('should return only plugins of a given type when it is passed', () => { + const mounters = routerify.lookup(Mounter) + + expect(mounters).to.have.length.of.at.least(2) + expect(mounters[0]).to.be.an.instanceOf(ExpressMounter) + expect(mounters[1]).to.be.an.instanceOf(RestifyMounter) + + const registrars = routerify.lookup(Registrar) + + expect(registrars[0]).to.be.an.instanceOf(IndexRegistrar) + expect(registrars[1]).to.be.an.instanceOf(VerbRegistrar) + }) + }) + + context('when an existing name is provided along with type', () => { + it('should return the specific ', () => { + class LookupTestPlugin extends Plugin { + + getPluginName() { + return 'lookup-test-plugin' + } + + } + + const plugin = routerify.use(new LookupTestPlugin()) + + expect(routerify.lookup(Plugin, 'lookup-test-plugin')).to.equal(plugin) + }) + }) + + context('when an unknown name is provided along with type', () => { + it('should throw an error', () => { + expect(routerify.lookup.bind(routerify, Plugin, 'foo')).to.throw(Error, 'Unable to lookup Plugin: foo') + }) + }) + }) + + describe('.use', () => { + it('should register the plugin', () => { + class UseTestPlugin extends Plugin { + + getPluginName() { + return 'use-test-plugin' + } + + } + + const plugin = new UseTestPlugin() + + expect(routerify.use(plugin)).to.equal(plugin) + expect(routerify.lookup(Plugin, 'use-test-plugin')).to.equal(plugin) + }) + + context('when plugin is null', () => { + it('should not add a plugin', () => { + const plugins = routerify.lookup(Object) + + expect(routerify.use(null)).to.be.null + expect(routerify.lookup(Object)).to.eql(plugins) + }) + }) + }) - describe('.version', () => { - // TODO: Complete + describe('.version', () => { + it('should match package version', () => { + expect(routerify.version).to.equal(version) + }) + }) }) }) From c2d9428cdc276dc507dcf1caa6379edf7c116620 Mon Sep 17 00:00:00 2001 From: Alasdair Mercer Date: Sun, 18 Dec 2016 02:17:50 +0000 Subject: [PATCH 08/12] #7 changed default registrar to IndexRegistrar --- README.md | 74 +++++++++++++++++++++--------------------- src/routerify.js | 2 +- test/routerify.spec.js | 4 +-- 3 files changed, 40 insertions(+), 40 deletions(-) diff --git a/README.md b/README.md index 140a3f2..3863259 100644 --- a/README.md +++ b/README.md @@ -50,45 +50,10 @@ you can simply [create your own](#create-your-own-registrar) and, optionally, [create a pull request](https://github.com/Skelp/routerify/compare) to share it with everyone, provided our opinions match :) If not, you can always release it as a plugin. -#### `VerbRegistrar` - -**Name:** `"verb"` -**Default:** Yes -**How it works:** - -It expects the following file structure under the target directory: - -``` -- / - - users/ - - _userId/ - - sessions/ - - get.js - - del.js - - get.js - - put.js - - get.js - - helper.js - - post.js -``` - -Only files whose base name matches that of a supported verb will be loaded (e.g. `helper.js` in the above example would -be ignored) and it expects each one of those modules to export either a single handler method or an array of handler -methods. - -In the example above, the following routes would be registered: - -* `GET /users` -* `POST /users` -* `DELETE /users/:userId` -* `GET /users/:userId` -* `PUT /users/:userId` -* `GET /users/:userId/sessions` - #### `IndexRegistrar` **Name:** `"index"` -**Default:** No +**Default:** Yes **How it works:** It expects the following file structure under the target directory: @@ -137,6 +102,41 @@ following routes would be registered: The `fetchUser` method is ignored because it does not match a supported verb. +#### `VerbRegistrar` + +**Name:** `"verb"` +**Default:** No +**How it works:** + +It expects the following file structure under the target directory: + +``` +- / + - users/ + - _userId/ + - sessions/ + - get.js + - del.js + - get.js + - put.js + - get.js + - helper.js + - post.js +``` + +Only files whose base name matches that of a supported verb will be loaded (e.g. `helper.js` in the above example would +be ignored) and it expects each one of those modules to export either a single handler method or an array of handler +methods. + +In the example above, the following routes would be registered: + +* `GET /users` +* `POST /users` +* `DELETE /users/:userId` +* `GET /users/:userId` +* `PUT /users/:userId` +* `GET /users/:userId/sessions` + #### Create your own Registrar In order to create your own registrar you simply need to extend the `Registrar` class and tell Routerify about it: @@ -291,7 +291,7 @@ The following options can be passed to Routerify: | `glob` | Any options to be passed to the `glob` module when searching for source files within `dir`. | `{}` | | `mounter` | The name of the `Mounter` to be used to mount the discovered routes on to the `server`. | `"express"` | | `paramPattern` | The regular expression to be used to match path parameter variables. | `/^_(.+)/` | -| `registrar` | The name of the `Registrar` used to load routes from source files in a given structure and then mount them via the `mounter`. | `"verb"` | +| `registrar` | The name of the `Registrar` used to load routes from source files in a given structure and then mount them via the `mounter`. | `"index"` | | `server` | The server object (e.g. `express()`) to which the routes are to be mounted. | N/A | | `verbs` | The verbs (corresponding to HTTP methods) to be supported. Defaults to those provided by the `mounter` if not specified. | `mounter.getDefaultValues()` | diff --git a/src/routerify.js b/src/routerify.js index d701f36..502fac6 100644 --- a/src/routerify.js +++ b/src/routerify.js @@ -58,7 +58,7 @@ function routerify(options) { glob: {}, mounter: 'express', paramPattern: /^_(.+)/, - registrar: 'verb', + registrar: 'index', server: null, verbs: null }) diff --git a/test/routerify.spec.js b/test/routerify.spec.js index 2b7a42b..3b04a26 100644 --- a/test/routerify.spec.js +++ b/test/routerify.spec.js @@ -98,7 +98,7 @@ describe('routerify', () => { beforeEach(() => { defaultMounter = routerify.lookup(Mounter, 'express') sinon.stub(defaultMounter, 'getDefaultVerbs') - defaultRegistrar = routerify.lookup(Registrar, 'verb') + defaultRegistrar = routerify.lookup(Registrar, 'index') sinon.stub(defaultRegistrar, 'register') }) @@ -121,7 +121,7 @@ describe('routerify', () => { glob: {}, mounter: 'express', paramPattern: /^_(.+)/, - registrar: 'verb', + registrar: 'index', server, verbs } From c134b2f292eec5bfba1bb9ef95b11d2173fd34cb Mon Sep 17 00:00:00 2001 From: Alasdair Mercer Date: Sun, 18 Dec 2016 02:31:36 +0000 Subject: [PATCH 09/12] bumped version to 0.2.0 --- .npmignore | 1 + CHANGES.md | 9 +++++++++ README.md | 2 +- package.json | 2 +- 4 files changed, 12 insertions(+), 2 deletions(-) create mode 100644 CHANGES.md diff --git a/.npmignore b/.npmignore index ad56a2c..8ac94cb 100644 --- a/.npmignore +++ b/.npmignore @@ -3,5 +3,6 @@ test/ .* *.log AUTHORS.md +CHANGES.md CONTRIBUTING.md README.md diff --git a/CHANGES.md b/CHANGES.md new file mode 100644 index 0000000..762f241 --- /dev/null +++ b/CHANGES.md @@ -0,0 +1,9 @@ +## Version 0.2.0, 2016.12.18 + +* Add unit tests [#3](https://github.com/Skelp/routerify/issues/3) +* Simplify configuration of mounters and registrars [#4](https://github.com/Skelp/routerify/issues/4), [#6](https://github.com/Skelp/routerify/issues/6) +* Change default value for `registrar` option to `"index"` [#7](https://github.com/Skelp/routerify/issues/7) + +## Version 0.1.0, 2016.12.15 + +* Initial release diff --git a/README.md b/README.md index 3863259..ff27b56 100644 --- a/README.md +++ b/README.md @@ -332,7 +332,7 @@ The current version of Routerify. ``` javascript routerify.version -=> "0.1.0" +=> "0.2.0" ``` ## Bugs diff --git a/package.json b/package.json index e8a4d12..62bdc48 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "routerify", - "version": "0.1.0", + "version": "0.2.0", "description": "Opinionated router loader for Express-like applications", "homepage": "https://github.com/Skelp/routerify", "bugs": { From d2d20dbb8d84f8e35ee606009e67044d62476e79 Mon Sep 17 00:00:00 2001 From: Alasdair Mercer Date: Tue, 16 May 2017 00:07:26 +0100 Subject: [PATCH 10/12] resolved #9 by moving from Skelp to !ninja --- .eslintrc.json | 2 +- .travis.yml | 10 +- AUTHORS.md | 2 +- CHANGES.md | 6 +- CONTRIBUTING.md | 10 +- LICENSE.md | 2 +- README.md | 42 ++-- package.json | 30 +-- src/mounter/express-mounter.js | 18 +- src/mounter/index.js | 16 +- src/mounter/restify-mounter.js | 18 +- src/plugin.js | 10 +- src/registrar/index-registrar.js | 24 +-- src/registrar/index.js | 24 +-- src/registrar/verb-registrar.js | 22 +- src/routerify.js | 62 +++--- src/utilities.js | 8 +- test/fixtures/registrar/index/ignore.js | 6 +- test/fixtures/registrar/index/index.js | 6 +- .../registrar/index/level1/_id/index.js | 6 +- .../index/level1/_id/level2/index.js | 6 +- test/fixtures/registrar/index/level1/index.js | 6 +- test/fixtures/registrar/verb/get.js | 6 +- test/fixtures/registrar/verb/head.js | 6 +- test/fixtures/registrar/verb/ignore.js | 6 +- .../fixtures/registrar/verb/level1/_id/del.js | 6 +- .../fixtures/registrar/verb/level1/_id/get.js | 6 +- .../registrar/verb/level1/_id/level2/get.js | 6 +- .../fixtures/registrar/verb/level1/_id/put.js | 6 +- test/fixtures/registrar/verb/level1/get.js | 6 +- test/fixtures/registrar/verb/level1/post.js | 6 +- test/mounter/express-mounter.spec.js | 72 +++---- test/mounter/index.spec.js | 36 ++-- test/mounter/restify-mounter.spec.js | 94 ++++---- test/plugin.spec.js | 24 +-- test/registrar/index-registrar.spec.js | 34 +-- test/registrar/index.spec.js | 54 ++--- test/registrar/registrar-test-case.js | 54 ++--- test/registrar/verb-registrar.spec.js | 34 +-- test/routerify.spec.js | 204 +++++++++--------- test/utilities.spec.js | 20 +- 41 files changed, 511 insertions(+), 505 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index c9807b8..5d5f81b 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,5 +1,5 @@ { - "extends": "skelp/v3/es6", + "extends": "notninja/es6", "env": { "node": true }, diff --git a/.travis.yml b/.travis.yml index 41f2138..35862fe 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,14 @@ +sudo: false language: node_js node_js: - "6" - "7" script: - - npm test + - npm run ci after_script: - - npm run coveralls + - npm run report-coverage +notifications: + slack: + rooms: + - secure: hiJNVw5FKod3veWv+h5PQby2G1n/xmFJZQlkr0YH3ISzLVIxYnOPzWlwfnK6F6BI4t8Dd76Q/n/dvxJdLI8O8B6neuE667MdVShctgcYUxtnR7ZYjJz4SKokmmGRS0Fl864Sxp/jBkNYezWljaDu35mnxIwY8ApKhmPboDxfHYHZ0hdYqNleRFN9rPNaisijpP48iAFcg+nksMkrdOB8Ritj29raPRTpmFVcwYJaEX/lpv8IhVErVHqe+RQrRIo2eWC6DxZKqTxksjZFNjcaDmRoIoXUG9sfCR7p+fMSVOXPnYqbQWO5QxOWTuoCrxsHHzkWzKbFOSTtsfq7+BVtIWR7PhoR8GJrl9a7P9+SoO/jf2rfPi4MiXXIoBqiNoGvH5IDrgvTX7ZkUecBxQAvUxmZIK8z+l/RX4oahTHuESZkAQ8CzhLlbLk1vAuojvq2Hm7Mz7wmSbQeV8qPrfQ575sssyRRndWwyrDVdhFdwyHRedLgFz4JsdFMWWVCkU3z6RyJ5nNv9Kd3MHhihHjBUsIDqk6z3ImbrLJSjEuKSzVOAcNDzrwwr6ed3OHPCNDAPuypULOATzDUH1gL5xmGFHRX3tRAQ4nRTERahojveMmFPtce32SaEXJb8Vt7pXyWoSaDH1XmYewmFd+Lhl+5z7btwY44yPIDuDcGbQqf/aQ= + on_success: change diff --git a/AUTHORS.md b/AUTHORS.md index 62f2a88..3849918 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -1,3 +1,3 @@ # Authors ordered by first contribution -* Alasdair Mercer +* Alasdair Mercer diff --git a/CHANGES.md b/CHANGES.md index 762f241..5395143 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,8 +1,8 @@ ## Version 0.2.0, 2016.12.18 -* Add unit tests [#3](https://github.com/Skelp/routerify/issues/3) -* Simplify configuration of mounters and registrars [#4](https://github.com/Skelp/routerify/issues/4), [#6](https://github.com/Skelp/routerify/issues/6) -* Change default value for `registrar` option to `"index"` [#7](https://github.com/Skelp/routerify/issues/7) +* Add unit tests [#3](https://github.com/NotNinja/routerify/issues/3) +* Simplify configuration of mounters and registrars [#4](https://github.com/NotNinja/routerify/issues/4), [#6](https://github.com/NotNinja/routerify/issues/6) +* Change default value for `registrar` option to `"index"` [#7](https://github.com/NotNinja/routerify/issues/7) ## Version 0.1.0, 2016.12.15 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1550e55..26ec0ed 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,9 +1,9 @@ # Contributing -If you have any questions about [Routerify](https://github.com/Skelp/routerify) please feel free to -[raise an issue](https://github.com/Skelp/routerify/issues/new). +If you have any questions about [Routerify](https://github.com/NotNinja/routerify) please feel free to +[raise an issue](https://github.com/NotNinja/routerify/issues/new). -Please [search existing issues](https://github.com/Skelp/routerify/issues) for the same feature and/or issue before +Please [search existing issues](https://github.com/NotNinja/routerify/issues) for the same feature and/or issue before raising a new issue. Commenting on an existing issue is usually preferred over raising duplicate issues. Please ensure that all files conform to the coding standards, using the same coding style as the rest of the code base. @@ -21,5 +21,5 @@ You must have at least [Node.js](https://nodejs.org) 6 or newer. All pull requests should be made to the `develop` branch. Don't forget to add your details to the list of -[AUTHORS.md](https://github.com/Skelp/routerify/blob/master/AUTHORS.md) if you want your contribution to be recognized -by others. +[AUTHORS.md](https://github.com/NotNinja/routerify/blob/master/AUTHORS.md) if you want your contribution to be +recognized by others. diff --git a/LICENSE.md b/LICENSE.md index 03779e7..6cc536a 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,4 +1,4 @@ -Copyright (C) 2016 Alasdair Mercer, Skelp +Copyright (C) 2017 Alasdair Mercer, !ninja Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index ff27b56..4340cc6 100644 --- a/README.md +++ b/README.md @@ -10,13 +10,13 @@ Y8b d88P "Y88P" -[Routerify](https://github.com/Skelp/routerify) is an opinionated router loader for Express-like applications. +[Routerify](https://github.com/NotNinja/routerify) is an opinionated router loader for Express-like applications. -[![Build](https://img.shields.io/travis/Skelp/routerify/develop.svg?style=flat-square)](https://travis-ci.org/Skelp/routerify) -[![Coverage](https://img.shields.io/coveralls/Skelp/routerify/develop.svg?style=flat-square)](https://coveralls.io/github/Skelp/routerify) -[![Dependencies](https://img.shields.io/david/Skelp/routerify.svg?style=flat-square)](https://david-dm.org/Skelp/routerify) -[![Dev Dependencies](https://img.shields.io/david/dev/Skelp/routerify.svg?style=flat-square)](https://david-dm.org/Skelp/routerify#info=devDependencies) -[![License](https://img.shields.io/npm/l/routerify.svg?style=flat-square)](https://github.com/Skelp/routerify/blob/master/LICENSE.md) +[![Build](https://img.shields.io/travis/NotNinja/routerify/develop.svg?style=flat-square)](https://travis-ci.org/NotNinja/routerify) +[![Coverage](https://img.shields.io/coveralls/NotNinja/routerify/develop.svg?style=flat-square)](https://coveralls.io/github/NotNinja/routerify) +[![Dependencies](https://img.shields.io/david/NotNinja/routerify.svg?style=flat-square)](https://david-dm.org/NotNinja/routerify) +[![Dev Dependencies](https://img.shields.io/david/dev/NotNinja/routerify.svg?style=flat-square)](https://david-dm.org/NotNinja/routerify?type=dev) +[![License](https://img.shields.io/npm/l/routerify.svg?style=flat-square)](https://github.com/NotNinja/routerify/blob/master/LICENSE.md) [![Release](https://img.shields.io/npm/v/routerify.svg?style=flat-square)](https://www.npmjs.com/package/routerify) * [Install](#install) @@ -39,7 +39,7 @@ You'll need to have at least [Node.js](https://nodejs.org) 6 or newer. Routerify is opinionated. Routerify is also configurable and extensible via plugins. The two core plugin types that Routerify has are *Registrars* and *Mounters*. Through these, Routerify can be configured to load routes from modules in any pattern and mount them onto any server. - + ### Registrars Registrars are responsible for picking which source files within the directory should be loaded as modules and how the @@ -47,7 +47,7 @@ routes are extracted from the modules. These routes are then passed to the mount Routerify includes some opinionated registrars. If you don't like our opinion, you can either not use Routerify **or** you can simply [create your own](#create-your-own-registrar) and, optionally, -[create a pull request](https://github.com/Skelp/routerify/compare) to share it with everyone, provided our opinions +[create a pull request](https://github.com/NotNinja/routerify/compare) to share it with everyone, provided our opinions match :) If not, you can always release it as a plugin. #### `IndexRegistrar` @@ -126,7 +126,7 @@ It expects the following file structure under the target directory: Only files whose base name matches that of a supported verb will be loaded (e.g. `helper.js` in the above example would be ignored) and it expects each one of those modules to export either a single handler method or an array of handler -methods. +methods. In the example above, the following routes would be registered: @@ -150,7 +150,7 @@ class CustomRegistrar extends Registrar { getPluginName() { return 'custom' } - + register(file, options) { // Load the module, extract the routes, and mount them via this.mounter ... @@ -173,7 +173,8 @@ routerify({ ``` You probably want to take a look at the relevant -[source code](https://github.com/Skelp/routerify/tree/master/src/registrar) before trying to create your own registrar. +[source code](https://github.com/NotNinja/routerify/tree/master/src/registrar) before trying to create your own +registrar. ### Mounters @@ -183,7 +184,7 @@ option is specified, and they determine how parameter path variables are formatt Routerify includes some mounters for common server frameworks. If your favorite framework is not supported, you can [create your own](#create-your-own-mounter) and, optionally, -[create a pull request](https://github.com/Skelp/routerify/compare) to share it with everyone. +[create a pull request](https://github.com/NotNinja/routerify/compare) to share it with everyone. #### `ExpressMounter` @@ -205,7 +206,7 @@ benefits for Restify applications by allowing extra optional information (e.g. ` mounting routes. This can be done by simply adding an `options` property containing the additional information to one of the route -handlers and it will be passed in. +handlers and it will be passed in. #### Create your own Mounter @@ -216,7 +217,7 @@ const routerify = require('routerify') const Mounter = require('routerify/src/mounter') class CustomMounter extends Mounter { - + formatParamPath(param) { // Format param for insertion into the route URL return ... @@ -253,7 +254,7 @@ routerify({ ``` You probably want to take a look at the relevant -[source code](https://github.com/Skelp/routerify/tree/master/src/mounter) before trying to create your own mounter. +[source code](https://github.com/NotNinja/routerify/tree/master/src/mounter) before trying to create your own mounter. ## API @@ -338,20 +339,19 @@ routerify.version ## Bugs If you have any problems with Routerify or would like to see changes currently in development you can do so -[here](https://github.com/Skelp/routerify/issues). +[here](https://github.com/NotNinja/routerify/issues). ## Contributors If you want to contribute, you're a legend! Information on how you can do so can be found in -[CONTRIBUTING.md](https://github.com/Skelp/routerify/blob/master/CONTRIBUTING.md). We want your suggestions and pull +[CONTRIBUTING.md](https://github.com/NotNinja/routerify/blob/master/CONTRIBUTING.md). We want your suggestions and pull requests! A list of Routerify contributors can be found in -[AUTHORS.md](https://github.com/Skelp/routerify/blob/master/AUTHORS.md). +[AUTHORS.md](https://github.com/NotNinja/routerify/blob/master/AUTHORS.md). ## License -See [LICENSE.md](https://github.com/Skelp/routerify/raw/master/LICENSE.md) for more information on our MIT license. +See [LICENSE.md](https://github.com/NotNinja/routerify/raw/master/LICENSE.md) for more information on our MIT license. -© 2016 [Skelp](https://skelp.io) - +[![Copyright !ninja](https://cdn.rawgit.com/NotNinja/branding/master/assets/copyright/base/not-ninja-copyright-186x25.png)](https://not.ninja) diff --git a/package.json b/package.json index 62bdc48..81a7eec 100644 --- a/package.json +++ b/package.json @@ -2,14 +2,14 @@ "name": "routerify", "version": "0.2.0", "description": "Opinionated router loader for Express-like applications", - "homepage": "https://github.com/Skelp/routerify", + "homepage": "https://github.com/NotNinja/routerify", "bugs": { - "url": "https://github.com/Skelp/routerify/issues" + "url": "https://github.com/NotNinja/routerify/issues" }, "author": { "name": "Alasdair Mercer", - "email": "alasdair@skelp.io", - "url": "https://skelp.io" + "email": "mercer.alasdair@gmail.com", + "url": "https://not.ninja" }, "license": "MIT", "keywords": [ @@ -19,7 +19,7 @@ ], "repository": { "type": "git", - "url": "https://github.com/Skelp/routerify.git" + "url": "https://github.com/NotNinja/routerify.git" }, "dependencies": { "glob": "^7.1.1", @@ -27,20 +27,20 @@ }, "devDependencies": { "chai": "^3.5.0", - "coveralls": "^2.11.15", - "eslint": "^3.12.1", - "eslint-config-skelp": "^0.1.5", + "coveralls": "^2.13.1", + "eslint": "^3.19.0", + "eslint-config-notninja": "^0.1.1", "istanbul": "^0.4.5", - "mocha": "^3.2.0", - "sinon": "^1.17.6" + "mocha": "^3.4.1", + "sinon": "^2.2.0" }, "main": "src/routerify.js", "scripts": { - "coveralls": "istanbul cover _mocha --report lcovonly -- -R spec \"test/**/*.spec.js\" && coveralls < coverage/lcov.info", - "test": "npm run test-lint && npm run test-suite && npm run test-coverage", - "test-coverage": "istanbul check-coverage", - "test-lint": "eslint \"src/**/*.js\" \"test/**/*.js\"", - "test-suite": "istanbul cover _mocha -- -R spec \"test/**/*.spec.js\"" + "ci": "npm run test", + "report-coverage": "istanbul cover _mocha --report lcovonly -- -R spec \"test/**/*.spec.js\" && coveralls < coverage/lcov.info", + "pretest": "eslint \"src/**/*.js\" \"test/**/*.js\"", + "test": "istanbul cover _mocha -- -R list \"test/**/*.spec.js\"", + "posttest": "istanbul check-coverage" }, "engines": { "node": ">=6" diff --git a/src/mounter/express-mounter.js b/src/mounter/express-mounter.js index a7fed9d..1b5b528 100644 --- a/src/mounter/express-mounter.js +++ b/src/mounter/express-mounter.js @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 Alasdair Mercer, Skelp + * Copyright (C) 2017 Alasdair Mercer, !ninja * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -20,9 +20,9 @@ * SOFTWARE. */ -'use strict' +'use strict'; -const Mounter = require('./index') +const Mounter = require('./index'); /** * An implementation of {@link Mounter} that is intended to be used for Express applications. @@ -37,7 +37,7 @@ class ExpressMounter extends Mounter { * @inheritDoc */ formatParamPath(param) { - return `:${param}` + return `:${param}`; } /** @@ -45,7 +45,7 @@ class ExpressMounter extends Mounter { * @inheritDoc */ getDefaultVerbs() { - return [ 'del', 'get', 'head', 'opts', 'patch', 'post', 'put' ] + return [ 'del', 'get', 'head', 'opts', 'patch', 'post', 'put' ]; } /** @@ -53,7 +53,7 @@ class ExpressMounter extends Mounter { * @inheritDoc */ getPluginName() { - return 'express' + return 'express'; } /** @@ -61,11 +61,11 @@ class ExpressMounter extends Mounter { * @inheritDoc */ mount(url, verb, handlers, options) { - handlers = Array.isArray(handlers) ? handlers : [ handlers ] + handlers = Array.isArray(handlers) ? handlers : [ handlers ]; - options.server[verb](url, ...handlers) + options.server[verb](url, ...handlers); } } -module.exports = ExpressMounter +module.exports = ExpressMounter; diff --git a/src/mounter/index.js b/src/mounter/index.js index 936bf68..9840be0 100644 --- a/src/mounter/index.js +++ b/src/mounter/index.js @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 Alasdair Mercer, Skelp + * Copyright (C) 2017 Alasdair Mercer, !ninja * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -20,10 +20,10 @@ * SOFTWARE. */ -'use strict' +'use strict'; -const Plugin = require('../plugin') -const Utilities = require('../utilities') +const Plugin = require('../plugin'); +const Utilities = require('../utilities'); /** * Responsible for mounting a route, whose information is provided by a {@link Registrar}, onto the server. @@ -42,7 +42,7 @@ class Mounter extends Plugin { * @abstract */ formatParamPath(param) { - Utilities.abstracted(Mounter, 'formatParamPath') + Utilities.abstracted(Mounter, 'formatParamPath'); } /** @@ -53,7 +53,7 @@ class Mounter extends Plugin { * @abstract */ getDefaultVerbs() { - Utilities.abstracted(Mounter, 'getDefaultVerbs') + Utilities.abstracted(Mounter, 'getDefaultVerbs'); } /** @@ -68,9 +68,9 @@ class Mounter extends Plugin { * @abstract */ mount(url, verb, handlers, options) { - Utilities.abstracted(Mounter, 'mount') + Utilities.abstracted(Mounter, 'mount'); } } -module.exports = Mounter +module.exports = Mounter; diff --git a/src/mounter/restify-mounter.js b/src/mounter/restify-mounter.js index 9ecad8a..36bbdaa 100644 --- a/src/mounter/restify-mounter.js +++ b/src/mounter/restify-mounter.js @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 Alasdair Mercer, Skelp + * Copyright (C) 2017 Alasdair Mercer, !ninja * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -20,9 +20,9 @@ * SOFTWARE. */ -'use strict' +'use strict'; -const ExpressMounter = require('./express-mounter') +const ExpressMounter = require('./express-mounter'); /** * An extension of {@link ExpressMounter} which provides further compatibility with Restify by reading @@ -42,7 +42,7 @@ class RestifyMounter extends ExpressMounter { * @inheritDoc */ getPluginName() { - return 'restify' + return 'restify'; } /** @@ -50,19 +50,19 @@ class RestifyMounter extends ExpressMounter { * @inheritDoc */ mount(url, verb, handlers, options) { - handlers = Array.isArray(handlers) ? handlers : [ handlers ] + handlers = Array.isArray(handlers) ? handlers : [ handlers ]; - const handlerWithOptions = handlers.find((handler) => handler.options != null) + const handlerWithOptions = handlers.find((handler) => handler.options != null); if (handlerWithOptions) { url = Object.assign({}, handlerWithOptions.options, { method: verb, path: url - }) + }); } - super.mount(url, verb, handlers, options) + super.mount(url, verb, handlers, options); } } -module.exports = RestifyMounter +module.exports = RestifyMounter; diff --git a/src/plugin.js b/src/plugin.js index 9d73dee..b3c9d52 100644 --- a/src/plugin.js +++ b/src/plugin.js @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 Alasdair Mercer, Skelp + * Copyright (C) 2017 Alasdair Mercer, !ninja * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -20,9 +20,9 @@ * SOFTWARE. */ -'use strict' +'use strict'; -const Utilities = require('./utilities') +const Utilities = require('./utilities'); /** * A plugin can be registered to be used and then looked up later based on the plugin name. @@ -41,9 +41,9 @@ class Plugin { * @abstract */ getPluginName() { - Utilities.abstracted(Plugin, 'getPluginName') + Utilities.abstracted(Plugin, 'getPluginName'); } } -module.exports = Plugin +module.exports = Plugin; diff --git a/src/registrar/index-registrar.js b/src/registrar/index-registrar.js index 13bd021..570d783 100644 --- a/src/registrar/index-registrar.js +++ b/src/registrar/index-registrar.js @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 Alasdair Mercer, Skelp + * Copyright (C) 2017 Alasdair Mercer, !ninja * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -20,11 +20,11 @@ * SOFTWARE. */ -'use strict' +'use strict'; -const path = require('path') +const path = require('path'); -const Registrar = require('./index') +const Registrar = require('./index'); /** * An implementation of {@link Registrar} where the index files in each directory is loaded as a module and the result @@ -41,7 +41,7 @@ class IndexRegistrar extends Registrar { * @override */ getPluginName() { - return 'index' + return 'index'; } /** @@ -49,21 +49,21 @@ class IndexRegistrar extends Registrar { * @override */ register(file, options) { - const name = path.basename(file, options.ext) + const name = path.basename(file, options.ext); if (name !== 'index') { - return + return; } - const router = this.loadRouter(file, options) - const url = this.buildUrl(file, options) + const router = this.loadRouter(file, options); + const url = this.buildUrl(file, options); Object.keys(router) .filter(options.verbs.includes.bind(options.verbs)) .forEach((verb) => { - options.mounter.mount(url, verb, router[verb], options) - }) + options.mounter.mount(url, verb, router[verb], options); + }); } } -module.exports = IndexRegistrar +module.exports = IndexRegistrar; diff --git a/src/registrar/index.js b/src/registrar/index.js index 186fb0c..2f854e8 100644 --- a/src/registrar/index.js +++ b/src/registrar/index.js @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 Alasdair Mercer, Skelp + * Copyright (C) 2017 Alasdair Mercer, !ninja * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -20,12 +20,12 @@ * SOFTWARE. */ -'use strict' +'use strict'; -const path = require('path') +const path = require('path'); -const Plugin = require('../plugin') -const Utilities = require('../utilities') +const Plugin = require('../plugin'); +const Utilities = require('../utilities'); /** * Responsible for loading routes from a specific structure of modules and mounting them onto the server using a @@ -54,13 +54,13 @@ class Registrar extends Plugin { return file.split(path.sep) .slice(0, -1) .reduce((memo, segment) => { - const match = segment.match(options.paramPattern) + const match = segment.match(options.paramPattern); if (match) { - segment = options.mounter.formatParamPath(match[1]) + segment = options.mounter.formatParamPath(match[1]); } - return `${memo}/${segment}` - }, '') + return `${memo}/${segment}`; + }, ''); } /** @@ -72,7 +72,7 @@ class Registrar extends Plugin { * @protected */ loadRouter(file, options) { - return require(path.resolve(options.dir, file)) + return require(path.resolve(options.dir, file)); } /** @@ -85,9 +85,9 @@ class Registrar extends Plugin { * @abstract */ register(file, options) { - Utilities.abstracted(Registrar, 'register') + Utilities.abstracted(Registrar, 'register'); } } -module.exports = Registrar +module.exports = Registrar; diff --git a/src/registrar/verb-registrar.js b/src/registrar/verb-registrar.js index 80dc305..fa5c3e0 100644 --- a/src/registrar/verb-registrar.js +++ b/src/registrar/verb-registrar.js @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 Alasdair Mercer, Skelp + * Copyright (C) 2017 Alasdair Mercer, !ninja * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -20,11 +20,11 @@ * SOFTWARE. */ -'use strict' +'use strict'; -const path = require('path') +const path = require('path'); -const Registrar = require('./index') +const Registrar = require('./index'); /** * An implementation of {@link Registrar} where the name of the files in each directory is checked and, if it's a @@ -41,7 +41,7 @@ class VerbRegistrar extends Registrar { * @override */ getPluginName() { - return 'verb' + return 'verb'; } /** @@ -49,17 +49,17 @@ class VerbRegistrar extends Registrar { * @override */ register(file, options) { - const name = path.basename(file, options.ext) + const name = path.basename(file, options.ext); if (!options.verbs.includes(name)) { - return + return; } - const handlers = this.loadRouter(file, options) - const url = this.buildUrl(file, options) + const handlers = this.loadRouter(file, options); + const url = this.buildUrl(file, options); - options.mounter.mount(url, name, handlers, options) + options.mounter.mount(url, name, handlers, options); } } -module.exports = VerbRegistrar +module.exports = VerbRegistrar; diff --git a/src/routerify.js b/src/routerify.js index 502fac6..2e49a86 100644 --- a/src/routerify.js +++ b/src/routerify.js @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 Alasdair Mercer, Skelp + * Copyright (C) 2017 Alasdair Mercer, !ninja * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -20,18 +20,18 @@ * SOFTWARE. */ -'use strict' +'use strict'; -const defaultsDeep = require('lodash.defaultsdeep') -const glob = require('glob') +const defaultsDeep = require('lodash.defaultsdeep'); +const glob = require('glob'); -const ExpressMounter = require('./mounter/express-mounter') -const IndexRegistrar = require('./registrar/index-registrar') -const Mounter = require('./mounter') -const { version } = require('../package.json') -const Registrar = require('./registrar') -const RestifyMounter = require('./mounter/restify-mounter') -const VerbRegistrar = require('./registrar/verb-registrar') +const ExpressMounter = require('./mounter/express-mounter'); +const IndexRegistrar = require('./registrar/index-registrar'); +const Mounter = require('./mounter'); +const { version } = require('../package.json'); +const Registrar = require('./registrar'); +const RestifyMounter = require('./mounter/restify-mounter'); +const VerbRegistrar = require('./registrar/verb-registrar'); /** * A set containing all registered {@link Plugin} instances. @@ -39,7 +39,7 @@ const VerbRegistrar = require('./registrar/verb-registrar') * @private * @type {Set.} */ -const plugins = new Set() +const plugins = new Set(); /** * Mounts routes onto a given server by loading modules within a specific directory. @@ -61,19 +61,19 @@ function routerify(options) { registrar: 'index', server: null, verbs: null - }) + }); - const mounter = options.mounter = routerify.lookup(Mounter, options.mounter) - const registrar = options.registrar = routerify.lookup(Registrar, options.registrar) + const mounter = options.mounter = routerify.lookup(Mounter, options.mounter); + const registrar = options.registrar = routerify.lookup(Registrar, options.registrar); if (!options.verbs) { - options.verbs = mounter.getDefaultVerbs() + options.verbs = mounter.getDefaultVerbs(); } - const globOptions = Object.assign({ cwd: options.dir }, options.glob) - const files = glob.sync(`**/*${options.ext}`, globOptions) + const globOptions = Object.assign({ cwd: options.dir }, options.glob); + const files = glob.sync(`**/*${options.ext}`, globOptions); - files.forEach((file) => registrar.register(file, options)) + files.forEach((file) => registrar.register(file, options)); } Object.assign(routerify, { @@ -97,16 +97,16 @@ Object.assign(routerify, { lookup(type, name) { if (typeof name === 'undefined') { return Array.from(plugins) - .filter((plugin) => plugin instanceof type) + .filter((plugin) => plugin instanceof type); } const match = Array.from(plugins) - .find((plugin) => plugin instanceof type && plugin.getPluginName() === name) + .find((plugin) => plugin instanceof type && plugin.getPluginName() === name); if (match == null) { - throw new Error(`Unable to lookup ${type.name}: ${name}`) + throw new Error(`Unable to lookup ${type.name}: ${name}`); } - return match + return match; }, /** @@ -121,10 +121,10 @@ Object.assign(routerify, { */ use(plugin) { if (plugin) { - plugins.add(plugin) + plugins.add(plugin); } - return plugin + return plugin; }, /** @@ -136,14 +136,14 @@ Object.assign(routerify, { */ version -}) +}); -routerify.use(new ExpressMounter()) -routerify.use(new RestifyMounter()) -routerify.use(new IndexRegistrar()) -routerify.use(new VerbRegistrar()) +routerify.use(new ExpressMounter()); +routerify.use(new RestifyMounter()); +routerify.use(new IndexRegistrar()); +routerify.use(new VerbRegistrar()); -module.exports = routerify +module.exports = routerify; /** * The options that can be passed to routerify. diff --git a/src/utilities.js b/src/utilities.js index 3cfb4f1..2e0a899 100644 --- a/src/utilities.js +++ b/src/utilities.js @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 Alasdair Mercer, Skelp + * Copyright (C) 2017 Alasdair Mercer, !ninja * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -20,7 +20,7 @@ * SOFTWARE. */ -'use strict' +'use strict'; /** * Contains utility methods. @@ -41,9 +41,9 @@ class Utilities { * @static */ static abstracted(type, methodName) { - throw new Error(`${type.name}#${methodName} has not been implemented`) + throw new Error(`${type.name}#${methodName} has not been implemented`); } } -module.exports = Utilities +module.exports = Utilities; diff --git a/test/fixtures/registrar/index/ignore.js b/test/fixtures/registrar/index/ignore.js index 09f57a9..283bede 100644 --- a/test/fixtures/registrar/index/ignore.js +++ b/test/fixtures/registrar/index/ignore.js @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 Alasdair Mercer, Skelp + * Copyright (C) 2017 Alasdair Mercer, !ninja * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -20,8 +20,8 @@ * SOFTWARE. */ -'use strict' +'use strict'; function ignorePost(req, res, next) {} -module.exports = { post: ignorePost } +module.exports = { post: ignorePost }; diff --git a/test/fixtures/registrar/index/index.js b/test/fixtures/registrar/index/index.js index 60ea5bc..c04f5cb 100644 --- a/test/fixtures/registrar/index/index.js +++ b/test/fixtures/registrar/index/index.js @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 Alasdair Mercer, Skelp + * Copyright (C) 2017 Alasdair Mercer, !ninja * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -20,7 +20,7 @@ * SOFTWARE. */ -'use strict' +'use strict'; function rootGet(req, res, next) {} function rootHead1(req, res, next) {} @@ -31,4 +31,4 @@ module.exports = { get: rootGet, head: [ rootHead1, rootHead2 ] -} +}; diff --git a/test/fixtures/registrar/index/level1/_id/index.js b/test/fixtures/registrar/index/level1/_id/index.js index 29796e1..3973da0 100644 --- a/test/fixtures/registrar/index/level1/_id/index.js +++ b/test/fixtures/registrar/index/level1/_id/index.js @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 Alasdair Mercer, Skelp + * Copyright (C) 2017 Alasdair Mercer, !ninja * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -20,7 +20,7 @@ * SOFTWARE. */ -'use strict' +'use strict'; function level1IdDel(req, res, next) {} function level1IdGet(req, res, next) {} @@ -32,4 +32,4 @@ module.exports = { get: level1IdGet, post: level1IdPost -} +}; diff --git a/test/fixtures/registrar/index/level1/_id/level2/index.js b/test/fixtures/registrar/index/level1/_id/level2/index.js index 26fbac2..ce20d87 100644 --- a/test/fixtures/registrar/index/level1/_id/level2/index.js +++ b/test/fixtures/registrar/index/level1/_id/level2/index.js @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 Alasdair Mercer, Skelp + * Copyright (C) 2017 Alasdair Mercer, !ninja * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -20,8 +20,8 @@ * SOFTWARE. */ -'use strict' +'use strict'; function level1IdLevel2Get(req, res, next) {} -module.exports = { get: level1IdLevel2Get } +module.exports = { get: level1IdLevel2Get }; diff --git a/test/fixtures/registrar/index/level1/index.js b/test/fixtures/registrar/index/level1/index.js index 9067f9e..4c3625c 100644 --- a/test/fixtures/registrar/index/level1/index.js +++ b/test/fixtures/registrar/index/level1/index.js @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 Alasdair Mercer, Skelp + * Copyright (C) 2017 Alasdair Mercer, !ninja * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -20,7 +20,7 @@ * SOFTWARE. */ -'use strict' +'use strict'; function level1Get(req, res, next) {} function level1Post(req, res, next) {} @@ -30,4 +30,4 @@ module.exports = { get: level1Get, post: level1Post -} +}; diff --git a/test/fixtures/registrar/verb/get.js b/test/fixtures/registrar/verb/get.js index 6c293fe..4a65038 100644 --- a/test/fixtures/registrar/verb/get.js +++ b/test/fixtures/registrar/verb/get.js @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 Alasdair Mercer, Skelp + * Copyright (C) 2017 Alasdair Mercer, !ninja * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -20,8 +20,8 @@ * SOFTWARE. */ -'use strict' +'use strict'; function rootGet(req, res, next) {} -module.exports = rootGet +module.exports = rootGet; diff --git a/test/fixtures/registrar/verb/head.js b/test/fixtures/registrar/verb/head.js index 25c8c6c..8e39091 100644 --- a/test/fixtures/registrar/verb/head.js +++ b/test/fixtures/registrar/verb/head.js @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 Alasdair Mercer, Skelp + * Copyright (C) 2017 Alasdair Mercer, !ninja * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -20,9 +20,9 @@ * SOFTWARE. */ -'use strict' +'use strict'; function rootHead1(req, res, next) {} function rootHead2(req, res, next) {} -module.exports = [ rootHead1, rootHead2 ] +module.exports = [ rootHead1, rootHead2 ]; diff --git a/test/fixtures/registrar/verb/ignore.js b/test/fixtures/registrar/verb/ignore.js index a09aa05..ab9bb4c 100644 --- a/test/fixtures/registrar/verb/ignore.js +++ b/test/fixtures/registrar/verb/ignore.js @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 Alasdair Mercer, Skelp + * Copyright (C) 2017 Alasdair Mercer, !ninja * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -20,8 +20,8 @@ * SOFTWARE. */ -'use strict' +'use strict'; function ignore(req, res, next) {} -module.exports = ignore +module.exports = ignore; diff --git a/test/fixtures/registrar/verb/level1/_id/del.js b/test/fixtures/registrar/verb/level1/_id/del.js index 4629a55..d0162b5 100644 --- a/test/fixtures/registrar/verb/level1/_id/del.js +++ b/test/fixtures/registrar/verb/level1/_id/del.js @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 Alasdair Mercer, Skelp + * Copyright (C) 2017 Alasdair Mercer, !ninja * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -20,8 +20,8 @@ * SOFTWARE. */ -'use strict' +'use strict'; function level1IdDel(req, res, next) {} -module.exports = level1IdDel +module.exports = level1IdDel; diff --git a/test/fixtures/registrar/verb/level1/_id/get.js b/test/fixtures/registrar/verb/level1/_id/get.js index 1c7ba44..f632bcb 100644 --- a/test/fixtures/registrar/verb/level1/_id/get.js +++ b/test/fixtures/registrar/verb/level1/_id/get.js @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 Alasdair Mercer, Skelp + * Copyright (C) 2017 Alasdair Mercer, !ninja * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -20,8 +20,8 @@ * SOFTWARE. */ -'use strict' +'use strict'; function level1IdGet(req, res, next) {} -module.exports = level1IdGet +module.exports = level1IdGet; diff --git a/test/fixtures/registrar/verb/level1/_id/level2/get.js b/test/fixtures/registrar/verb/level1/_id/level2/get.js index 9be6e3d..c96d06f 100644 --- a/test/fixtures/registrar/verb/level1/_id/level2/get.js +++ b/test/fixtures/registrar/verb/level1/_id/level2/get.js @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 Alasdair Mercer, Skelp + * Copyright (C) 2017 Alasdair Mercer, !ninja * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -20,8 +20,8 @@ * SOFTWARE. */ -'use strict' +'use strict'; function level1IdLevel2Get(req, res, next) {} -module.exports = level1IdLevel2Get +module.exports = level1IdLevel2Get; diff --git a/test/fixtures/registrar/verb/level1/_id/put.js b/test/fixtures/registrar/verb/level1/_id/put.js index f17da57..8792fed 100644 --- a/test/fixtures/registrar/verb/level1/_id/put.js +++ b/test/fixtures/registrar/verb/level1/_id/put.js @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 Alasdair Mercer, Skelp + * Copyright (C) 2017 Alasdair Mercer, !ninja * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -20,8 +20,8 @@ * SOFTWARE. */ -'use strict' +'use strict'; function level1IdPut(req, res, next) {} -module.exports = level1IdPut +module.exports = level1IdPut; diff --git a/test/fixtures/registrar/verb/level1/get.js b/test/fixtures/registrar/verb/level1/get.js index 5f4540c..bea4b56 100644 --- a/test/fixtures/registrar/verb/level1/get.js +++ b/test/fixtures/registrar/verb/level1/get.js @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 Alasdair Mercer, Skelp + * Copyright (C) 2017 Alasdair Mercer, !ninja * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -20,8 +20,8 @@ * SOFTWARE. */ -'use strict' +'use strict'; function level1Get(req, res, next) {} -module.exports = level1Get +module.exports = level1Get; diff --git a/test/fixtures/registrar/verb/level1/post.js b/test/fixtures/registrar/verb/level1/post.js index 01a0ee1..a3f9eba 100644 --- a/test/fixtures/registrar/verb/level1/post.js +++ b/test/fixtures/registrar/verb/level1/post.js @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 Alasdair Mercer, Skelp + * Copyright (C) 2017 Alasdair Mercer, !ninja * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -20,8 +20,8 @@ * SOFTWARE. */ -'use strict' +'use strict'; function level1Post(req, res, next) {} -module.exports = level1Post +module.exports = level1Post; diff --git a/test/mounter/express-mounter.spec.js b/test/mounter/express-mounter.spec.js index ab2347d..623a0ac 100644 --- a/test/mounter/express-mounter.spec.js +++ b/test/mounter/express-mounter.spec.js @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 Alasdair Mercer, Skelp + * Copyright (C) 2017 Alasdair Mercer, !ninja * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -20,65 +20,65 @@ * SOFTWARE. */ -'use strict' +'use strict'; -const { expect } = require('chai') -const sinon = require('sinon') +const { expect } = require('chai'); +const sinon = require('sinon'); -const ExpressMounter = require('../../src/mounter/express-mounter') +const ExpressMounter = require('../../src/mounter/express-mounter'); describe('mounter/express-mounter', () => { describe('ExpressMounter.prototype', () => { - let mounter + let mounter; beforeEach(() => { - mounter = new ExpressMounter() - }) + mounter = new ExpressMounter(); + }); describe('.formatParamPath', () => { it('should prefix the parameter with a colon', () => { - expect(mounter.formatParamPath('foo')).to.equal(':foo') - }) - }) + expect(mounter.formatParamPath('foo')).to.equal(':foo'); + }); + }); describe('.getDefaultVerbs', () => { it('should contain all verbs supported by Express', () => { - expect(mounter.getDefaultVerbs()).to.eql([ 'del', 'get', 'head', 'opts', 'patch', 'post', 'put' ]) - }) - }) + expect(mounter.getDefaultVerbs()).to.eql([ 'del', 'get', 'head', 'opts', 'patch', 'post', 'put' ]); + }); + }); describe('.getPluginName', () => { it('should return correct name', () => { - expect(mounter.getPluginName()).to.equal('express') - }) - }) + expect(mounter.getPluginName()).to.equal('express'); + }); + }); describe('.mount', () => { context('when a single handler is provided', () => { it('should mount the handler onto the server', () => { - const url = '/foo' - const handler = function a() {} - const server = sinon.stub({ get() {} }) + const url = '/foo'; + const handler = function handler() {}; + const server = sinon.stub({ get() {} }); - mounter.mount(url, 'get', handler, { server }) + mounter.mount(url, 'get', handler, { server }); - expect(server.get.calledOnce).to.be.true - expect(server.get.args[0]).to.eql([ url, handler ]) - }) - }) + expect(server.get.calledOnce).to.be.true; + expect(server.get.args[0]).to.eql([ url, handler ]); + }); + }); context('when multiple handlers are provided in an array', () => { it('should mount all handlers onto the server', () => { - const url = '/foo' - const handlers = [ function a() {}, function b() {} ] - const server = sinon.stub({ get() {} }) + const url = '/foo'; + const handlers = [ function a() {}, function b() {} ]; + const server = sinon.stub({ get() {} }); - mounter.mount(url, 'get', handlers, { server }) + mounter.mount(url, 'get', handlers, { server }); - expect(server.get.calledOnce).to.be.true - expect(server.get.args[0]).to.eql([ url ].concat(handlers)) - }) - }) - }) - }) -}) + expect(server.get.calledOnce).to.be.true; + expect(server.get.args[0]).to.eql([ url ].concat(handlers)); + }); + }); + }); + }); +}); diff --git a/test/mounter/index.spec.js b/test/mounter/index.spec.js index 9182002..c5d0c9b 100644 --- a/test/mounter/index.spec.js +++ b/test/mounter/index.spec.js @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 Alasdair Mercer, Skelp + * Copyright (C) 2017 Alasdair Mercer, !ninja * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -20,36 +20,36 @@ * SOFTWARE. */ -'use strict' +'use strict'; -const { expect } = require('chai') +const { expect } = require('chai'); -const Mounter = require('../../src/mounter') +const Mounter = require('../../src/mounter'); describe('mounter/index', () => { describe('Mounter.prototype', () => { - let mounter + let mounter; beforeEach(() => { - mounter = new Mounter() - }) + mounter = new Mounter(); + }); describe('.formatParamPath', () => { it('should be abstract', () => { - expect(mounter.formatParamPath.bind(mounter)).to.throw('Mounter#formatParamPath has not been implemented') - }) - }) + expect(mounter.formatParamPath.bind(mounter)).to.throw('Mounter#formatParamPath has not been implemented'); + }); + }); describe('.getDefaultVerbs', () => { it('should be abstract', () => { - expect(mounter.getDefaultVerbs.bind(mounter)).to.throw('Mounter#getDefaultVerbs has not been implemented') - }) - }) + expect(mounter.getDefaultVerbs.bind(mounter)).to.throw('Mounter#getDefaultVerbs has not been implemented'); + }); + }); describe('.mount', () => { it('should be abstract', () => { - expect(mounter.mount.bind(mounter)).to.throw('Mounter#mount has not been implemented') - }) - }) - }) -}) + expect(mounter.mount.bind(mounter)).to.throw('Mounter#mount has not been implemented'); + }); + }); + }); +}); diff --git a/test/mounter/restify-mounter.spec.js b/test/mounter/restify-mounter.spec.js index 6054145..619896f 100644 --- a/test/mounter/restify-mounter.spec.js +++ b/test/mounter/restify-mounter.spec.js @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 Alasdair Mercer, Skelp + * Copyright (C) 2017 Alasdair Mercer, !ninja * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -20,86 +20,86 @@ * SOFTWARE. */ -'use strict' +'use strict'; -const { expect } = require('chai') -const sinon = require('sinon') +const { expect } = require('chai'); +const sinon = require('sinon'); -const RestifyMounter = require('../../src/mounter/restify-mounter') +const RestifyMounter = require('../../src/mounter/restify-mounter'); describe('mounter/restify-mounter', () => { describe('RestifyMounter.prototype', () => { - let mounter + let mounter; beforeEach(() => { - mounter = new RestifyMounter() - }) + mounter = new RestifyMounter(); + }); describe('.formatParamPath', () => { it('should prefix the parameter with a colon', () => { - expect(mounter.formatParamPath('foo')).to.equal(':foo') - }) - }) + expect(mounter.formatParamPath('foo')).to.equal(':foo'); + }); + }); describe('.getDefaultVerbs', () => { it('should contain all verbs supported by Restify', () => { - expect(mounter.getDefaultVerbs()).to.eql([ 'del', 'get', 'head', 'opts', 'patch', 'post', 'put' ]) - }) - }) + expect(mounter.getDefaultVerbs()).to.eql([ 'del', 'get', 'head', 'opts', 'patch', 'post', 'put' ]); + }); + }); describe('.getPluginName', () => { it('should return correct name', () => { - expect(mounter.getPluginName()).to.equal('restify') - }) - }) + expect(mounter.getPluginName()).to.equal('restify'); + }); + }); describe('.mount', () => { context('when a single handler is provided', () => { it('should mount the handler onto the server', () => { - const url = '/foo' - const handler = function a() {} - const server = sinon.stub({ get() {} }) + const url = '/foo'; + const handler = function handler() {}; + const server = sinon.stub({ get() {} }); - mounter.mount(url, 'get', handler, { server }) + mounter.mount(url, 'get', handler, { server }); - expect(server.get.calledOnce).to.be.true - expect(server.get.args[0]).to.eql([ url, handler ]) - }) - }) + expect(server.get.calledOnce).to.be.true; + expect(server.get.args[0]).to.eql([ url, handler ]); + }); + }); context('when multiple handlers are provided in an array', () => { it('should mount all handlers onto the server', () => { - const url = '/foo' - const handlers = [ function a() {}, function b() {} ] - const server = sinon.stub({ get() {} }) + const url = '/foo'; + const handlers = [ function a() {}, function b() {} ]; + const server = sinon.stub({ get() {} }); - mounter.mount(url, 'get', handlers, { server }) + mounter.mount(url, 'get', handlers, { server }); - expect(server.get.calledOnce).to.be.true - expect(server.get.args[0]).to.eql([ url ].concat(handlers)) - }) - }) + expect(server.get.calledOnce).to.be.true; + expect(server.get.args[0]).to.eql([ url ].concat(handlers)); + }); + }); context('when any handler has a options attached', () => { it('should mount the handlers to the server with those options', () => { - const url = '/foo' - const verb = 'get' - const handlers = [ function a() {}, function b() {} ] - const server = sinon.stub({ get() {} }) - const version = '1.0.0' + const url = '/foo'; + const verb = 'get'; + const handlers = [ function a() {}, function b() {} ]; + const server = sinon.stub({ get() {} }); + const version = '1.0.0'; - handlers[0].options = { version } + handlers[0].options = { version }; - mounter.mount(url, verb, handlers, { server }) + mounter.mount(url, verb, handlers, { server }); - expect(server.get.calledOnce).to.be.true + expect(server.get.calledOnce).to.be.true; expect(server.get.args[0]).to.eql([ { method: verb, path: url, version - } ].concat(handlers)) - }) - }) - }) - }) -}) + } ].concat(handlers)); + }); + }); + }); + }); +}); diff --git a/test/plugin.spec.js b/test/plugin.spec.js index e64865a..07814fc 100644 --- a/test/plugin.spec.js +++ b/test/plugin.spec.js @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 Alasdair Mercer, Skelp + * Copyright (C) 2017 Alasdair Mercer, !ninja * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -20,24 +20,24 @@ * SOFTWARE. */ -'use strict' +'use strict'; -const { expect } = require('chai') +const { expect } = require('chai'); -const Plugin = require('../src/plugin') +const Plugin = require('../src/plugin'); describe('plugin', () => { describe('Plugin.prototype', () => { - let plugin + let plugin; beforeEach(() => { - plugin = new Plugin() - }) + plugin = new Plugin(); + }); describe('.getPluginName', () => { it('should be abstract', () => { - expect(plugin.getPluginName.bind(plugin)).to.throw('Plugin#getPluginName has not been implemented') - }) - }) - }) -}) + expect(plugin.getPluginName.bind(plugin)).to.throw('Plugin#getPluginName has not been implemented'); + }); + }); + }); +}); diff --git a/test/registrar/index-registrar.spec.js b/test/registrar/index-registrar.spec.js index 3000df1..cddcd3f 100644 --- a/test/registrar/index-registrar.spec.js +++ b/test/registrar/index-registrar.spec.js @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 Alasdair Mercer, Skelp + * Copyright (C) 2017 Alasdair Mercer, !ninja * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -20,31 +20,31 @@ * SOFTWARE. */ -'use strict' +'use strict'; -const IndexRegistrar = require('../../src/registrar/index-registrar') -const RegistrarTestCase = require('./registrar-test-case') +const IndexRegistrar = require('../../src/registrar/index-registrar'); +const RegistrarTestCase = require('./registrar-test-case'); describe('registrar/index-registrar', () => { describe('IndexRegistrar.prototype', () => { - let registrar - let testCase + let registrar; + let testCase; beforeEach(() => { - registrar = new IndexRegistrar() - testCase = new RegistrarTestCase(registrar, 'index') - }) + registrar = new IndexRegistrar(); + testCase = new RegistrarTestCase(registrar, 'index'); + }); describe('.getPluginName', () => { it('should return correct name', () => { - testCase.testGetPluginName() - }) - }) + testCase.testGetPluginName(); + }); + }); describe('.register', () => { it('should register index file-based routes via mounter', () => { - testCase.testRegister() - }) - }) - }) -}) + testCase.testRegister(); + }); + }); + }); +}); diff --git a/test/registrar/index.spec.js b/test/registrar/index.spec.js index 58a741f..1848071 100644 --- a/test/registrar/index.spec.js +++ b/test/registrar/index.spec.js @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 Alasdair Mercer, Skelp + * Copyright (C) 2017 Alasdair Mercer, !ninja * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -20,50 +20,50 @@ * SOFTWARE. */ -'use strict' +'use strict'; -const { expect } = require('chai') -const path = require('path') -const sinon = require('sinon') +const { expect } = require('chai'); +const path = require('path'); +const sinon = require('sinon'); -const Mounter = require('../../src/mounter') -const Registrar = require('../../src/registrar') -const routerify = require('../../src/routerify') +const Mounter = require('../../src/mounter'); +const Registrar = require('../../src/registrar'); +const routerify = require('../../src/routerify'); describe('registrar/index', () => { describe('Registrar.prototype', () => { - let mounter - let registrar + let mounter; + let registrar; beforeEach(() => { - mounter = sinon.createStubInstance(Mounter) - registrar = new Registrar() - }) + mounter = sinon.createStubInstance(Mounter); + registrar = new Registrar(); + }); describe('.buildUrl', () => { it('should build the URL correctly', () => { - mounter.formatParamPath.withArgs('id').returns('{id}') + mounter.formatParamPath.withArgs('id').returns('{id}'); - const file = path.join('test', 'fixtures', 'registrar', 'verb', 'level1', '_id', 'level2', 'get.js') + const file = path.join('test', 'fixtures', 'registrar', 'verb', 'level1', '_id', 'level2', 'get.js'); const url = registrar.buildUrl(file, { mounter, paramPattern: /^_(.+)/ - }) + }); - expect(url).to.equal('/test/fixtures/registrar/verb/level1/{id}/level2') - }) - }) + expect(url).to.equal('/test/fixtures/registrar/verb/level1/{id}/level2'); + }); + }); describe('.loadRouter', () => { it('should load file as module based on "dir" option', () => { - expect(registrar.loadRouter('routerify.js', { dir: 'src' })).to.equal(routerify) - }) - }) + expect(registrar.loadRouter('routerify.js', { dir: 'src' })).to.equal(routerify); + }); + }); describe('.register', () => { it('should be abstract', () => { - expect(registrar.register.bind(registrar)).to.throw('Registrar#register has not been implemented') - }) - }) - }) -}) + expect(registrar.register.bind(registrar)).to.throw('Registrar#register has not been implemented'); + }); + }); + }); +}); diff --git a/test/registrar/registrar-test-case.js b/test/registrar/registrar-test-case.js index 01925f5..583d9ad 100644 --- a/test/registrar/registrar-test-case.js +++ b/test/registrar/registrar-test-case.js @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 Alasdair Mercer, Skelp + * Copyright (C) 2017 Alasdair Mercer, !ninja * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -20,23 +20,23 @@ * SOFTWARE. */ -'use strict' +'use strict'; /* eslint "arrow-body-style": "off" */ -const { expect } = require('chai') -const glob = require('glob') -const path = require('path') -const sinon = require('sinon') +const { expect } = require('chai'); +const glob = require('glob'); +const path = require('path'); +const sinon = require('sinon'); -const Mounter = require('../../src/mounter/index') +const Mounter = require('../../src/mounter/index'); class Route { constructor(url, verb, handlers = 1) { - this.url = url - this.verb = verb - this.handlers = handlers + this.url = url; + this.verb = verb; + this.handlers = handlers; } } @@ -44,11 +44,11 @@ class Route { class RegistrarTestCase { constructor(registrar, name) { - this.registrar = registrar - this.name = name - this.directory = `test/fixtures/registrar/${name}` - this.mounter = sinon.createStubInstance(Mounter) - this.mounter.formatParamPath.withArgs('id').returns('{id}') + this.registrar = registrar; + this.name = name; + this.directory = `test/fixtures/registrar/${name}`; + this.mounter = sinon.createStubInstance(Mounter); + this.mounter.formatParamPath.withArgs('id').returns('{id}'); this.expected = [ new Route('/', 'get'), new Route('/', 'head', 2), @@ -58,16 +58,16 @@ class RegistrarTestCase { new Route('/level1/{id}', 'get'), new Route('/level1/{id}', 'put'), new Route('/level1/{id}/level2', 'get') - ] + ]; } testGetPluginName() { - expect(this.registrar.getPluginName()).to.equal(this.name) + expect(this.registrar.getPluginName()).to.equal(this.name); } testRegister() { - const directory = this.directory.replace(/\\|\//g, path.sep) - const fixtures = glob.sync('**/*.js', { cwd: directory }) + const directory = this.directory.replace(/\\|\//g, path.sep); + const fixtures = glob.sync('**/*.js', { cwd: directory }); const options = { dir: directory, ext: '.js', @@ -75,21 +75,21 @@ class RegistrarTestCase { paramPattern: /^_(.+)/, registrar: this.registrar, verbs: [ 'del', 'get', 'head', 'opts', 'patch', 'post', 'put' ] - } + }; fixtures.forEach((fixture) => { - this.registrar.register(fixture, options) - }) + this.registrar.register(fixture, options); + }); - expect(this.mounter.mount.callCount).to.equal(this.expected.length) + expect(this.mounter.mount.callCount).to.equal(this.expected.length); this.expected.forEach((route) => { this.mounter.mount.calledWith(route.url, route.verb, sinon.match((arg) => { - return route.handlers === 1 ? typeof arg === 'function' : Array.isArray(arg) && arg.length === route.handlers - }), options) - }) + return route.handlers === 1 ? typeof arg === 'function' : Array.isArray(arg) && arg.length === route.handlers; + }), options); + }); } } -module.exports = RegistrarTestCase +module.exports = RegistrarTestCase; diff --git a/test/registrar/verb-registrar.spec.js b/test/registrar/verb-registrar.spec.js index 7d2e59b..30e5759 100644 --- a/test/registrar/verb-registrar.spec.js +++ b/test/registrar/verb-registrar.spec.js @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 Alasdair Mercer, Skelp + * Copyright (C) 2017 Alasdair Mercer, !ninja * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -20,31 +20,31 @@ * SOFTWARE. */ -'use strict' +'use strict'; -const RegistrarTestCase = require('./registrar-test-case') -const VerbRegistrar = require('../../src/registrar/verb-registrar') +const RegistrarTestCase = require('./registrar-test-case'); +const VerbRegistrar = require('../../src/registrar/verb-registrar'); describe('registrar/verb-registrar', () => { describe('VerbRegistrar.prototype', () => { - let registrar - let testCase + let registrar; + let testCase; beforeEach(() => { - registrar = new VerbRegistrar() - testCase = new RegistrarTestCase(registrar, 'verb') - }) + registrar = new VerbRegistrar(); + testCase = new RegistrarTestCase(registrar, 'verb'); + }); describe('.getPluginName', () => { it('should return correct name', () => { - testCase.testGetPluginName() - }) - }) + testCase.testGetPluginName(); + }); + }); describe('.register', () => { it('should register verb file-based routes via mounter', () => { - testCase.testRegister() - }) - }) - }) -}) + testCase.testRegister(); + }); + }); + }); +}); diff --git a/test/routerify.spec.js b/test/routerify.spec.js index 3b04a26..426a22b 100644 --- a/test/routerify.spec.js +++ b/test/routerify.spec.js @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 Alasdair Mercer, Skelp + * Copyright (C) 2017 Alasdair Mercer, !ninja * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -20,56 +20,56 @@ * SOFTWARE. */ -'use strict' +'use strict'; -const { expect } = require('chai') -const glob = require('glob') -const sinon = require('sinon') +const { expect } = require('chai'); +const glob = require('glob'); +const sinon = require('sinon'); -const ExpressMounter = require('../src/mounter/express-mounter') -const IndexRegistrar = require('../src/registrar/index-registrar') -const Mounter = require('../src/mounter') -const { version } = require('../package.json') -const Plugin = require('../src/plugin') -const Registrar = require('../src/registrar') -const RestifyMounter = require('../src/mounter/restify-mounter') -const routerify = require('../src/routerify') -const VerbRegistrar = require('../src/registrar/verb-registrar') +const ExpressMounter = require('../src/mounter/express-mounter'); +const IndexRegistrar = require('../src/registrar/index-registrar'); +const Mounter = require('../src/mounter'); +const { version } = require('../package.json'); +const Plugin = require('../src/plugin'); +const Registrar = require('../src/registrar'); +const RestifyMounter = require('../src/mounter/restify-mounter'); +const routerify = require('../src/routerify'); +const VerbRegistrar = require('../src/registrar/verb-registrar'); describe('routerify', () => { describe('routerify', () => { - let mounter - const mounterName = 'routerify-test-mounter' - let registrar - const registrarName = 'routerify-test-registrar' + let mounter; + const mounterName = 'routerify-test-mounter'; + let registrar; + const registrarName = 'routerify-test-registrar'; before(() => { - mounter = sinon.createStubInstance(Mounter) - mounter.getPluginName.returns(mounterName) - registrar = sinon.createStubInstance(Registrar) - registrar.getPluginName.returns(registrarName) + mounter = sinon.createStubInstance(Mounter); + mounter.getPluginName.returns(mounterName); + registrar = sinon.createStubInstance(Registrar); + registrar.getPluginName.returns(registrarName); - routerify.use(mounter) - routerify.use(registrar) - }) + routerify.use(mounter); + routerify.use(registrar); + }); beforeEach(() => { - sinon.stub(glob, 'sync') - }) + sinon.stub(glob, 'sync'); + }); afterEach(() => { - mounter.getDefaultVerbs.reset() - registrar.register.reset() + mounter.getDefaultVerbs.reset(); + registrar.register.reset(); - glob.sync.restore() - }) + glob.sync.restore(); + }); it('should find and register all route modules based on options', () => { const files = [ 'file1.coffee', 'file2.coffee', 'file3.coffee' - ] + ]; const options = { dir: 'test', ext: '.coffee', @@ -79,42 +79,42 @@ describe('routerify', () => { registrar: registrarName, server: sinon.spy(), verbs: [ 'get', 'head' ] - } + }; - glob.sync.withArgs('**/*.coffee', { cwd: 'test', foo: 'bar' }).returns(files) + glob.sync.withArgs('**/*.coffee', { cwd: 'test', foo: 'bar' }).returns(files); - routerify(options) + routerify(options); - expect(registrar.register.callCount).to.equal(files.length) + expect(registrar.register.callCount).to.equal(files.length); files.forEach((file) => { - expect(registrar.register.calledWithExactly(file, options)) - }) - }) + expect(registrar.register.calledWithExactly(file, options)); + }); + }); context('when no options are specifed', () => { - let defaultMounter - let defaultRegistrar + let defaultMounter; + let defaultRegistrar; beforeEach(() => { - defaultMounter = routerify.lookup(Mounter, 'express') - sinon.stub(defaultMounter, 'getDefaultVerbs') - defaultRegistrar = routerify.lookup(Registrar, 'index') - sinon.stub(defaultRegistrar, 'register') - }) + defaultMounter = routerify.lookup(Mounter, 'express'); + sinon.stub(defaultMounter, 'getDefaultVerbs'); + defaultRegistrar = routerify.lookup(Registrar, 'index'); + sinon.stub(defaultRegistrar, 'register'); + }); afterEach(() => { - defaultMounter.getDefaultVerbs.restore() - defaultRegistrar.register.restore() - }) + defaultMounter.getDefaultVerbs.restore(); + defaultRegistrar.register.restore(); + }); it('should use correct default options', () => { const files = [ 'file1.js', 'file2.js', 'file3.js' - ] - const server = sinon.spy() - const verbs = [ 'get', 'head' ] + ]; + const server = sinon.spy(); + const verbs = [ 'get', 'head' ]; const options = { dir: process.cwd(), ext: '.js', @@ -124,99 +124,99 @@ describe('routerify', () => { registrar: 'index', server, verbs - } + }; - defaultMounter.getDefaultVerbs.returns(verbs) - glob.sync.withArgs('**/*.js', { cwd: options.dir }).returns(files) + defaultMounter.getDefaultVerbs.returns(verbs); + glob.sync.withArgs('**/*.js', { cwd: options.dir }).returns(files); - routerify({ server }) + routerify({ server }); - expect(defaultRegistrar.register.callCount).to.equal(files.length) + expect(defaultRegistrar.register.callCount).to.equal(files.length); files.forEach((file) => { - expect(defaultRegistrar.register.calledWithExactly(file, options)) - }) - }) - }) + expect(defaultRegistrar.register.calledWithExactly(file, options)); + }); + }); + }); describe('.lookup', () => { context('when only a type is provided', () => { it('should return all plugins when Plugin is passed', () => { - const plugins = routerify.lookup(Plugin) + const plugins = routerify.lookup(Plugin); - expect(plugins).to.have.length.of.at.least(4) - expect(plugins[0]).to.be.an.instanceOf(ExpressMounter) - expect(plugins[1]).to.be.an.instanceOf(RestifyMounter) - expect(plugins[2]).to.be.an.instanceOf(IndexRegistrar) - expect(plugins[3]).to.be.an.instanceOf(VerbRegistrar) - }) + expect(plugins).to.have.length.of.at.least(4); + expect(plugins[0]).to.be.an.instanceOf(ExpressMounter); + expect(plugins[1]).to.be.an.instanceOf(RestifyMounter); + expect(plugins[2]).to.be.an.instanceOf(IndexRegistrar); + expect(plugins[3]).to.be.an.instanceOf(VerbRegistrar); + }); it('should return only plugins of a given type when it is passed', () => { - const mounters = routerify.lookup(Mounter) + const mounters = routerify.lookup(Mounter); - expect(mounters).to.have.length.of.at.least(2) - expect(mounters[0]).to.be.an.instanceOf(ExpressMounter) - expect(mounters[1]).to.be.an.instanceOf(RestifyMounter) + expect(mounters).to.have.length.of.at.least(2); + expect(mounters[0]).to.be.an.instanceOf(ExpressMounter); + expect(mounters[1]).to.be.an.instanceOf(RestifyMounter); - const registrars = routerify.lookup(Registrar) + const registrars = routerify.lookup(Registrar); - expect(registrars[0]).to.be.an.instanceOf(IndexRegistrar) - expect(registrars[1]).to.be.an.instanceOf(VerbRegistrar) - }) - }) + expect(registrars[0]).to.be.an.instanceOf(IndexRegistrar); + expect(registrars[1]).to.be.an.instanceOf(VerbRegistrar); + }); + }); context('when an existing name is provided along with type', () => { it('should return the specific ', () => { class LookupTestPlugin extends Plugin { getPluginName() { - return 'lookup-test-plugin' + return 'lookup-test-plugin'; } } - const plugin = routerify.use(new LookupTestPlugin()) + const plugin = routerify.use(new LookupTestPlugin()); - expect(routerify.lookup(Plugin, 'lookup-test-plugin')).to.equal(plugin) - }) - }) + expect(routerify.lookup(Plugin, 'lookup-test-plugin')).to.equal(plugin); + }); + }); context('when an unknown name is provided along with type', () => { it('should throw an error', () => { - expect(routerify.lookup.bind(routerify, Plugin, 'foo')).to.throw(Error, 'Unable to lookup Plugin: foo') - }) - }) - }) + expect(routerify.lookup.bind(routerify, Plugin, 'foo')).to.throw(Error, 'Unable to lookup Plugin: foo'); + }); + }); + }); describe('.use', () => { it('should register the plugin', () => { class UseTestPlugin extends Plugin { getPluginName() { - return 'use-test-plugin' + return 'use-test-plugin'; } } - const plugin = new UseTestPlugin() + const plugin = new UseTestPlugin(); - expect(routerify.use(plugin)).to.equal(plugin) - expect(routerify.lookup(Plugin, 'use-test-plugin')).to.equal(plugin) - }) + expect(routerify.use(plugin)).to.equal(plugin); + expect(routerify.lookup(Plugin, 'use-test-plugin')).to.equal(plugin); + }); context('when plugin is null', () => { it('should not add a plugin', () => { - const plugins = routerify.lookup(Object) + const plugins = routerify.lookup(Object); - expect(routerify.use(null)).to.be.null - expect(routerify.lookup(Object)).to.eql(plugins) - }) - }) - }) + expect(routerify.use(null)).to.be.null; + expect(routerify.lookup(Object)).to.eql(plugins); + }); + }); + }); describe('.version', () => { it('should match package version', () => { - expect(routerify.version).to.equal(version) - }) - }) - }) -}) + expect(routerify.version).to.equal(version); + }); + }); + }); +}); diff --git a/test/utilities.spec.js b/test/utilities.spec.js index 08e1b86..5ec3b8a 100644 --- a/test/utilities.spec.js +++ b/test/utilities.spec.js @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 Alasdair Mercer, Skelp + * Copyright (C) 2017 Alasdair Mercer, !ninja * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -20,11 +20,11 @@ * SOFTWARE. */ -'use strict' +'use strict'; -const { expect } = require('chai') +const { expect } = require('chai'); -const Utilities = require('../src/utilities') +const Utilities = require('../src/utilities'); describe('utilities', () => { describe('Utilities', () => { @@ -33,13 +33,13 @@ describe('utilities', () => { class Foo { static bar() { - Utilities.abstracted(Foo, 'bar') + Utilities.abstracted(Foo, 'bar'); } } - expect(Foo.bar).to.throw(Error, 'Foo#bar has not been implemented') - }) - }) - }) -}) + expect(Foo.bar).to.throw(Error, 'Foo#bar has not been implemented'); + }); + }); + }); +}); From e9df249adb32d3d60d7a73c6b74c02663e7d1879 Mon Sep 17 00:00:00 2001 From: Alasdair Mercer Date: Tue, 16 May 2017 00:19:56 +0100 Subject: [PATCH 11/12] resolved #10 by switching from Coveralls to Codecov --- README.md | 2 +- codecov.yml | 1 + package.json | 4 ++-- 3 files changed, 4 insertions(+), 3 deletions(-) create mode 100644 codecov.yml diff --git a/README.md b/README.md index 4340cc6..1155ba3 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ [Routerify](https://github.com/NotNinja/routerify) is an opinionated router loader for Express-like applications. [![Build](https://img.shields.io/travis/NotNinja/routerify/develop.svg?style=flat-square)](https://travis-ci.org/NotNinja/routerify) -[![Coverage](https://img.shields.io/coveralls/NotNinja/routerify/develop.svg?style=flat-square)](https://coveralls.io/github/NotNinja/routerify) +[![Coverage](https://img.shields.io/codecov/c/github/NotNinja/routerify/develop.svg?style=flat-square)](https://codecov.io/gh/NotNinja/routerify) [![Dependencies](https://img.shields.io/david/NotNinja/routerify.svg?style=flat-square)](https://david-dm.org/NotNinja/routerify) [![Dev Dependencies](https://img.shields.io/david/dev/NotNinja/routerify.svg?style=flat-square)](https://david-dm.org/NotNinja/routerify?type=dev) [![License](https://img.shields.io/npm/l/routerify.svg?style=flat-square)](https://github.com/NotNinja/routerify/blob/master/LICENSE.md) diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 0000000..db24720 --- /dev/null +++ b/codecov.yml @@ -0,0 +1 @@ +comment: off diff --git a/package.json b/package.json index 81a7eec..a83af81 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ }, "devDependencies": { "chai": "^3.5.0", - "coveralls": "^2.13.1", + "codecov": "^2.2.0", "eslint": "^3.19.0", "eslint-config-notninja": "^0.1.1", "istanbul": "^0.4.5", @@ -37,7 +37,7 @@ "main": "src/routerify.js", "scripts": { "ci": "npm run test", - "report-coverage": "istanbul cover _mocha --report lcovonly -- -R spec \"test/**/*.spec.js\" && coveralls < coverage/lcov.info", + "report-coverage": "istanbul cover _mocha --report lcovonly -- -R spec \"test/**/*.spec.js\" && codecov", "pretest": "eslint \"src/**/*.js\" \"test/**/*.js\"", "test": "istanbul cover _mocha -- -R list \"test/**/*.spec.js\"", "posttest": "istanbul check-coverage" From 7d85b5c2e8ee7a7c458f0e1978a5d28ee210ea96 Mon Sep 17 00:00:00 2001 From: Alasdair Mercer Date: Tue, 16 May 2017 00:27:35 +0100 Subject: [PATCH 12/12] roll 0.3.0 --- CHANGES.md | 5 +++++ README.md | 2 +- package.json | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 5395143..d9fb257 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,8 @@ +## Version 0.3.0, 2017.05.16 + +* Move to !ninja [#9](https://github.com/NotNinja/routerify/issues/9) +* Switch to Codecov for code coverage [#10](https://github.com/NotNinja/routerify/issues/10) + ## Version 0.2.0, 2016.12.18 * Add unit tests [#3](https://github.com/NotNinja/routerify/issues/3) diff --git a/README.md b/README.md index 1155ba3..2fb48f1 100644 --- a/README.md +++ b/README.md @@ -333,7 +333,7 @@ The current version of Routerify. ``` javascript routerify.version -=> "0.2.0" +=> "0.3.0" ``` ## Bugs diff --git a/package.json b/package.json index a83af81..89bf63d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "routerify", - "version": "0.2.0", + "version": "0.3.0", "description": "Opinionated router loader for Express-like applications", "homepage": "https://github.com/NotNinja/routerify", "bugs": {