Skip to content

Commit 5ab93d2

Browse files
committed
Merge pull request #284 from davincho/temporary-entites
Support for temporary items
2 parents 5d5777d + 29aef0d commit 5ab93d2

File tree

10 files changed

+137
-3
lines changed

10 files changed

+137
-3
lines changed

src/datastore/async_methods/create.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ module.exports = function create (resourceName, attrs, options) {
2020
let _this = this
2121
let DSUtils = _this.utils
2222
let definition = _this.definitions[resourceName]
23+
let resource = _this.store[resourceName]
2324
let adapter
2425

2526
options = options || {}
@@ -32,7 +33,7 @@ module.exports = function create (resourceName, attrs, options) {
3233
rejectionError = DSUtils._oErr('attrs')
3334
} else {
3435
options = DSUtils._(definition, options)
35-
if (options.upsert && DSUtils._sn(attrs[definition.idAttribute])) {
36+
if (options.upsert && DSUtils._sn(attrs[definition.idAttribute]) && !resource.temporaryItems[attrs[definition.idAttribute]]) {
3637
return _this.update(resourceName, attrs[definition.idAttribute], attrs, options)
3738
}
3839
options.logFn('create', attrs, options)

src/datastore/index.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,8 @@ function like (pattern, flags) {
155155
}
156156

157157
defaultsPrototype.defaultFilter = function (collection, resourceName, params, options) {
158+
let idA = this.definitions[resourceName].idAttribute
159+
let resource = this.store[resourceName]
158160
let filtered = collection
159161
let where = null
160162
let reserved = {
@@ -193,6 +195,11 @@ defaultsPrototype.defaultFilter = function (collection, resourceName, params, op
193195
filtered = DSUtils.filter(filtered, function (attrs) {
194196
let first = true
195197
let keep = true
198+
199+
if (options.excludeTemporary && resource.temporaryItems[attrs[idA]]) {
200+
return false
201+
}
202+
196203
DSUtils.forOwn(where, function (clause, field) {
197204
if (!DSUtils._o(clause)) {
198205
clause = {
@@ -259,8 +266,13 @@ defaultsPrototype.defaultFilter = function (collection, resourceName, params, op
259266
first = false
260267
})
261268
})
269+
262270
return keep
263271
})
272+
} else if (options.excludeTemporary) {
273+
filtered = DSUtils.filter(filtered, function (attrs) {
274+
return resource.temporaryItems[attrs[idA]]
275+
})
264276
}
265277

266278
let orderBy = null

src/datastore/sync_methods/defineResource.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ let instanceMethods = [
2525
'changes',
2626
'commit',
2727
'hasChanges',
28+
'isNew',
2829
'lastModified',
2930
'lastSaved',
3031
'previous',
@@ -269,7 +270,8 @@ module.exports = function defineResource (definition) {
269270
observers: {},
270271
changeHistories: {},
271272
changeHistory: [],
272-
collectionModified: 0
273+
collectionModified: 0,
274+
temporaryItems: {}
273275
}
274276

275277
let resource = _this.store[def.name]

src/datastore/sync_methods/index.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,18 @@ export default {
330330
},
331331
inject: require('./inject'),
332332

333+
// Return whether the item with the given primary key is a temporary item.
334+
//
335+
// @param resourceName The name of the type of resource of the item.
336+
// @param id The primary key of the item.
337+
// @returns Whether the item with the given primary key is a temporary item.
338+
isNew (resourceName, id) {
339+
let {_this, _resourceName, _id} = check.call(this, 'isNew', resourceName, id || fakeId)
340+
let resource = _this.store[_resourceName]
341+
342+
return !!resource.temporaryItems[_id]
343+
},
344+
333345
// Return the timestamp from the last time the item with the given primary key was changed.
334346
//
335347
// @param resourceName The name of the type of resource of the item.

src/datastore/sync_methods/inject.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,8 @@ function _inject (definition, resource, attrs, options) {
134134
args.push(attrs[dep])
135135
})
136136
attrs[idA] = c[idA][c[idA].length - 1].apply(attrs, args)
137+
} else if (options.temporary) {
138+
attrs[idA] = DSUtils.guid()
137139
}
138140

139141
if (!(idA in attrs)) {
@@ -234,6 +236,10 @@ function _inject (definition, resource, attrs, options) {
234236
_react.call(item, {}, {}, {}, null, true)
235237
// save "previous" attributes of the injected item, for change diffs later
236238
resource.previousAttributes[id] = DSUtils.copy(item, null, null, null, definition.relationFields)
239+
// mark item as temporary if guid has been generated
240+
if (options.temporary) {
241+
resource.temporaryItems[id] = true
242+
}
237243
} else {
238244
// item is being re-injected
239245
// new properties take precedence

src/utils.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ let upperCase = require('mout/string/upperCase')
1919
let get = require('mout/object/get')
2020
let set = require('mout/object/set')
2121
let observe = require('../lib/observe-js/observe.js')
22+
let guid = require('mout/random/guid')
2223
let w, P, File
2324
let objectProto = Object.prototype
2425
let toString = objectProto.toString
@@ -498,6 +499,7 @@ export default {
498499
return isString(json) ? JSON.parse(json) : json
499500
},
500501
get,
502+
guid,
501503
intersection,
502504
isArray,
503505
isBlacklisted,

test/both/datastore/sync_methods/filter.test.js

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -564,5 +564,16 @@ describe('DS#filter', function () {
564564
assert.deepEqual(User.filter({ where: { name: { notLike: 'foo%foo' } } }), [users[0], users[1], users[2], users[3], users[4], users[5], users[6], users[9]]);
565565
assert.deepEqual(User.filter({ where: { name: { notLike: 'foo_foo' } } }), [users[0], users[1], users[2], users[3], users[4], users[5], users[6], users[7], users[9]]);
566566
assert.deepEqual(User.filter({ where: { name: { notLike: 'foo%foo_' } } }), [users[0], users[1], users[2], users[3], users[4], users[5], users[6], users[7], users[8]]);
567-
})
567+
});
568+
it('should include temporary items when `excludeTemporary` is false', function() {
569+
var user1 = User.inject({ name: 'foo' }, {temporary: true});
570+
var user2 = User.inject({ id: 2, name: 'foo' });
571+
572+
assert(User.filter().length == 2);
573+
assert(User.filter(null, {excludeTemporary: false}).length == 2);
574+
assert(User.filter(null, {excludeTemporary: true}).length == 1);
575+
576+
assert(User.filter({name: 'foo'}, {excludeTemporary: true}).length == 1);
577+
assert(User.filter({name: 'foo'}, {excludeTemporary: false}).length == 2);
578+
});
568579
});

test/both/datastore/sync_methods/inject.test.js

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -487,4 +487,53 @@ describe('DS#inject', function () {
487487
], 'bars should have been injected, but not linked');
488488
assert.equal(Bar.getAll().length, 3, '3 bars should be in the store');
489489
});
490+
it('should inject temporary item', function() {
491+
var Foo = store.defineResource({
492+
name: 'foo',
493+
idAttribute: 'foo_id'
494+
});
495+
496+
var foo = Foo.inject({bar: 'bar'}, {temporary: true});
497+
498+
assert(foo.foo_id != null);
499+
});
500+
it('should replace temporary item with real item from server, conserving any relations to other items', function() {
501+
delete user1.id;
502+
503+
// Create item and related items referencing temporary ID
504+
var user = store.inject('user', user1, {temporary: true});
505+
var comment = {
506+
id: 2,
507+
approvedBy: user.id,
508+
content: 'test comment 2'
509+
};
510+
var profile = {
511+
author: 'John',
512+
age: 30,
513+
id: 5,
514+
userId: user.id
515+
};
516+
517+
// belongsTo relation
518+
store.inject('organization', organization2);
519+
assert.deepEqual(JSON.stringify(user.organization), JSON.stringify(organization2));
520+
521+
// hasMany relation
522+
store.inject('comment', comment);
523+
assert.deepEqual(JSON.stringify(user.comments[0]), JSON.stringify(comment));
524+
525+
// hasOne relation
526+
store.inject('profile', profile);
527+
assert.deepEqual(JSON.stringify(user.profile), JSON.stringify(profile));
528+
529+
// Inject item with real ID, replacing guid
530+
var guid = user.id;
531+
profile.userId = comment.approvedBy = user.id = 10;
532+
user = store.inject('user', user, {replacingId: guid});
533+
534+
assert.deepEqual(JSON.stringify(user.organization), JSON.stringify(organization2));
535+
assert.deepEqual(JSON.stringify(user.comments[0]), JSON.stringify(comment));
536+
assert.deepEqual(JSON.stringify(user.profile), JSON.stringify(profile));
537+
538+
});
490539
});
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
describe('DS#isNew', function () {
2+
it('should return true for temporary instances', function () {
3+
var user = store.inject('user', {
4+
foo: 'foo'
5+
}, {
6+
temporary: true
7+
});
8+
9+
assert.ok(user.DSIsNew());
10+
});
11+
it('should return false for `real` instances', function() {
12+
var user = store.inject('user', user1);
13+
assert.notOk(user.DSIsNew());
14+
});
15+
});

