Skip to content

Commit

Permalink
Initial node support: Add request, and add browser field, and configu…
Browse files Browse the repository at this point in the history
…re everything to use it.
  • Loading branch information
latentflip committed May 26, 2015
1 parent 18ec49e commit 7968958
Show file tree
Hide file tree
Showing 6 changed files with 139 additions and 132 deletions.
2 changes: 2 additions & 0 deletions ampersand-sync-browser.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
var xhr = require('xhr');
module.exports = require('./core')(xhr);
132 changes: 2 additions & 130 deletions ampersand-sync.js
Original file line number Diff line number Diff line change
@@ -1,130 +1,2 @@
/*$AMPERSAND_VERSION*/
var result = require('lodash.result');
var defaults = require('lodash.defaults');
var includes = require('lodash.includes');
var assign = require('lodash.assign');
var xhr = require('xhr');
var qs = require('qs');


// Throw an error when a URL is needed, and none is supplied.
var urlError = function () {
throw new Error('A "url" property or function must be specified');
};

// Map from CRUD to HTTP for our default `Backbone.sync` implementation.
var methodMap = {
'create': 'POST',
'update': 'PUT',
'patch': 'PATCH',
'delete': 'DELETE',
'read': 'GET'
};

module.exports = function (method, model, options) {
var type = methodMap[method];
var headers = {};

// Default options, unless specified.
defaults(options || (options = {}), {
emulateHTTP: false,
emulateJSON: false,
// overrideable primarily to enable testing
xhrImplementation: xhr
});

// Default request options.
var params = {type: type};

// Ensure that we have a URL.
if (!options.url) {
options.url = result(model, 'url') || urlError();
}

// Ensure that we have the appropriate request data.
if (options.data == null && model && (method === 'create' || method === 'update' || method === 'patch')) {
params.json = options.attrs || model.toJSON(options);
}

// If passed a data param, we add it to the URL or body depending on request type
if (options.data && type === 'GET') {
// make sure we've got a '?'
options.url += includes(options.url, '?') ? '&' : '?';
options.url += qs.stringify(options.data);
}

// For older servers, emulate JSON by encoding the request into an HTML-form.
if (options.emulateJSON) {
headers['Content-Type'] = 'application/x-www-form-urlencoded';
params.body = params.json ? {model: params.json} : {};
delete params.json;
}

// For older servers, emulate HTTP by mimicking the HTTP method with `_method`
// And an `X-HTTP-Method-Override` header.
if (options.emulateHTTP && (type === 'PUT' || type === 'DELETE' || type === 'PATCH')) {
params.type = 'POST';
if (options.emulateJSON) params.body._method = type;
headers['X-HTTP-Method-Override'] = type;
}

// When emulating JSON, we turn the body into a querystring.
// We do this later to let the emulateHTTP run its course.
if (options.emulateJSON) {
params.body = qs.stringify(params.body);
}

// Start setting ajaxConfig options (headers, xhrFields).
var ajaxConfig = (result(model, 'ajaxConfig') || {});

// Combine generated headers with user's headers.
if (ajaxConfig.headers) {
assign(headers, ajaxConfig.headers);
}
params.headers = headers;

//Set XDR for cross domain in IE8/9
if (ajaxConfig.useXDR) {
params.useXDR = true;
}

// Set raw xhr options.
if (ajaxConfig.xhrFields) {
var beforeSend = ajaxConfig.beforeSend;
params.beforeSend = function (req) {
for (var key in ajaxConfig.xhrFields) {
req[key] = ajaxConfig.xhrFields[key];
}
if (beforeSend) return beforeSend.apply(this, arguments);
};
params.xhrFields = ajaxConfig.xhrFields;
} else {
params.beforeSend = ajaxConfig.beforeSend;
}

// Turn a jQuery.ajax formatted request into xhr compatible
params.method = params.type;

var ajaxSettings = assign(params, options);

// Make the request. The callback executes functions that are compatible
// With jQuery.ajax's syntax.
var request = options.xhr = options.xhrImplementation(ajaxSettings, function (err, resp, body) {
if (err || resp.statusCode >= 400) {
if (options.error) return options.error(resp, 'error', err.message || body);
} else {
// Parse body as JSON if a string.
if (body && typeof body === 'string') {
try {
body = JSON.parse(body);
} catch (err) {
if (options.error) return options.error(resp, 'error', err.message);
}
}
if (options.success) return options.success(body, 'success', resp);
}
});
model.trigger('request', model, request, options, ajaxSettings);
request.ajaxSettings = ajaxSettings;
return request;
};
var xhr = require('request');
module.exports = require('./core')(xhr);
131 changes: 131 additions & 0 deletions core.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
/*$AMPERSAND_VERSION*/
module.exports = function (xhr) {
var result = require('lodash.result');
var defaults = require('lodash.defaults');
var includes = require('lodash.includes');
var assign = require('lodash.assign');
var qs = require('qs');


// Throw an error when a URL is needed, and none is supplied.
var urlError = function () {
throw new Error('A "url" property or function must be specified');
};

// Map from CRUD to HTTP for our default `Backbone.sync` implementation.
var methodMap = {
'create': 'POST',
'update': 'PUT',
'patch': 'PATCH',
'delete': 'DELETE',
'read': 'GET'
};

return function (method, model, options) {
var type = methodMap[method];
var headers = {};

// Default options, unless specified.
defaults(options || (options = {}), {
emulateHTTP: false,
emulateJSON: false,
// overrideable primarily to enable testing
xhrImplementation: xhr
});

// Default request options.
var params = {type: type};

// Ensure that we have a URL.
if (!options.url) {
options.url = result(model, 'url') || urlError();
}

// Ensure that we have the appropriate request data.
if (options.data == null && model && (method === 'create' || method === 'update' || method === 'patch')) {
params.json = options.attrs || model.toJSON(options);
}

// If passed a data param, we add it to the URL or body depending on request type
if (options.data && type === 'GET') {
// make sure we've got a '?'
options.url += includes(options.url, '?') ? '&' : '?';
options.url += qs.stringify(options.data);
}

// For older servers, emulate JSON by encoding the request into an HTML-form.
if (options.emulateJSON) {
headers['Content-Type'] = 'application/x-www-form-urlencoded';
params.body = params.json ? {model: params.json} : {};
delete params.json;
}

// For older servers, emulate HTTP by mimicking the HTTP method with `_method`
// And an `X-HTTP-Method-Override` header.
if (options.emulateHTTP && (type === 'PUT' || type === 'DELETE' || type === 'PATCH')) {
params.type = 'POST';
if (options.emulateJSON) params.body._method = type;
headers['X-HTTP-Method-Override'] = type;
}

// When emulating JSON, we turn the body into a querystring.
// We do this later to let the emulateHTTP run its course.
if (options.emulateJSON) {
params.body = qs.stringify(params.body);
}

// Start setting ajaxConfig options (headers, xhrFields).
var ajaxConfig = (result(model, 'ajaxConfig') || {});

// Combine generated headers with user's headers.
if (ajaxConfig.headers) {
assign(headers, ajaxConfig.headers);
}
params.headers = headers;

//Set XDR for cross domain in IE8/9
if (ajaxConfig.useXDR) {
params.useXDR = true;
}

// Set raw xhr options.
if (ajaxConfig.xhrFields) {
var beforeSend = ajaxConfig.beforeSend;
params.beforeSend = function (req) {
for (var key in ajaxConfig.xhrFields) {
req[key] = ajaxConfig.xhrFields[key];
}
if (beforeSend) return beforeSend.apply(this, arguments);
};
params.xhrFields = ajaxConfig.xhrFields;
} else {
params.beforeSend = ajaxConfig.beforeSend;
}

// Turn a jQuery.ajax formatted request into xhr compatible
params.method = params.type;

var ajaxSettings = assign(params, options);

// Make the request. The callback executes functions that are compatible
// With jQuery.ajax's syntax.
var request = options.xhr = options.xhrImplementation(ajaxSettings, function (err, resp, body) {
if (err || resp.statusCode >= 400) {
if (options.error) return options.error(resp, 'error', err.message || body);
} else {
// Parse body as JSON if a string.
if (body && typeof body === 'string') {
try {
body = JSON.parse(body);
} catch (err) {
if (options.error) return options.error(resp, 'error', err.message);
}
}
if (options.success) return options.success(body, 'success', resp);
}
});
model.trigger('request', model, request, options, ajaxSettings);
request.ajaxSettings = ajaxSettings;
return request;
};
};
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"lodash.includes": "^3.1.0",
"lodash.result": "^3.0.0",
"qs": "^2.2.4",
"request": "^2.55.0",
"xhr": "^2.0.1"
},
"devDependencies": {
Expand All @@ -44,6 +45,7 @@
],
"license": "MIT",
"main": "ampersand-sync.js",
"browser": "ampersand-sync-browser.js",
"repository": {
"type": "git",
"url": "https://github.com/ampersandjs/ampersand-sync.git"
Expand Down
2 changes: 1 addition & 1 deletion test/integration.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
var test = require('tape');
var sync = require('../ampersand-sync');
var sync = require('../');
var Model = require('ampersand-model');


Expand Down
2 changes: 1 addition & 1 deletion test/unit.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
var test = require('tape');
var sync = require('../ampersand-sync');
var sync = require('../');
var Model = require('ampersand-model');


Expand Down

0 comments on commit 7968958

Please sign in to comment.