-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbackbone.cache.js
More file actions
179 lines (156 loc) · 6.77 KB
/
backbone.cache.js
File metadata and controls
179 lines (156 loc) · 6.77 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
import Store from 'react-native-simple-store';
import _ from 'underscore';
import async from 'async';
module.exports = function(Backbone) {
Backbone.Collection.prototype.prefixedCacheKey =
Backbone.Model.prototype.prefixedCacheKey = function() {
// cache key is based on a prefix passed to enableCache, then falls back to
//a global setting on the Backbone object, and then a predefined string
var prefix = Backbone.CACHE_PREFIX || "backbone-cache";
if (!this.cacheKey) throw new Error('cacheKey function or property is required');
var key = prefix + _.result(this, 'cacheKey');
if (!key) throw new Error('invalid cache key');
return key;
};
// call this method on a model that should be cached. it's job
// is a listen for changes on the model and keep the cache up to
// date with the latest version
Backbone.Model.prototype.enableCache = function() {
if (!this.cacheKey) throw new Error('cacheKey function or property is required');
if (this._cache_enabled) return;
this._cache_enabled = true;
// update cached version of the model any time it's synced from
// the server, delaying callback until cache write is completed
var _fetch = this.fetch;
this.fetch = function(options) {
if (!options) options = {};
var _success = options.success;
options.success = function() {
var args = arguments;
this.cache(function() {
if (_success) _success.apply(this, args);
}.bind(this));
}.bind(this);
return _fetch.call(this, options);
}.bind(this);
var _save = this.save;
this.save = function(key, val, options) {
// Handle both `"key", value` and `{key: value}` -style arguments.
var attrs;
if (key == null || typeof key === 'object') {
attrs = key;
options = val;
} else (attrs = {})[key] = val;
if (!options) options = {};
var _success = options.success;
options.success = function() {
var args = arguments;
this.cache(function() {
if (_success) _success.apply(this, args);
}.bind(this));
}.bind(this);
return _save.call(this, attrs, options);
}.bind(this);
var _destroy = this.destroy;
this.destroy = function(options) {
if (!options) options = {};
var _success = options.success;
options.success = function() {
var args = arguments;
this.evictFromCache(function() {
if (_success) _success.apply(this, args);
}.bind(this));
}.bind(this);
return _destroy.call(this, options);
}.bind(this);
// catch models being removed on sync from collection too
this.on('destroy', function() {
this.evictFromCache();
}.bind(this), this);
};
// a method to actually do the cache writing
Backbone.Model.prototype.cache = function(callback) {
var toCache = [[this.prefixedCacheKey(), this.toJSON()]];
// if this model is also in a collection then make sure the list of
// ids that make up the collection is also up to date
if (this.collection && this.collection._cache_enabled) {
toCache.push([this.collection.prefixedCacheKey(), _.uniq(_.compact(this.collection.models.map(function(model) {
if (model.isNew()) return false;
return model.prefixedCacheKey();
})))]);
}
Store.save(toCache).then(callback);
};
Backbone.Model.prototype.evictFromCache = function(callback) {
this.off(undefined, undefined, this);
return Store.delete(this.prefixedCacheKey()).then(callback);
};
Backbone.Model.prototype.restoreFromCache = function(callback) {
var model = this;
// load cached object and inject it into the backbone model
Store.get(this.prefixedCacheKey()).then(function(cached) {
if (cached) model.set(cached, { silent: false });
callback();
}).catch(callback);
};
Backbone.Collection.prototype.enableCache = function(options) {
if (!this.cacheKey) throw new Error('cacheKey function or property is required');
if (this._cache_enabled) return;
this._cache_enabled = true;
this.models.map(function(model){
model.enableCache();
});
this.on('add', function(model) {
model.enableCache();
});
var _fetch = this.fetch;
this.fetch = function(options) {
if (!options) options = {};
var _success = options.success;
options.success = function() {
var args = arguments;
this.cache(function() {
if (_success) _success.apply(this, args);
}.bind(this));
}.bind(this);
return _fetch.call(this, options);
}.bind(this);
};
Backbone.Collection.prototype.cache = function(callback) {
// generate the list of models to be saved as pairs with the
// key they should be cached under
var toCache = this.models.map(function(model){
return [ model.prefixedCacheKey(), model.toJSON() ];
});
// and add an entry for the collection which is the list
// of cache keys that are currently part of this collection
toCache.push([this.prefixedCacheKey(), _.uniq(_.compact(this.models.map(function(model) {
if (model.isNew()) return false;
return model.prefixedCacheKey();
})))]);
Store.save(toCache).then(function() {
callback();
}.bind(this));
};
Backbone.Collection.prototype.evictFromCache = function() {
this.each(function(m){ m.evictFromCache(); });
return Store.delete(this.prefixedCacheKey());
};
Backbone.Collection.prototype.restoreFromCache = function(callback) {
var collection = this;
// load saved list of model cachKey()'s, unpack into full models and inject
// into the backbone collection
Store.get(this.prefixedCacheKey()).then(function(cached) {
if (!cached) return callback();
// remove any obviously invalid elements
cached = _.compact(cached);
Store.get(cached).then(function(values) {
var models = values.map(function(value) {
return new (collection.model)(value);
});
collection.set(models, { silent: false });
callback();
});
});
};
};