test/browser/datastore/async_methods/create.test.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,4 +270,28 @@ describe('DS#create', function () {
270270
assert.deepEqual(JSON.stringify(Foo.get(1)), JSON.stringify({ first: 'John', last: 'Anderson', id: 1, fullName: 'John Anderson' }));
271271
});
272272
});
273+
it('should create an temporary item with guid', function () {
274+
var Foo = store.defineResource({
275+
name: 'foo',
276+
idAttribute: 'foo_id'
277+
});
278+
279+
var _this = this;
280+
var foo = Foo.inject({bar: 'bar'}, {temporary: true});
281+
var data = JSON.stringify(foo);
282+
283+
setTimeout(function () {
284+
assert.equal(1, _this.requests.length);
285+
assert.equal(_this.requests[0].url, 'http://test.js-data.io/foo');
286+
assert.equal(_this.requests[0].method, 'POST');
287+
assert.equal(_this.requests[0].requestBody, data);
288+
_this.requests[0].respond(200, { 'Content-Type': 'application/json' }, DSUtils.toJson({ name: 'foo', foo_id: 2 }));
289+
}, 100);
290+
291+
return Foo.create(foo).then(function (f) {
292+
assert.deepEqual(JSON.stringify(f), JSON.stringify({ name: 'foo', foo_id: 2 }), 'foo 1 should have been created');
293+
assert.deepEqual(JSON.stringify(Foo.get(2)), JSON.stringify({ name: 'foo', foo_id: 2}));
294+
assert(Foo.get(foo.foo_id) == null, 'Temporary ID should have been overwritten');
295+
});
296+
});
273297
});

0 commit comments

Comments
 (0)