Files
cm-app-gateway/node_modules/beeline/README.markdown

242 lines
12 KiB
Markdown

# Beeline
A laughably simplistic router for node.js
Currently works with node.js v0.3.1 and above
## Goals
* Simple
* Unobtrusive
* Fairly Foolproof
* Easy to debug
* Fast
## Examples
```javascript
var bee = require("beeline");
var router = bee.route({ // Create a new router
"/cheggit": function(req, res) {
// Called when req.url === "/cheggit" or req.url === "/cheggit?woo=poo"
},
"/names/`last-name`/`first-name`": function(req, res, tokens, values) {
// Called when req.url contains three parts, the first of is "name".
// The parameter tokens is an object that maps token names to values.
// For example if req.url === "/names/smith/will"
// then tokens === { "first-name": "will", "last-name": "smith" }
// and values === [ "will", "smith" ]
// also req.params === tokens
},
"/static/`path...`": function(req, res, tokens, values) {
// Called when req.url starts with "/static/"
// The parameter tokens is an object that maps token name to a value
// The parameter values is a list of
// For example if req.url === "/static/pictures/actors/smith/will.jpg"
// then tokens === { "path": "pictures/actors/smith/will.jpg" }
// and values === [ "pictures/actors/smith/will.jpg" ]
// also req.params === tokens
},
"/`user`/static/`path...`": function(req, res, tokens, values) {
// Called when req.url contains at least three parts, the second of which
// is "static".
// The parameter tokens is an object that maps token names and value
// For example if req.url === "/da-oozer/static/pictures/venkman.jpg"
// then tokens === { "user": "da-oozer", "path": "pictures/venkman.jpg" }
// and values === [ "da-oozer", "pictures/venkman.jpg" ]
// also req.params === tokens
},
"/blogs/`user-id: [a-z]{2}-\\d{5}`/`post-id: \\d+`": function(
req, res, tokens, values
) {
// Called when req.url starts with "/blogs/" and when the second and third
// parts match /[a-z]{2}-\d{5}/ and /\d+/ respectiviely.
// The parameter tokens is an object that maps token names and value
// For example if req.url === "/blog/ab-12345/1783"
// then tokens === { "user-id": "ab-12345", "post-id": "1783" }
// and values === [ "ab-12345", "1783" ]
// also req.params === tokens
},
"r`^/actors/([\\w]+)/([\\w]+)$`": function(req, res, matches) {
// Called when req.url matches this regex: "^/actors/([\\w]+)/([\\w]+)$"
// An array of captured groups is passed as the third parameter
// For example if req.url === "/actors/smith/will"
// then matches === [ "smith", "will" ]
},
"`404`": function(req, res) {
// Called when no other route rule are matched
//
// This handler can later be called explicitly with router.missing
},
"`500`": function(req, res, err) {
// Called when an exception is thrown by another router function
// The error that caused the exception is passed as the third parameter
// This _not_ guaranteed to catch all exceptions
//
// This handler can later be called explicitly with router.error
}
});
router.add({ // Use `.add` to append new rules to a router
"/ /home r`^/index(.php|.html|.xhtml)?$`": function(req, res) {
// Called when req.url === "/" or req.url === "/home"
// or req.url matches this regex: ^/index(.php|.html|.xhtml)?$
// (i.e. req.url === "/index.php" or req.url === "/index.html")
// Note that any number of rules can be combined using a space.
// All rules will call the same request handler when matched.
},
"/my-method": { // Method (aka verb) specific dispatch. Note case matters.
"GET": function(req, res) {
// Called when req.url === "/my-method" and req.method === "GET"
},
"POST PUT": function(req, res) {
// Called when req.url === "/my-method" and
// req.method === "POST" or req.method === "PUT"
// Methods can be combined with a space like URL rules.
},
"any": function(req, res) {
// Called when req.url === "/my-method" and req.method is not
// "GET" or "POST"
}
},
"`405`": function(req, res) {
// Called when when a URL is specified but no corresponding method (aka verb)
// matches. For example, this handler would be executed if the "any" catch
// all wasn't specified in the handler above and req.method === "HEAD"
//
// This handler can later be called explicitly with router.missingVerb
},
"/explicit-calls": function(req, res) { // If necessary you can reroute requests
if(url.parse(req.url).query["item-name"] === "unknown") {
// Calls the 404 (aka missing) handler:
return router.missing(req, res, this);
// The last parameter is optional. It sets the this pointer in the
// 404 handler.
}
if(url.parse(req.url).query["item-name"] === "an-error") {
// Calls the 500 (aka error) handler:
return router.error(req, res, err, this);
// The last parameter is optional. It sets the this pointer in the
// 500 handler.
}
// Do normal request handling
}
});
// Starts serve with routes defined above:
require("http").createServer(router).listen(8001);
```
See `test/test.js` for a working example.
## The API
To start, simply store the `beeline` library in a local variable:
```javascript
var bee = require("beeline");
```
The `beeline` library contains the following three methods:
- `bee.route(routes)`: Used to create a new router. It returns a function called `rtn_fn` that takes [ServerRequest](http://nodejs.org/docs/v0.6.10/api/http.html#http.ServerRequest) and [ServerResponse](http://nodejs.org/docs/v0.6.10/api/http.html#http.ServerResponse) objects as parameters. The `routes` parameter is an objects that maps rules to handlers. See examples section for more details.
- `bee.staticFile(path, mimeType[, maxage=31536000])`: This is a utility method that is used to quickly expose static files. It returns a function called `rtn_fn` that takes [ServerRequest](http://nodejs.org/docs/v0.6.10/api/http.html#http.ServerRequest) and [ServerResponse](http://nodejs.org/docs/v0.6.10/api/http.html#http.ServerResponse) objects as parameters. When `rtn_fn` is called, the file contents located at `path` are served (via the ServerResponse) with the `Content-Type` set to the `mimeType` parameter. If the file at `path` does not exist a `404` is served. The optional `maxage` parameter is used to in the response's `Cache-Control` header. Also note that all `Set-Cookie` headers are removed. Here's an example of how you might use `bee.staticFile`:
```javascript
bee.route({
"/robots.txt": bee.staticFile("./content/robots.txt", "text/plain")
});
```
- `bee.staticDir(path, mimeTypes[, maxage=31536000])`: This is utility method is used to expose directories of files. It returns a function called `rtn_fn` that takes a [ServerRequest](http://nodejs.org/docs/v0.6.10/api/http.html#http.ServerRequest) object, a [ServerResponse](http://nodejs.org/docs/v0.6.10/api/http.html#http.ServerResponse) object, an optional third parameter, and an array of strings called `matches` as parameters. Whenever `rtn_fn` is called, the items of `matches` are joined together and then concatenated to `path`. The resulting string is assumed to be a path to a specific file. If this file exists, its contents are served (via the ServerResponse) with the `Content-Type` set to the value that corresponds to the file's extension in the `mimeTypes` object. If the resulting string doesn't point to an existing file or if the file's extension is not found in `mimeTypes`, then a `404` is served. Also, file extensions require a leading period (`.`) and are assumed to be lowercase. The optional `maxage` parameter is used to in the response's `Cache-Control` header. Also note that all `Set-Cookie` headers are removed. Here's an example of how you might use `bee.staticDir`:
```javascript
bee.route({
// /pics/mofo.png serves ./content/pics/mofo.png
// /pics/la-ghetto/oh-gee.gif serves ./content/pics/la-ghetto/oh-gee.gif
// /pics/woo-fee.tiff serves a 404 since there's no corresponding
// mimeType specified.
// This helps prevent accidental exposure.
"r`^/pics/(.*)$`": bee.staticDir(
"./content/pics/",
{
".gif": "image/gif", ".png": "image/png",
".jpg": "image/jpeg", ".jpeg": "image/jpeg"
}
),
// Also works with URLs with tokens
// /static/help/faq.html serves ./static/help/faq.html
// /static/properties.json serves a 404 since there's no corresponding
// mimeType specified.
"/static/`path...`": bee.staticDir(
"./static/",
{
".txt": "text/plain", ".html": "text/html",
".css": "text/css", ".xml": "text/xml"
}
),
// More complicated path constructs also works
// /will-smith/img-library/headshots/sexy42.jpg
// serves ./user-images/will-smith/headshots/sexy42.jpg
"/`user`/img-library/`path...`": bee.staticDir(
"./user-images/", { ".jpg": "image/jpeg", ".jpeg": "image/jpeg" }
)
});
```
Beeline is also at least somewhat compatibile with [expressjs](https://github.com/visionmedia/express). Here's an example:
```javascript
app.use(beeline.route({
"/": function(req, res, next) {
fs.readFile("./templates/index.html", function(err, data) {
if(err) { throw err; }
res.html(data);
});
},
"/`user`/static/`path...`": function(req, res, tokens, values, next) {
/* ... code ... */
}
}));
```
Note the `next` callback is always passed as the last parameter.
### Precedence Rules
In the event that a request matches two rules, the following precedence rules are considered:
- Fully defined rules take highest precedence. In other words, `"/index"` has a higher precedences then ``"r`^/index$`"`` even though semantically both rules are exactly the same.
- Tokens and RegExp rules have the same precedence
- RegExp rules take higher precedence than `404`
- `404` and `405` have the lowest precedences
- The `500` rules is outside the precedence rules. It can potentially be triggered at any time.
- Amoung request methods, "any" has the lowerest precdence. Also note that the "x-http-method-override" header is respected.
If the exact same rule is defined twice, then it's unspecified which request handler will be triggered.
## Getting Beeline
The easiest way to get beeline is with [npm](http://npmjs.org/):
npm install beeline
Alternatively you can clone this git repository:
git clone git://github.com/xavi-/beeline.git
## Running Unit Tests
Execute the following commands to run the beeline's unit tests:
$ cd <beeline-directory>
$ cd test
$ node test.js
The last line printed to the console should be, "All done. Everything passed.", if all the tests passed successfully.
## Developed by
* Xavi Ramirez
## License
This project is released under [The MIT License](http://www.opensource.org/licenses/mit-license.php).