Skip to content

Commit

Permalink
fixes #1, add ability to force replaces
Browse files Browse the repository at this point in the history
  • Loading branch information
dbashford committed Jul 8, 2015
1 parent 057c067 commit b5ef9ba
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 5 deletions.
45 changes: 40 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

hapi-route-builder is a library for building routes in [Hapi](http://hapijs.com/). The goal of this library is to allow for dynamic and terse route building while isolating your application's routes from possible breakages when updating Hapi versions.

hapi-route-builder uses the [Builder Pattern](https://en.wikipedia.org/wiki/Builder_pattern) to create route configuration. The library's[fluent API](https://en.wikipedia.org/wiki/Fluent_interface#JavaScript) makes your route configuration readable.
hapi-route-builder uses the [Builder Pattern](https://en.wikipedia.org/wiki/Builder_pattern) to create route configuration. The library's [fluent API](https://en.wikipedia.org/wiki/Fluent_interface#JavaScript) makes your route configuration readable.

To help reduce duplication in the configuration the library includes the ability to set default configuration and to constrain those defaults to certain routes or route patterns.

Expand Down Expand Up @@ -95,15 +95,17 @@ But the output is the same.

`npm install --save hapi-route-builder`

# Usage

```javascript
var hrb = require("hapi-route-builder");
var RouteBuilder = hrb.RouteBuilder;
var RBDefault = hrb.RBDefault;
```

## RouteBuilder
## Route Buliding

Use the RouteBuilder by instantiating a new instance and then chaining its functions. Calling the `build` function will return the Hapi route configuration object.
Use the `RouteBuilder` by instantiating a new instance and then chaining its functions. Calling the `build` function will return the Hapi route configuration object.

```javascript
var config =
Expand All @@ -113,7 +115,7 @@ var config =
.build();
```

## RBDefault
## Route Defaults

To utilize default configuration across routes instantiate an `RBDefault`. It takes as part of its constructor a function that takes a `RouteBuilder` instance. You can then chain any of the `RouteBuilder` functions.

Expand All @@ -130,7 +132,40 @@ RouteBuilder.addDefault(def);

Defaults are applied to a route when you call an output function (`build` or `print` for instance). That is important to keep in mind for those `RouteBuilder` functions for which call order matters. A call to `preSerial` as part of a RBDefault will result in that added `pre` being the last in the `pre` array. The `preSerial` function takes an optional `index` parameter to handle this case.

Any `replace`ments, registered using the `replace` function, are the only things executed after defaults are applied.
## Configuration Replacements & Forced Replacements

When `build` is called on a route, the RouteBuilder performs several tasks upon the assembled configuration.

First it applies defaults to the route being built assuming the route's `url` has had defaults applied.

Next it will address any replacements registered for the routes using the `replace` function. This allows for default config to be overridden, or possibly any external configuration to be used to update route config.

Last it will check the entire configuration to see if any values, either object values or values of Arrays, are set to a string that both begins and ends with `%`. If any such string exists anywhere in the configuration, an Error will be thrown. Strings surrounded with percent signs must be replaced using the `replace` function. This forced replacement functionality allows a default to be applied that must be implemented by all, or a subset, of routes.

As a naive example, assume all routes must have a `path`.

```javascript
RouteBuilder.addDefault(new RBDefault(function(rb) {
rb.url("%replaceme%")
}));
```

Now, when `build`ing a route, if the `url` hasn't been set, `build` will throw an error.

```javascript
new RouteBuilder()
.post()
.build() // throws error because url not set.
```

To replace it, use the `replace` function.

```javascript
new RouteBuilder()
.post()
.replace("%replaceme%", "/api/foo")
.build() // does not throw error
```

# API

Expand Down
10 changes: 10 additions & 0 deletions src/route_builder.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
var utils = require("./utils")
, traverse = require('traverse')
, replaceCheck = /^%.*%$/
;

function RouteBuilder() {
Expand Down Expand Up @@ -66,6 +67,14 @@ RouteBuilder.prototype._applyReplaces = function() {
}
};

RouteBuilder.prototype._checkForcedReplaces = function() {
traverse(this.route).forEach(function (x) {
if (typeof x === "string" && replaceCheck.test(x)) {
throw new Error("String " + x + " not replaced in the configuration.");
}
});
};

RouteBuilder.prototype.replace = function(key, val) {
this.replaces.push({key:key, val:val});
return this;
Expand All @@ -74,6 +83,7 @@ RouteBuilder.prototype.replace = function(key, val) {
RouteBuilder.prototype.build = function() {
this._applyDefaults();
this._applyReplaces();
this._checkForcedReplaces();
return this.route;
};

Expand Down
22 changes: 22 additions & 0 deletions test/builder_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,26 @@ describe("building config", function() {
expect(config.path).to.eql("foo/{foo_id}");
expect(config.config.pre[0]).to.eql(bar);
});

it("throws error replace when %% syntax not replaced", function() {
var run = function() {
new RouteBuilder()
.url("%replaceme%")
.build()
};

expect(run).to.throw(Error);
});

it("will not throw error if %% replaced", function() {
var run = function() {
new RouteBuilder()
.url("%replaceme")
.replace("%replaceme%", "/api/foo")
.build()
};

expect(run).to.not.throw(Error);
});

});

0 comments on commit b5ef9ba

Please sign in to comment.