diff --git a/backbone.js b/backbone.js index 5c863272f..152f327f5 100644 --- a/backbone.js +++ b/backbone.js @@ -538,7 +538,7 @@ // If the server returns an attributes hash that differs, the model's // state will be `set` again. save: function(key, val, options) { - var attrs, method, xhr, attributes = this.attributes; + var attrs, method, xhr, attributes = this.attributes, wait; // Handle both `"key", value` and `{key: value}` -style arguments. if (key == null || typeof key === 'object') { @@ -549,18 +549,19 @@ } options = _.extend({validate: true}, options); + wait = options.wait; // If we're not waiting and attributes exist, save acts as // `set(attr).save(null, opts)` with validation. Otherwise, check if // the model will be valid when the attributes, if any, are set. - if (attrs && !options.wait) { + if (attrs && !wait) { if (!this.set(attrs, options)) return false; } else { if (!this._validate(attrs, options)) return false; } // Set temporary attributes if `{wait: true}`. - if (attrs && options.wait) { + if (attrs && wait) { this.attributes = _.extend({}, attributes, attrs); } @@ -573,7 +574,7 @@ // Ensure attributes are restored during synchronous saves. model.attributes = attributes; var serverAttrs = model.parse(resp, options); - if (options.wait) serverAttrs = _.extend(attrs || {}, serverAttrs); + if (wait) serverAttrs = _.extend(attrs || {}, serverAttrs); if (_.isObject(serverAttrs) && !model.set(serverAttrs, options)) { return false; } @@ -587,7 +588,7 @@ xhr = this.sync(method, this, options); // Restore attributes. - if (attrs && options.wait) this.attributes = attributes; + if (attrs && wait) this.attributes = attributes; return xhr; }, @@ -599,6 +600,7 @@ options = options ? _.clone(options) : {}; var model = this; var success = options.success; + var wait = options.wait; var destroy = function() { model.stopListening(); @@ -606,7 +608,7 @@ }; options.success = function(resp) { - if (options.wait || model.isNew()) destroy(); + if (wait || model.isNew()) destroy(); if (success) success.call(options.context, model, resp, options); if (!model.isNew()) model.trigger('sync', model, resp, options); }; @@ -618,7 +620,7 @@ wrapError(this, options); var xhr = this.sync('delete', this, options); - if (!options.wait) destroy(); + if (!wait) destroy(); return xhr; }, @@ -984,7 +986,7 @@ var collection = this; var success = options.success; options.success = function(model, resp, callbackOptions) { - if (options.wait) collection.add(model, callbackOptions); + if (wait) collection.add(model, callbackOptions); if (success) success.call(callbackOptions.context, model, resp, callbackOptions); }; model.save(null, options); diff --git a/test/collection.js b/test/collection.js index 3b6e7f657..00080ae11 100644 --- a/test/collection.js +++ b/test/collection.js @@ -1221,6 +1221,25 @@ Backbone.ajax = ajax; }); + test("fetch will pass extra options to success callback", 1, function () { + var SpecialSyncCollection = Backbone.Collection.extend({ + url: '/test', + sync: function (method, collection, options) { + _.extend(options, { specialSync: true }); + return Backbone.Collection.prototype.sync.call(this, method, collection, options); + } + }); + + var collection = new SpecialSyncCollection(); + + collection.fetch({ success: onSuccess }); + this.ajaxSettings.success(); + + function onSuccess(collection, resp, options) { + ok(options.specialSync, "Options were passed correctly to callback"); + } + }); + test("`add` only `sort`s when necessary", 2, function () { var collection = new (Backbone.Collection.extend({ comparator: 'a' diff --git a/test/model.js b/test/model.js index 7b30a2a07..eca518592 100644 --- a/test/model.js +++ b/test/model.js @@ -565,12 +565,50 @@ equal(this.ajaxSettings.url, '/collection/42'); }); + test("save will pass extra options to success callback", 1, function () { + var SpecialSyncModel = Backbone.Model.extend({ + sync: function (method, model, options) { + _.extend(options, { specialSync: true }); + return Backbone.Model.prototype.sync.call(this, method, model, options); + }, + urlRoot: '/test' + }); + + var model = new SpecialSyncModel(); + + model.save(null, { success: onSuccess }); + this.ajaxSettings.success(); + + function onSuccess(model, response, options) { + ok(options.specialSync, "Options were passed correctly to callback"); + } + }); + test("fetch", 2, function() { doc.fetch(); equal(this.syncArgs.method, 'read'); ok(_.isEqual(this.syncArgs.model, doc)); }); + test("fetch will pass extra options to success callback", 1, function () { + var SpecialSyncModel = Backbone.Model.extend({ + sync: function (method, model, options) { + _.extend(options, { specialSync: true }); + return Backbone.Model.prototype.sync.call(this, method, model, options); + }, + urlRoot: '/test' + }); + + var model = new SpecialSyncModel(); + + model.fetch({ success: onSuccess }); + this.ajaxSettings.success(); + + function onSuccess(model, response, options) { + ok(options.specialSync, "Options were passed correctly to callback"); + } + }); + test("destroy", 3, function() { doc.destroy(); equal(this.syncArgs.method, 'delete'); @@ -580,6 +618,25 @@ equal(newModel.destroy(), false); }); + test("destroy will pass extra options to success callback", 1, function () { + var SpecialSyncModel = Backbone.Model.extend({ + sync: function (method, model, options) { + _.extend(options, { specialSync: true }); + return Backbone.Model.prototype.sync.call(this, method, model, options); + }, + urlRoot: '/test' + }); + + var model = new SpecialSyncModel({ id: 'id' }); + + model.destroy({ success: onSuccess }); + this.ajaxSettings.success(); + + function onSuccess(model, response, options) { + ok(options.specialSync, "Options were passed correctly to callback"); + } + }); + test("non-persisted destroy", 1, function() { var a = new Backbone.Model({ 'foo': 1, 'bar': 2, 'baz': 3}); a.sync = function() { throw "should not be called"; };