From 796ab7531bb7e6ee393c153f231d651cee0d1403 Mon Sep 17 00:00:00 2001 From: Jarrett Colby Date: Fri, 27 Feb 2015 17:27:54 -0500 Subject: [PATCH] Fix #188 --- .../assets/javascripts/backbone_rails_sync.js | 70 ++++++++++++++----- 1 file changed, 53 insertions(+), 17 deletions(-) diff --git a/vendor/assets/javascripts/backbone_rails_sync.js b/vendor/assets/javascripts/backbone_rails_sync.js index 359cb49..343471a 100644 --- a/vendor/assets/javascripts/backbone_rails_sync.js +++ b/vendor/assets/javascripts/backbone_rails_sync.js @@ -1,32 +1,68 @@ -(function($) { - Backbone._sync = Backbone.sync; +(function() { + var origSync = Backbone.sync; + // Define a patched version of Backbone.Model.toJSON that takes into account paramRoot. + // Note that toJSON is not called when you call save with {patch: true}. So we handle + // that case elsewhere. + function wrapToJSON(origToJSON) { + return function(options) { + // This is the attributes hash formatted in the Backbone standard way. + var unwrapped = origToJSON.apply(this, [options]); + if (this.paramRoot) { + // For this model, we want to wrap the attributes hash in a parameter name, as + // expected by Rails. + var json = {}; + json[this.paramRoot] = unwrapped; + return json; + } else { + // This model doesn't define paramRoot, so we stick with the default + // Backbone behavior. + return unwrapped; + } + } + } + + + // Patch Backbone.sync so that it 1) uses the csrf-token, and 2) uses our patched + // version of toJSON. Backbone.sync = function(method, model, options) { if (!options.noCSRF) { var beforeSend = options.beforeSend; // Set X-CSRF-Token HTTP header options.beforeSend = function(xhr) { - var token = $('meta[name="csrf-token"]').attr('content'); + var token = jQuery('meta[name="csrf-token"]').attr('content'); if (token) xhr.setRequestHeader('X-CSRF-Token', token); if (beforeSend) return beforeSend.apply(this, arguments); }; } - - // Serialize data, optionally using paramRoot - if (options.data == null && model && (method === 'create' || method === 'update' || method === 'patch')) { - options.contentType = 'application/json'; - data = JSON.stringify(options.attrs || model.toJSON(options)); + + if (options.attrs) { + // sync wasn't called with options.attrs. So Model.save was called with + // {patch: true}. if (model.paramRoot) { - data = {}; - data[model.paramRoot] = model.toJSON(options); - } else { - data = model.toJSON(); + // Wrap options.attrs in paramRoot and pass control to the original Backbone.sync. + var wrapped = {}; + wrapped[model.paramRoot] = options.attrs; + options.attrs = wrapped; + } + return origSync(method, model, options); + } else { + // sync wasn't called with options.attrs. So Model.save wasn't called with + // {patch: true}. + // + // Temporarily patch Backbone.Model.toJSON so it takes into account paramRoot. + // Restore the original version of toJSON after we're done. + var ret; + try { + var origToJSON = model.toJSON; + model.toJSON = wrapToJSON(origToJSON); + ret = origSync(method, model, options); + } finally { + model.toJSON = origToJSON; } - options.data = JSON.stringify(data); } - - return Backbone._sync(method, model, options); + + return ret; }; - -})(jQuery); \ No newline at end of file +})(); \ No newline at end of file