Skip to content

Commit

Permalink
Abstract common client bits into a rest/client module
Browse files Browse the repository at this point in the history
- move client.chain() and client.skip() into the client module
- added client.wrap() as the preferred way to apply interceptors to clients
- deprecated client.chain(); although it still works it will log a warning
- updated docs and examples to use wrap
  • Loading branch information
scothis committed Mar 31, 2014
1 parent 21ea7e7 commit c963ecd
Show file tree
Hide file tree
Showing 34 changed files with 271 additions and 188 deletions.
11 changes: 6 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ The return value from a client is a promise that is resolved with the response w

The core client behavior can be augmented with [interceptors](docs/interceptors.md#interceptor-principals). An interceptor wraps the client and transforms the request and response. For example: an interceptor may authenticate a request, or reject the promise if an error is encountered. Interceptors may be combined to create a client with the desired behavior. A configured interceptor acts just like a client. The core clients are basic, they only know the low level mechanics of making a request and parsing the response. All other behavior is applied and configurated with interceptors.

Interceptors are applied to a client by chaining. To chain a client with an interceptor, call the `chain` function on the client providing the interceptor and optionally a configuration object. A new client is returned containing the interceptor's behavior applied to the parent client. It's important to note that the behavior of the original client is not modified, in order to use the new behavior, you must use the returned client.
Interceptors are applied to a client by wrapping. To wrap a client with an interceptor, call the `wrap` method on the client providing the interceptor and optionally a configuration object. A new client is returned containing the interceptor's behavior applied to the parent client. It's important to note that the behavior of the original client is not modified, in order to use the new behavior, you must use the returned client.


### Making a basic request: ###
Expand Down Expand Up @@ -50,7 +50,7 @@ var rest, mime, client;
rest = require('rest'),
mime = require('rest/interceptor/mime');

client = rest.chain(mime);
client = rest.wrap(mime);
client({ path: '/data.json' }).then(function(response) {
console.log('response: ', response);
});
Expand All @@ -68,8 +68,8 @@ rest = require('rest'),
mime = require('rest/interceptor/mime');
errorCode = require('rest/interceptor/errorCode');

client = rest.chain(mime)
.chain(errorCode, { code: 500 });
client = rest.wrap(mime)
.wrap(errorCode, { code: 500 });
client({ path: '/data.json' }).then(
function(response) {
console.log('response: ', response);
Expand Down Expand Up @@ -106,7 +106,7 @@ Let's take the previous example and configure the client using a wire.js specifi
}
```

There are a couple things to notice. First is the '$plugins' section, by declaring the `rest/wire` module, the `rest` factory becomes available within the specification. The second thing to notice is that we no longer need to individually `require()` interceptor modules; wire.js is smart enough to automatically fetch the modules. The interceptors are then chained together in the order they are defined and provided with the corresponding config object, if it's defined. The resulting client can then be injected into any other object using standard wire.js facilities.
There are a couple things to notice. First is the '$plugins' section, by declaring the `rest/wire` module, the `rest` factory becomes available within the specification. The second thing to notice is that we no longer need to individually `require()` interceptor modules; wire.js is smart enough to automatically fetch the modules. The interceptors are then wrapped in the order they are defined and provided with the corresponding config object, if it's defined. The resulting client can then be injected into any other object using standard wire.js facilities.


### Custom MIME Converters: ###
Expand Down Expand Up @@ -231,6 +231,7 @@ Change Log

.next
- bump when.js version to ~3.0, 2.x is no longer supported
- perfer `client.wrap()` to `client.chain()`, `chain` is now deprecated
- add HTTP specific methods to the promises returned from clients: .entity(), .status(), .headers(), .header(name)
- removed 'rest/util/beget' favor Object.create

Expand Down
63 changes: 63 additions & 0 deletions client.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* Copyright 2014 the original author or authors
* @license MIT, see LICENSE.txt for details
*
* @author Scott Andrews
*/

(function (define) {
'use strict';

define(function (/* require */) {

/**
* Add common helper methods to a client impl
*
* @param {function} impl the client implementation
* @param {Client} [target] target of this client, used when wrapping other clients
* @returns {Client} the client impl with additional methods
*/
return function client(impl, target) {

if (target) {

/**
* @returns {Client} the target client
*/
impl.skip = function skip() {
return target;
};

}

/**
* Allow a client to easily be wrapped by an interceptor
*
* @param {Interceptor} interceptor the interceptor to wrap this client with
* @param [config] configuration for the interceptor
* @returns {Client} the newly wrapped client
*/
impl.wrap = function wrap(interceptor, config) {
return interceptor(impl, config);
};

/**
* @deprecated
*/
impl.chain = function chain() {
if (console) {
(console.warn || console.log).call(console, 'rest.js: client.chain() is deprecated, use client.wrap() instead');
}
return impl.wrap.apply(this, arguments);
};

return impl;

};

});

}(
typeof define === 'function' && define.amd ? define : function (factory) { module.exports = factory(require); }
// Boilerplate for AMD and Node
));
13 changes: 4 additions & 9 deletions client/jsonp.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,12 @@

define(function (require) {

var when, UrlBuilder, responsePromise;
var when, UrlBuilder, responsePromise, client;

when = require('when');
UrlBuilder = require('../UrlBuilder');
responsePromise = require('../util/responsePromise');
client = require('../client');

// consider abstracting this into a util module
function clearProperty(scope, propertyName) {
Expand Down Expand Up @@ -77,7 +78,7 @@
*
* @returns {Promise<Response>}
*/
function jsonp(request) {
return client(function jsonp(request) {
return new responsePromise.ResponsePromise(function (resolve, reject) {

var callbackName, callbackParams, script, firstScript, response;
Expand Down Expand Up @@ -132,13 +133,7 @@
firstScript.parentNode.insertBefore(script, firstScript);

});
}

jsonp.chain = function (interceptor, config) {
return interceptor(jsonp, config);
};

return jsonp;
});

});

Expand Down
13 changes: 4 additions & 9 deletions client/node.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

define(function (require) {

var parser, http, https, when, UrlBuilder, normalizeHeaderName, responsePromise, httpsExp;
var parser, http, https, when, UrlBuilder, normalizeHeaderName, responsePromise, client, httpsExp;

parser = envRequire('url');
http = envRequire('http');
Expand All @@ -20,6 +20,7 @@
UrlBuilder = require('../UrlBuilder');
normalizeHeaderName = require('../util/normalizeHeaderName');
responsePromise = require('../util/responsePromise');
client = require('../client');

httpsExp = /^https/i;

Expand Down Expand Up @@ -55,7 +56,7 @@
return buffer;
};

function node(request) {
return client(function node(request) {
return new responsePromise.ResponsePromise(function (resolve, reject) {

var options, clientRequest, client, url, headers, entity, response;
Expand Down Expand Up @@ -132,13 +133,7 @@
clientRequest.end();

});
}

node.chain = function (interceptor, config) {
return interceptor(node, config);
};

return node;
});

});

Expand Down
13 changes: 4 additions & 9 deletions client/xdr.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,14 @@

define(function (require) {

var when, UrlBuilder, responsePromise;
var when, UrlBuilder, responsePromise, client;

when = require('when');
UrlBuilder = require('../UrlBuilder');
responsePromise = require('../util/responsePromise');
client = require('../client');

function xdr(request) {
return client(function xdr(request) {
return new responsePromise.ResponsePromise(function (resolve, reject) {

var client, method, url, entity, response;
Expand Down Expand Up @@ -71,13 +72,7 @@
}

});
}

xdr.chain = function (interceptor, config) {
return interceptor(xdr, config);
};

return xdr;
});

});

Expand Down
13 changes: 4 additions & 9 deletions client/xhr.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,13 @@

define(function (require) {

var when, UrlBuilder, normalizeHeaderName, responsePromise, headerSplitRE;
var when, UrlBuilder, normalizeHeaderName, responsePromise, client, headerSplitRE;

when = require('when');
UrlBuilder = require('../UrlBuilder');
normalizeHeaderName = require('../util/normalizeHeaderName');
responsePromise = require('../util/responsePromise');
client = require('../client');

// according to the spec, the line break is '\r\n', but doesn't hold true in practice
headerSplitRE = /[\r|\n]+/;
Expand Down Expand Up @@ -50,7 +51,7 @@
return headers;
}

function xhr(request) {
return client(function xhr(request) {
return new responsePromise.ResponsePromise(function (resolve, reject) {

var client, method, url, headers, entity, headerName, response, XMLHttpRequest;
Expand Down Expand Up @@ -137,13 +138,7 @@
}

});
}

xhr.chain = function (interceptor, config) {
return interceptor(xhr, config);
};

return xhr;
});

});

Expand Down
6 changes: 3 additions & 3 deletions docs/clients.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@

A rest.js [client](interfaces.md#interface-client) is simply a function that accepts an argument as the [request](interfaces.md#interface-request) and returns a promise for the [response](interfaces.md#interface-response).

Clients are typically extended by chaining interceptors that wrap the client core behavior providing additional functionality and returning an enriched client.
Clients are typically extended by wrapping interceptors that wrap the client core behavior providing additional functionality and returning an enriched client.

```javascript
client = rest.chain(interceptor);
client = rest.wrap(interceptor);
assert.same(rest, client.skip());
```

See the [interceptor docs](interceptors.md) for more information on interceptors and chaining.
See the [interceptor docs](interceptors.md) for more information on interceptors and wrapping.


## Provided Clients
Expand Down
Loading

0 comments on commit c963ecd

Please sign in to comment.