Added Backbone.emulateJSON to enable the current behavior of syncing and made sending the body as application/json without a wrapping model param, the default

This commit is contained in:
John Wright
2010-10-28 20:21:59 -06:00
parent cd21480e8e
commit 302c0d5cee
3 changed files with 102 additions and 26 deletions

View File

@@ -27,9 +27,14 @@
// For Backbone's purposes, jQuery owns the `$` variable.
var $ = this.jQuery;
// Turn on `emulateHttp` to fake `"PUT"` and `"DELETE"` requests via
// Turn on `emulateHTTP` to fake `"PUT"` and `"DELETE"` requests via
// the `_method` parameter.
Backbone.emulateHttp = false;
Backbone.emulateHTTP = false;
// Turn on `emulateJSON` to encode the body as
// `application/x-www-form-urlencoded` instead of `application/json`
// with the model as a JSON string in the param `model`
Backbone.emulateJSON = false;
// Backbone.Events
// -----------------
@@ -654,25 +659,37 @@
// * Send up the models as XML instead of JSON.
// * Persist models via WebSockets instead of Ajax.
//
// Turn on `Backbone.emulateHttp` in order to send `PUT` and `DELETE` requests
// Turn on `Backbone.emulateHTTP` in order to send `PUT` and `DELETE` requests
// as `POST`, with an `_method` parameter containing the true HTTP method.
// Useful when interfacing with server-side languages like **PHP** that make
// it difficult to read the body of `PUT` requests.
// it difficult to read the body of `PUT` requests. Also, turn on Backbone.emulateJSON
// to send the body as `application/x-www-form-urlencoded` instead of
// `application/json`, with the model in a JSON string param named `model`,
Backbone.sync = function(method, model, success, error) {
var sendModel = method === 'create' || method === 'update';
var data = sendModel ? {model : JSON.stringify(model)} : {};
var data = sendModel ? model : {};
var type = methodMap[method];
if (Backbone.emulateHttp && (type === 'PUT' || type === 'DELETE')) {
if (Backbone.emulateHTTP && (type === 'PUT' || type === 'DELETE')) {
data._method = type;
type = 'POST';
}
}
if (sendModel && Backbone.emulateJSON) data.model = JSON.stringify(model);
if (sendModel && !(Backbone.emulateHTTP || Backbone.emulateJSON)) data = JSON.stringify(data);
processData = Backbone.emulateJSON ? true : false;
contentType = Backbone.emulateJSON ? "application/x-www-form-urlencoded" : "application/json";
$.ajax({
url : getUrl(model),
type : type,
data : data,
dataType : 'json',
success : success,
error : error
url : getUrl(model),
type : type,
data : data,
processData : processData,
contentType : contentType,
dataType : 'json',
success : success,
error : error
});
};

View File

