Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 22 additions & 1 deletion src/ParseObject.js
Original file line number Diff line number Diff line change
Expand Up @@ -775,7 +775,7 @@ export default class ParseObject {
}

/**
* Creates a new model with identical attributes to this one.
* Creates a new model with identical attributes to this one, similar to Backbone.Model's clone()
* @method clone
* @return {Parse.Object}
*/
Expand Down Expand Up @@ -803,6 +803,27 @@ export default class ParseObject {
return clone;
}

/**
* Creates a new instance of this object. Not to be confused with clone()
* @method newInstance
* @return {Parse.Object}
*/
newInstance(): any {
let clone = new this.constructor();
if (!clone.className) {
clone.className = this.className;
}
clone.id = this.id;
if (singleInstance) {
// Just return an object with the right id
return clone;
}

let stateController = CoreManager.getObjectStateController();
stateController.duplicateState(this._getStateIdentifier(), clone._getStateIdentifier());
return clone;
}

/**
* Returns true if this object has never been saved to Parse.
* @method isNew
Expand Down
17 changes: 17 additions & 0 deletions src/UniqueInstanceStateController.js
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,23 @@ export function enqueueTask(obj: ParseObject, task: () => ParsePromise): ParsePr
return state.tasks.enqueue(task);
}

export function duplicateState(source: ParseObject, dest: ParseObject): void {
let oldState = initializeState(source);
let newState = initializeState(dest);
for (let key in oldState.serverData) {
newState.serverData[key] = oldState.serverData[key];
}
for (let index = 0; index < oldState.pendingOps.length; index++) {
for (let key in oldState.pendingOps[index]) {
newState.pendingOps[index][key] = oldState.pendingOps[index][key];
}
}
for (let key in oldState.objectCache) {
newState.objectCache[key] = oldState.objectCache[key];
}
newState.existed = oldState.existed;
}

export function clearAllState() {
objectState = new WeakMap();
}
29 changes: 29 additions & 0 deletions src/__tests__/ParseObject-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1720,6 +1720,20 @@ describe('ObjectController', () => {

xhrs[0].onreadystatechange();
});

it('can create a new instance of an object', () => {
let o = ParseObject.fromJSON({
className: 'Clone',
objectId: 'C12',
});
let o2 = o.newInstance();
expect(o.id).toBe(o2.id);
expect(o.className).toBe(o2.className);
o.set({ valid: true });
expect(o2.get('valid')).toBe(true);

expect(o).not.toBe(o2);
});
});

describe('ParseObject (unique instance mode)', () => {
Expand Down Expand Up @@ -1902,6 +1916,21 @@ describe('ParseObject (unique instance mode)', () => {
expect(o.has('name')).toBe(false);
expect(o2.has('name')).toBe(true);
});

it('can create a new instance of an object', () => {
let o = ParseObject.fromJSON({
className: 'Clone',
objectId: 'C14',
});
let o2 = o.newInstance();
expect(o.id).toBe(o2.id);
expect(o.className).toBe(o2.className);
expect(o).not.toBe(o2);
o.set({ valid: true });
expect(o2.get('valid')).toBe(undefined);
o2 = o.newInstance();
expect(o2.get('valid')).toBe(true);
});
});

class MyObject extends ParseObject {
Expand Down
22 changes: 22 additions & 0 deletions src/__tests__/ParseUser-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,28 @@ describe('ParseUser', () => {
expect(clone.get('sessionToken')).toBe(undefined);
});

it('can create a new instance of a User', () => {
ParseObject.disableSingleInstance();
let o = ParseObject.fromJSON({
className: '_User',
objectId: 'U111',
username: 'u111',
email: 'u111@parse.com',
sesionToken: '1313'
});
let o2 = o.newInstance();
expect(o.id).toBe(o2.id);
expect(o.className).toBe(o2.className);
expect(o.get('username')).toBe(o2.get('username'));
expect(o.get('sessionToken')).toBe(o2.get('sessionToken'));
expect(o).not.toBe(o2);
o.set({ admin: true });
expect(o2.get('admin')).toBe(undefined);
o2 = o.newInstance();
expect(o2.get('admin')).toBe(true);
ParseObject.enableSingleInstance();
});

it('makes session tokens readonly', () => {
var u = new ParseUser();
expect(u.set.bind(u, 'sessionToken', 'token')).toThrow(
Expand Down
61 changes: 61 additions & 0 deletions src/__tests__/UniqueInstanceStateController-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -420,4 +420,65 @@ describe('UniqueInstanceStateController', () => {
closure();
}
});

it('can duplicate the state of an object', () => {
let obj = new ParseObject();
UniqueInstanceStateController.setServerData(obj, { counter: 12, name: 'original' });
let setCount = new ParseOps.SetOp(44);
let setValid = new ParseOps.SetOp(true);
UniqueInstanceStateController.setPendingOp(obj, 'counter', setCount);
UniqueInstanceStateController.setPendingOp(obj, 'valid', setValid);

let duplicate = new ParseObject();
UniqueInstanceStateController.duplicateState(obj, duplicate);
expect(UniqueInstanceStateController.getState(duplicate)).toEqual({
serverData: { counter: 12, name: 'original' },
pendingOps: [{ counter: setCount, valid: setValid }],
objectCache: {},
tasks: new TaskQueue(),
existed: false
});

UniqueInstanceStateController.setServerData(duplicate, { name: 'duplicate' });
expect(UniqueInstanceStateController.getState(obj)).toEqual({
serverData: { counter: 12, name: 'original' },
pendingOps: [{ counter: setCount, valid: setValid }],
objectCache: {},
tasks: new TaskQueue(),
existed: false
});
expect(UniqueInstanceStateController.getState(duplicate)).toEqual({
serverData: { counter: 12, name: 'duplicate' },
pendingOps: [{ counter: setCount, valid: setValid }],
objectCache: {},
tasks: new TaskQueue(),
existed: false
});

UniqueInstanceStateController.commitServerChanges(obj, { o: { a: 12 } });
expect(UniqueInstanceStateController.getState(obj)).toEqual({
serverData: { counter: 12, name: 'original', o: { a: 12 } },
pendingOps: [{ counter: setCount, valid: setValid }],
objectCache: { o: '{"a":12}' },
tasks: new TaskQueue(),
existed: false
});
expect(UniqueInstanceStateController.getState(duplicate)).toEqual({
serverData: { counter: 12, name: 'duplicate' },
pendingOps: [{ counter: setCount, valid: setValid }],
objectCache: {},
tasks: new TaskQueue(),
existed: false
});

let otherDup = new ParseObject();
UniqueInstanceStateController.duplicateState(obj, otherDup);
expect(UniqueInstanceStateController.getState(otherDup)).toEqual({
serverData: { counter: 12, name: 'original', o: { a: 12 } },
pendingOps: [{ counter: setCount, valid: setValid }],
objectCache: { o: '{"a":12}' },
tasks: new TaskQueue(),
existed: false
});
});
});