Skip to content

Commit

Permalink
Merge pull request #1 from Skelp/develop
Browse files Browse the repository at this point in the history
0.1.0
  • Loading branch information
neocotic authored Dec 15, 2016
2 parents 61722cb + 8ca298e commit 995a339
Show file tree
Hide file tree
Showing 4 changed files with 291 additions and 20 deletions.
291 changes: 286 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
[![Release](https://img.shields.io/npm/v/routerify.svg?style=flat-square)](https://www.npmjs.com/package/routerify)

* [Install](#install)
* [Configurations](#configurations)
* [API](#api)
* [Bugs](#bugs)
* [Contributors](#contributors)
Expand All @@ -32,17 +33,297 @@ $ npm install --save routerify

You'll need to have at least [Node.js](https://nodejs.org) 6 or newer.

## Modes
## Configurations

TODO: Document
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.

### Registrars

Registrars are responsible for picking which source files within the directory should be loaded as modules and how the
routes are extracted from the modules. These routes are then passed to the mounter.

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
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
**How it works:**

It expects the following file structure under the target directory:

```
- /
- users/
- _userId/
- sessions/
- index.js
- index.js
- helper.js
- index.js
```

Only files whose base name is `index` will be loaded (e.g. `helper.js` in the above example would be ignored) and it
expects each one of those modules to export an object whose values are either a single handler method or an array of
handler methods. Only handlers associated with properties on the object whose name matches that of a supported verb will
be registered.

If we were to build on the example above and say that the `/users/index.js` file looked something like this:

``` javascript
module.exports = {
del(req, res, next) {
...
}
get(req, res, next) {
...
},
put(req, res, next) {
...
},
fetchUser(userId) {
...
}
}
```

Then we could say that at least (we're ignoring routes defined in the other files for the purpose of this example) the
following routes would be registered:

* `DELETE /users/:userId`
* `GET /users/:userId`
* `PUT /users/:userId`

The `fetchUser` method is ignored because it does not match a supported verb.

#### Create your own Registrar

In order to create your own registrar you simply need to extend the `Registrar` class and tell Routerify about it:

``` javascript
const routerify = require('routerify')
const Registrar = require('routerify/src/registrar/registrar')

class CustomRegistrar extends Registrar {

static name() {
return 'custom'
}

register(file, options) {
// Load the module, extract the routes, and mount them via this.mounter
...
}
}

routerify.registrars[CustomRegistrar.name()] = CustomRegistrar

module.exports = CustomRegistrar
```

Now your new registrar can be used by simply specifying its name in the options:

``` javascript
routerify({
dir: path.join(__dirname, 'routes'),
registrar: 'custom',
server: app
})
```

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.

### Mounters

Mounters are primarily responsible for taking the routes and mounting them onto the server. However, they also provide
the default verbs (methods supported by the framework to make route requests for specific HTTP methods) when no `verbs`
option is specified, and they determine how parameter path variables are formatted.

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.

#### `ExpressMounter`

**Name:** `"express"`
**Default:** Yes
**Description:**

Supports all [Express](http://expressjs.com)-like frameworks.

#### `RestifyMounter`

**Name:** `"restify"`
**Default:** No
**Description:**

Supports the [Restify](http://restify.com) framework. Since Restify is an Express-like framework itself, the
`ExpressMounter` will work well for the most part, however, this extension of `ExpressMounter` provides additional
benefits for Restify applications by allowing extra optional information (e.g. `version`/`versions`) to be used when
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.

#### Create your own Mounter

In order to create your own mounter you simply need to extend the `Mounter` class and tell Routerify about it:

``` javascript
const routerify = require('routerify')
const Mounter = require('routerify/src/mounter/mounter')

class CustomMounter extends Mounter {

static name() {
return 'custom'
}

formatParamPath(param) {
// Format param for insertion into the route URL
return ...
}

getDefaultVerbs() {
// Specify the supported verbs
return [...]
}

mount(url, verb, handlers, options) {
// Mount the route onto options.server
...
}
}

routerify.mounters[CustomMounter.name()] = CustomMounter

module.exports = CustomMounter
```

Now your new mounter can be used by simply specifying its name in the options:

``` javascript
routerify({
dir: path.join(__dirname, 'routes'),
mounter: 'custom',
server: app
})
```

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.

## API

TODO: Document
Routerify exposes its API primarily through a single method.

### Options
### `routerify(options)`

TODO: Document
This is the primary method for Routerify and it will go through the target directory, find all routes using the
[registrar](#registrars), and finally mounts them onto the server using the [mounter](#mounter).

``` javascript
const express = require('express')
const path = require('path')
const routerify = require('routerify')
const app = express()

routerify({
dir: path.join(__dirname, 'routes'),
server: app
})

app.listen(3000, () => {
console.log('Example app listening on port 3000!')
})
```

#### Options

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()` |

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.

``` javascript
routerify.version
=> "0.1.0"
```

## Bugs

Expand Down
5 changes: 2 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "routerify",
"version": "0.1.0alpha",
"version": "0.1.0",
"description": "Opinionated router loader for Express-like applications",
"homepage": "https://github.com/Skelp/routerify",
"bugs": {
Expand All @@ -23,8 +23,7 @@
},
"dependencies": {
"glob": "^7.1.1",
"lodash.defaultsdeep": "^4.6.0",
"lodash.pick": "^4.4.0"
"lodash.defaultsdeep": "^4.6.0"
},
"devDependencies": {
"eslint": "^3.12.1",
Expand Down
13 changes: 2 additions & 11 deletions src/mounter/restify.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,6 @@

'use strict'

const pick = require('lodash.pick')

const ExpressMounter = require('./express')

/**
Expand Down Expand Up @@ -56,17 +54,10 @@ class RestifyMounter extends ExpressMounter {

const handlerWithOptions = handlers.find((handler) => handler.options != null)
if (handlerWithOptions) {
const opts = pick(handlerWithOptions.options, [
'contentType',
'name',
'version',
'versions'
])

url = Object.assign({
url = Object.assign({}, handlerWithOptions.options, {
method: verb,
path: url
}, opts)
})
}

super.mount(url, verb, handlers, options)
Expand Down
2 changes: 1 addition & 1 deletion src/routerify.js
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ module.exports = routerify
* @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 <code>mounter</code>.
* @property {Object} server - The server object to which the routes are to be mounted.
* @property {Object} server - The server object (e.g. <code>express()</code>) 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 <code>mounter</code> if not specified.
*/

0 comments on commit 995a339

Please sign in to comment.