From 896379dbf04ab509a52a3b070fbe48b8b4b44e3e Mon Sep 17 00:00:00 2001 From: Brian Carstensen Date: Thu, 25 Jun 2015 13:49:08 -0500 Subject: [PATCH 1/2] Don't cache GETs for any longer than they're pending --- json-api-client.js | 21 ++++++++------------- src/make-http-request.coffee | 8 +++----- 2 files changed, 11 insertions(+), 18 deletions(-) diff --git a/json-api-client.js b/json-api-client.js index eeeb806..f5bd30b 100644 --- a/json-api-client.js +++ b/json-api-client.js @@ -362,7 +362,7 @@ Object.defineProperty(module.exports, 'util', { },{"./emitter":1,"./make-http-request":3,"./merge-into":4,"./model":5,"./resource":6,"./type":7}],3:[function(_dereq_,module,exports){ var CACHE_FOR, cachedGets; -CACHE_FOR = 1000; +CACHE_FOR = 0; cachedGets = {}; @@ -406,19 +406,14 @@ module.exports = function(method, url, data, headers, modify) { var ref; if (request.readyState === request.DONE) { if ((200 <= (ref = request.status) && ref < 300)) { - if (method === 'GET') { - setTimeout((function() { - return delete cachedGets[url]; - }), CACHE_FOR); - } - return resolve(request); + resolve(request); } else { - if (method === 'GET') { - setTimeout((function() { - return delete cachedGets[url]; - }), CACHE_FOR); - } - return reject(request); + reject(request); + } + if (method === 'GET') { + return setTimeout((function() { + return delete cachedGets[url]; + }), CACHE_FOR); } } }; diff --git a/src/make-http-request.coffee b/src/make-http-request.coffee index 6c6b42f..b5dd9cc 100644 --- a/src/make-http-request.coffee +++ b/src/make-http-request.coffee @@ -1,5 +1,5 @@ # This should be just long enough that near-simultaneous GETs don't make multiple requests. -CACHE_FOR = 1000 +CACHE_FOR = 0 cachedGets = {} @@ -35,13 +35,11 @@ module.exports = (method, url, data, headers, modify) -> request.onreadystatechange = (e) -> if request.readyState is request.DONE if 200 <= request.status < 300 - if method is 'GET' - setTimeout (-> delete cachedGets[url]), CACHE_FOR resolve request else - if method is 'GET' - setTimeout (-> delete cachedGets[url]), CACHE_FOR reject request + if method is 'GET' + setTimeout (-> delete cachedGets[url]), CACHE_FOR if data? and headers?['Content-Type']?.indexOf('json') isnt -1 data = JSON.stringify data From 863a8a394d330fad8513b907196f6ebd52453d59 Mon Sep 17 00:00:00 2001 From: Brian Carstensen Date: Thu, 25 Jun 2015 13:50:35 -0500 Subject: [PATCH 2/2] Track keys being saved and never overwrite them --- json-api-client.js | 40 +++++++++++++++++++++++++++++++--------- src/resource.coffee | 17 +++++++++++++++-- src/type.coffee | 12 +++++++----- 3 files changed, 53 insertions(+), 16 deletions(-) diff --git a/json-api-client.js b/json-api-client.js index f5bd30b..e8d8e5f 100644 --- a/json-api-client.js +++ b/json-api-client.js @@ -591,6 +591,8 @@ Resource = (function(superClass) { Resource.prototype._linksCache = null; + Resource.prototype._savingKeys = null; + Resource.prototype._write = Promise.resolve(); function Resource(_type) { @@ -601,6 +603,7 @@ Resource = (function(superClass) { this._headers = {}; this._meta = {}; this._linksCache = {}; + this._savingKeys = {}; Resource.__super__.constructor.call(this, null); this._type.emit('change'); this.emit('create'); @@ -627,9 +630,17 @@ Resource = (function(superClass) { }; Resource.prototype.save = function() { - var payload; + var base, changes, key, payload; payload = {}; - payload[this._type._name] = this.toJSON.call(this.getChangesSinceSave()); + changes = this.toJSON.call(this.getChangesSinceSave()); + payload[this._type._name] = changes; + this._changedKeys.splice(0); + for (key in changes) { + if ((base = this._savingKeys)[key] == null) { + base[key] = 0; + } + this._savingKeys[key] += 1; + } this._write = this._write["catch"]((function(_this) { return function() { return null; @@ -643,9 +654,14 @@ Resource = (function(superClass) { return new ResourcePromise(save.then(function(arg) { var result; result = arg[0]; + for (key in changes) { + _this._savingKeys[key] -= 1; + if (_this._savingKeys[key] === 0) { + delete _this._savingKeys[key]; + } + } if (result !== _this) { _this.update(result); - _this._changedKeys.splice(0); result.destroy(); } _this.emit('save'); @@ -927,6 +943,7 @@ module.exports.Promise = ResourcePromise; var Emitter, Resource, Type, mergeInto, extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, hasProp = {}.hasOwnProperty, + indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }, slice = [].slice; Emitter = _dereq_('./emitter'); @@ -960,7 +977,7 @@ module.exports = Type = (function(superClass) { } Type.prototype.create = function(data, headers, meta) { - var moreRecentChanges, ref, ref1, resource; + var key, ref, ref1, resource, value; if (data == null) { data = {}; } @@ -976,12 +993,17 @@ module.exports = Type = (function(superClass) { resource = (ref1 = this._resourcesCache[data.id]) != null ? ref1 : new this.Resource(this); mergeInto(resource._headers, headers); mergeInto(resource._meta, meta); - moreRecentChanges = resource.getChangesSinceSave(); - resource.update(data); - resource.update(moreRecentChanges); - if (resource === this._resourcesCache[data.id]) { - resource._changedKeys.splice(0); + if (data.id != null) { + for (key in data) { + value = data[key]; + if ((indexOf.call(resource._changedKeys, key) < 0) && (!(key in resource._savingKeys))) { + resource[key] = value; + } + } + this._resourcesCache[resource.id] = resource; resource.emit('change'); + } else { + resource.update(data); } return resource; } diff --git a/src/resource.coffee b/src/resource.coffee index 9242e0c..ba3f23a 100644 --- a/src/resource.coffee +++ b/src/resource.coffee @@ -8,6 +8,7 @@ class Resource extends Model _headers: null _meta: null _linksCache: null + _savingKeys: null _write: Promise.resolve() constructor: (@_type) -> @@ -16,6 +17,7 @@ class Resource extends Model @_headers = {} @_meta = {} @_linksCache = {} + @_savingKeys = {} super null @_type.emit 'change' @emit 'create' @@ -33,7 +35,14 @@ class Resource extends Model save: -> payload = {} - payload[@_type._name] = @toJSON.call @getChangesSinceSave() + changes = @toJSON.call @getChangesSinceSave() + payload[@_type._name] = changes + + @_changedKeys.splice 0 + + for key of changes + @_savingKeys[key] ?= 0 + @_savingKeys[key] += 1 @_write = @_write .catch => @@ -46,9 +55,13 @@ class Resource extends Model @_type._client.post @_type._getURL(), payload new ResourcePromise save.then ([result]) => + for key of changes + @_savingKeys[key] -= 1 + if @_savingKeys[key] is 0 + delete @_savingKeys[key] + unless result is this @update result - @_changedKeys.splice 0 result.destroy() @emit 'save' this diff --git a/src/type.coffee b/src/type.coffee index e57c82e..6f19533 100644 --- a/src/type.coffee +++ b/src/type.coffee @@ -26,12 +26,14 @@ module.exports = class Type extends Emitter resource = @_resourcesCache[data.id] ? new @Resource this mergeInto resource._headers, headers mergeInto resource._meta, meta - moreRecentChanges = resource.getChangesSinceSave() - resource.update data - resource.update moreRecentChanges - if resource is @_resourcesCache[data.id] - resource._changedKeys.splice 0 + + if data.id? + for key, value of data when (key not in resource._changedKeys) and (key not of resource._savingKeys) + resource[key] = value + @_resourcesCache[resource.id] = resource resource.emit 'change' + else + resource.update data resource get: ->