@@ -1232,10 +1232,10 @@ var othello = NYPL.create({
<p>
With the default implementation, when <b>Backbone.sync</b> sends up a request to save
a model, its attributes will be passed, serialized as JSON, under the <tt>model</tt>
parameter. When returning a JSON response, send down the attributes of the
model that have been changed by the server, and need to be updated on the
client. When responding to a <tt>"read"</tt> request from a collection
a model, its attributes will be passed, serialized as JSON, and sent in the HTTP body
with content-type <tt>application/json</tt>. When returning a JSON response,
send down the attributes of the model that have been changed by the server, and need
to be updated on the client. When responding to a <tt>"read"</tt> request from a collection
(<a href="#Collection#fetch">Collection#fetch</a>), send down an array
of model attribute objects.
</p>
@@ -1255,15 +1255,26 @@ var othello = NYPL.create({
If your web server makes it difficult to work with real <tt>PUT</tt> and
<tt>DELETE</tt> requests, you may choose to emulate them instead, using
HTTP <tt>POST</tt>, and passing them under the <tt>_method</tt> parameter
instead, by turning on <tt>Backbone.emulateHttp</tt>:
instead, by turning on <tt>Backbone.emulateHTTP</tt>:
</p>
<pre>
Backbone.emulateHttp = true;
Backbone.emulateHTTP = true;
model.save(); // Sends a POST to "/collection/id", with "_method=PUT"
</pre>
<p>
Also you can choose to send the body with content-type
<tt>application/x-www-form-urlencoded</tt> and the model in a JSON
string param called <tt>model</tt> by turning on <tt>Backbone.emulateJSON</tt>:
</p>
<pre>
Backbone.emulateJSON = true;
model.save(); //sends data as application/x-www-form-urlencoded
</pre>
<p>
As an example, a Rails handler responding to an <tt>"update"</tt> call from
<b>Backbone.sync</b> might look like this: <i>(In real code, never use
@@ -1557,7 +1568,7 @@ var DocumentView = Backbone.View.extend({
as an option, which will be invoked if validation fails, overriding the
<tt>"error"</tt> event.
You can now tell backbone to use the <tt>_method</tt> hack instead of HTTP
methods by setting <tt>Backbone.emulateHttp = true</tt>.
methods by setting <tt>Backbone.emulateHTTP = true</tt>.
Existing Model and Collection data is no longer sent up unnecessarily with
<tt>GET</tt> and <tt>DELETE</tt> requests. Added a <tt>rake lint</tt> task.
Backbone is now published as an <a href="http://npmjs.org">NPM</a> module.

View File

@@ -36,10 +36,23 @@ $(document).ready(function() {
equals(lastRequest.url, '/library');
equals(lastRequest.type, 'POST');
equals(lastRequest.dataType, 'json');
var data = JSON.parse(lastRequest.data);
equals(data.title, 'The Tempest');
equals(data.author, 'Bill Shakespeare');
equals(data.length, 123);
});
test("sync: create with emulateJSON", function() {
Backbone.emulateJSON = true;
library.add(library.create(attrs));
equals(lastRequest.url, '/library');
equals(lastRequest.type, 'POST');
equals(lastRequest.dataType, 'json');
var data = JSON.parse(lastRequest.data.model);
equals(data.title, 'The Tempest');
equals(data.author, 'Bill Shakespeare');
equals(data.length, 123);
Backbone.emulateJSON = false;
});
test("sync: update", function() {
@@ -47,25 +60,53 @@ $(document).ready(function() {
equals(lastRequest.url, '/library/1-the-tempest');
equals(lastRequest.type, 'PUT');
equals(lastRequest.dataType, 'json');
var data = JSON.parse(lastRequest.data.model);
var data = JSON.parse(lastRequest.data);
equals(data.id, '1-the-tempest');
equals(data.title, 'The Tempest');
equals(data.author, 'William Shakespeare');
equals(data.length, 123);
});
test("sync: update with emulateHttp", function() {
Backbone.emulateHttp = true;
test("sync: update with emulateHTTP", function() {
Backbone.emulateHTTP = true;
library.first().save({id: '2-the-tempest', author: 'Tim Shakespeare'});
equals(lastRequest.url, '/library/2-the-tempest');
equals(lastRequest.type, 'POST');
equals(lastRequest.dataType, 'json');
equals(lastRequest.data._method, 'PUT');
Backbone.emulateHTTP = false;
});
test("sync: update with emulateJSON", function() {
Backbone.emulateJSON = true;
library.first().save({id: '2-the-tempest', author: 'Tim Shakespeare'});
equals(lastRequest.url, '/library/2-the-tempest');
equals(lastRequest.type, 'PUT');
var data = JSON.parse(lastRequest.data.model);
equals(lastRequest.dataType, 'json');
equals(data.id, '2-the-tempest');
equals(data.title, 'The Tempest');
equals(data.author, 'Tim Shakespeare');
equals(data.length, 123);
Backbone.emulateJSON = false;
});
test("sync: update with emulateHTTP and emulateJSON", function() {
Backbone.emulateHTTP = true;
Backbone.emulateJSON = true;
library.first().save({id: '2-the-tempest', author: 'Tim Shakespeare'});
equals(lastRequest.url, '/library/2-the-tempest');
equals(lastRequest.type, 'POST');
equals(lastRequest.dataType, 'json');
equals(lastRequest.data._method, 'PUT');
var data = JSON.parse(lastRequest.data.model);
equals(lastRequest.dataType, 'json');
equals(data.id, '2-the-tempest');
equals(data.title, 'The Tempest');
equals(data.author, 'Tim Shakespeare');
equals(data.length, 123);
Backbone.emulateHTTP = false;
Backbone.emulateJSON = false;
});
test("sync: read model", function() {
@@ -76,19 +117,26 @@ $(document).ready(function() {
});
test("sync: destroy", function() {
Backbone.emulateHttp = false;
library.first().destroy();
equals(lastRequest.url, '/library/2-the-tempest');
equals(lastRequest.type, 'DELETE');
ok(_.isEmpty(lastRequest.data));
});
test("sync: destroy with emulateHttp", function() {
Backbone.emulateHttp = true;
test("sync: destroy with emulateJSON", function() {
library.first().destroy();
equals(lastRequest.url, '/library/2-the-tempest');
equals(lastRequest.type, 'DELETE');
ok(_.isEmpty(lastRequest.data));
});
test("sync: destroy with emulateHTTP", function() {
Backbone.emulateHTTP = true;
library.first().destroy();
equals(lastRequest.url, '/library/2-the-tempest');
equals(lastRequest.type, 'POST');
equals(JSON.stringify(lastRequest.data), '{"_method":"DELETE"}');
Backbone.emulateHTTP = false;
});
});