[Location] Use parent id as location

Use the parent id as the location for a model.

This greatly reduces the recursive work that must be done
during move operations to keep the location accurate.

Additionally, the locationService now implements a method
`persistLocation` which can be used to persist the current object
location as it's original location.
This commit is contained in:
Pete Richards
2015-08-06 14:41:32 -07:00
parent f083d019a3
commit f72f88adfa
6 changed files with 112 additions and 360 deletions

View File

@@ -3,66 +3,90 @@
define(
[
'../../src/capabilities/LocationCapability',
'../DomainObjectFactory'
'../DomainObjectFactory',
'../ControlledPromise'
],
function (LocationCapability, domainObjectFactory) {
function (LocationCapability, domainObjectFactory, ControlledPromise) {
describe("LocationCapability", function () {
// xit("applies to objects with a context capability", function () {
// var domainObject = domainObjectFactory({
// capabilities: {
// context: true
// }
// });
// expect(LocationCapability.appliesTo(domainObject)).toBe(true);
// });
//
// xit("does not apply to objects without context capability", function () {
// var domainObject = domainObjectFactory();
// expect(LocationCapability.appliesTo(domainObject)).toBe(false);
// });
describe("instantiated with domain object", function () {
var locationCapability,
persistencePromise,
mutationPromise,
domainObject;
beforeEach(function () {
domainObject = domainObjectFactory({
capabilities: {
context: {
getPath: function() {
return [
domainObjectFactory({id: 'root'}),
domainObjectFactory({id: 'parent'}),
domainObjectFactory({id: 'me'})
];
getParent: function() {
return domainObjectFactory({id: 'root'});
}
}
},
persistence: jasmine.createSpyObj(
'persistenceCapability',
['persist']
),
mutation: jasmine.createSpyObj(
'mutationCapability',
['invoke']
)
}
});
persistencePromise = new ControlledPromise();
domainObject.capabilities.persistence.persist.andReturn(
persistencePromise
);
mutationPromise = new ControlledPromise();
domainObject.capabilities.mutation.invoke.andCallFake(
function (mutator) {
return mutationPromise.then(function () {
mutator(domainObject.model);
});
}
);
locationCapability = new LocationCapability(domainObject);
});
it("returns location", function () {
expect(locationCapability.getLocation())
.toBe('root/parent/me');
.toBe('root');
});
it("knows when the object is an original", function () {
domainObject.model.location = 'root/parent/me';
domainObject.model.location = 'root';
expect(locationCapability.isOriginal()).toBe(true);
expect(locationCapability.isLink()).toBe(false);
});
it("knows when the object is a link.", function () {
domainObject.model.location = 'root/another/location/me';
domainObject.model.location = 'different-root';
expect(locationCapability.isLink()).toBe(true);
expect(locationCapability.isOriginal()).toBe(false);
});
it("can persist location", function () {
var persistResult = locationCapability.persistLocation(),
whenComplete = jasmine.createSpy('whenComplete');
persistResult.then(whenComplete);
expect(domainObject.model.location).not.toBeDefined();
mutationPromise.resolve();
expect(domainObject.model.location).toBe('root');
expect(whenComplete).not.toHaveBeenCalled();
expect(domainObject.capabilities.persistence.persist)
.toHaveBeenCalled();
persistencePromise.resolve();
expect(whenComplete).toHaveBeenCalled();
});
});
});
}

View File

@@ -149,9 +149,7 @@ define(
newParent,
actionCapability,
locationCapability,
persistenceCapability,
persistencePromise,
mutationPromise,
locationPromise,
moveResult;
beforeEach(function () {
@@ -164,35 +162,18 @@ define(
'locationCapability',
[
'isOriginal',
'getLocation'
'persistLocation'
]
);
persistenceCapability = jasmine.createSpyObj(
'persistenceCapability',
['persist']
);
persistencePromise = new ControlledPromise();
persistenceCapability.persist.andReturn(persistencePromise);
mutationPromise = new ControlledPromise();
locationPromise = new ControlledPromise();
locationCapability.persistLocation.andReturn(locationPromise);
object = domainObjectFactory({
name: 'object',
capabilities: {
action: actionCapability,
mutation: {
invoke: function (mutator) {
mutator(object.model);
return mutationPromise;
}
},
persistence: persistenceCapability,
location: locationCapability
},
model: {
location: 'otherThing'
}
});
@@ -211,134 +192,48 @@ define(
.toHaveBeenCalledWith(jasmine.any(Function));
});
it("removes object when link is completed", function () {
linkService.perform.mostRecentCall.promise.resolve();
expect(object.getCapability)
.toHaveBeenCalledWith('action');
expect(actionCapability.perform)
.toHaveBeenCalledWith('remove');
});
describe("when moving an original", function() {
describe("when moving an original", function () {
beforeEach(function () {
locationCapability.isOriginal.andReturn(true);
locationCapability.getLocation.andReturn('newParent');
linkService.perform.mostRecentCall.promise.resolve();
});
it("updates location", function() {
expect(object.model.location).toBe('newParent');
});
it("persists new model when mutation completes", function() {
mutationPromise.resolve();
expect(persistenceCapability.persist)
it("updates location", function () {
expect(locationCapability.persistLocation)
.toHaveBeenCalled();
});
describe("after location update", function () {
beforeEach(function () {
locationPromise.resolve();
});
it("removes object from parent", function () {
expect(actionCapability.perform)
.toHaveBeenCalledWith('remove');
});
});
});
describe("when moving a link", function() {
describe("when moving a link", function () {
beforeEach(function () {
locationCapability.isOriginal.andReturn(false);
locationCapability.getLocation.andReturn('newParent');
linkService.perform.mostRecentCall.promise.resolve();
});
it("does not modify location", function() {
expect(object.model.location).toBe('otherThing');
});
it("does not call persistence", function() {
expect(persistenceCapability.persist)
it("does not update location", function () {
expect(locationCapability.persistLocation)
.not
.toHaveBeenCalled();
});
});
describe("when moving an object with children", function() {
var children;
beforeEach(function () {
var instantMutator = function (index) {
return {
invoke: function (mutator) {
mutator(children[index].model);
return {
then: function(callback) {
callback();
}
};
}
};
};
children = [
domainObjectFactory({
id: 'childa',
capabilities: {
location: jasmine.createSpyObj(
'childalocation',
['isOriginal', 'getLocation']
),
mutation: instantMutator(0)
},
model: {
location: 'childa-old-location'
}
}),
domainObjectFactory({
id: 'childb',
capabilities: {
location: jasmine.createSpyObj(
'childblocation',
['isOriginal', 'getLocation']
),
mutation: instantMutator(1)
},
model: {
location: 'childb-old-location'
}
}),
domainObjectFactory({
id: 'childc',
capabilities: {
location: jasmine.createSpyObj(
'childclocation',
['isOriginal', 'getLocation']
),
mutation: instantMutator(2)
},
model: {
location: 'childc-old-location'
}
})
];
children[0].capabilities.location.isOriginal.andReturn(true);
children[0].capabilities.location.getLocation.andReturn('childalocation');
children[1].capabilities.location.isOriginal.andReturn(true);
children[1].capabilities.location.getLocation.andReturn('childblocation');
children[2].capabilities.location.isOriginal.andReturn(false);
children[2].capabilities.location.getLocation.andReturn('childclocation');
object.capabilities.composition = {
invoke: function () {
return {
then: function (callback) {
callback(children);
}
};
}
};
linkService.perform.mostRecentCall.promise.resolve();
});
it("recursively updates the location of originals", function () {
expect(children[0].model.location).toBe('childalocation');
expect(children[1].model.location).toBe('childblocation');
expect(children[2].model.location).toBe('childc-old-location');
it("removes object from parent", function () {
expect(actionCapability.perform)
.toHaveBeenCalledWith('remove');
});
});
});
});
}