knockout-sync
Advanced tools
Comparing version 0.1.4 to 0.1.5
{ | ||
"name": "knockout-sync", | ||
"version": "0.1.4", | ||
"version": "0.1.5", | ||
"description": "A small backend using knockout mapping to sync entities with a RESTful Backend", | ||
@@ -5,0 +5,0 @@ "main": "Gruntfile.js", |
@@ -1,2 +0,2 @@ | ||
define(['knockout-mapping', './EntityModel', 'Amplify'], function(koMapping, EntityModel, amplify) { | ||
define(['knockout-mapping', './EntityModel', 'Amplify', 'lodash'], function(koMapping, EntityModel, amplify, _) { | ||
@@ -6,3 +6,2 @@ /** | ||
knockout-sync.entity-saved | ||
@@ -17,5 +16,7 @@ triggered when an entity with an existing id was saved again | ||
return function Backend(driver, entityModel) { | ||
return function Backend(driver, entityModel, prefixUrl) { | ||
var that = this; | ||
that.prefixUrl = prefixUrl || '/api/'; | ||
if (!entityModel || !(entityModel instanceof EntityModel)) { | ||
@@ -32,3 +33,3 @@ throw new Error('missing parameter #2 for EntityManager. Provide the entity model'); | ||
this.save = function(entity) { | ||
this.save = function(entity, callback) { | ||
var method, url; | ||
@@ -39,6 +40,6 @@ var entityMeta = that.model.getEntityMeta(entity.fqn); | ||
method = 'put'; | ||
url = '/api/'+entityMeta.singular+'/'+entity.id(); | ||
url = that.prefixUrl+entityMeta.singular+'/'+entity.id(); | ||
} else { | ||
method = 'post'; | ||
url = '/api/'+entityMeta.plural; | ||
url = that.prefixUrl+entityMeta.plural; | ||
} | ||
@@ -64,2 +65,6 @@ | ||
} | ||
if (callback) { | ||
callback.call(error); | ||
} | ||
}); | ||
@@ -74,3 +79,3 @@ }; | ||
that.driver.dispatch('GET', '/api/'+entityMeta.plural, undefined, function(error, result) { | ||
that.driver.dispatch('GET', that.prefixUrl+entityMeta.plural, undefined, function(error, result) { | ||
callback(undefined, result); | ||
@@ -80,2 +85,49 @@ }); | ||
/** | ||
* Queries a single entity returned by backend | ||
*/ | ||
this.get = function(entityFQN, identifiers, callback) { | ||
var entityMeta = that.model.getEntityMeta(entityFQN); | ||
if (_.isPlainObject(identifiers)) { | ||
// maybe map to filter here | ||
identifiers = _.values(identifiers); | ||
} else if (!_.isArray(identifiers)) { // single scalars | ||
identifiers = [identifiers]; | ||
} | ||
that.driver.dispatch('GET', that.prefixUrl+entityMeta.plural+'/'+identifiers.join('/'), undefined, function(error, result) { | ||
/* | ||
normalize single responses to always use repsonse plural form | ||
like: | ||
{ | ||
"page": { | ||
"id": 7 | ||
"slug": "start" | ||
} | ||
} | ||
to | ||
{ | ||
"pages": [ | ||
{ | ||
"id": 7, | ||
"slug": "start" | ||
} | ||
] | ||
} | ||
*/ | ||
if (result[entityMeta.singular] && !result[entityMeta.plural]) { | ||
result[entityMeta.plural] = [ result[entityMeta.singular] ]; | ||
delete result[entityMeta.singular]; | ||
} | ||
callback(undefined, result); | ||
}); | ||
}; | ||
this.serializeEntity = function(entity) { | ||
@@ -87,4 +139,4 @@ if (typeof(entity.serialize) === 'function') { | ||
return koMapping.toJS(entity); | ||
} | ||
}; | ||
}; | ||
}); |
@@ -31,8 +31,10 @@ define(['./Exception', './EntityModel', 'lodash', 'knockout', 'knockout-mapping', 'Amplify'], function(Exception, EntityModel, _, ko, koMapping, amplify) { | ||
this.isInit = false; | ||
this.init = function() { | ||
if (that.isInit) { | ||
throw new Error('dont init the em twice!'); | ||
} | ||
var firstFakeResponse = {}; | ||
// initialize empty collections | ||
// the ko.observableArray([]) has not the mapped* functions (like mappedRemove), when it is not mapped yet, so we do the first mapping by hand with empty collections | ||
@@ -44,3 +46,5 @@ _.each(that.model.getEntities(), function(entityMeta) { | ||
this.mapResponse(firstFakeResponse); | ||
// initialize all collections with a fully empty response | ||
that.mapResponse(firstFakeResponse); | ||
that.isInit = true; | ||
}; | ||
@@ -137,2 +141,13 @@ | ||
/* | ||
* returns the observable referencing the current collection of all entities fetched with entityFQN | ||
* @return ko.observableArray() | ||
*/ | ||
this.refAll = function(entityFQN) { | ||
var meta = this.getEntityMeta(entityFQN); | ||
return that.entities[meta.plural]; | ||
}; | ||
this.getKnockoutMappingMetadata = function() { | ||
@@ -190,2 +205,6 @@ var mapping = {}; | ||
create: function(options) { | ||
if (!options.data) { | ||
return ko.observable(null); | ||
} | ||
if (options.data.$type) { | ||
@@ -192,0 +211,0 @@ var relatedEntity = that.find(propertyEntityMeta.fqn, options.data.$ref); |
@@ -11,6 +11,5 @@ var chai = require('chai'); | ||
_.extend(config, { | ||
nodeRequire: require, | ||
baseUrl: "src/js", // seems to be relative to the dir where mocha is called | ||
} | ||
) | ||
nodeRequire: require, | ||
baseUrl: "src/js", // seems to be relative to the dir where mocha is called | ||
}) | ||
); | ||
@@ -21,4 +20,3 @@ | ||
var EntityModel = requirejs('KnockoutSync/EntityModel'); | ||
var AjaxDriver = function() { | ||
}; | ||
var AjaxDriver = function() {}; | ||
var amplify = requirejs('Amplify'); | ||
@@ -38,3 +36,4 @@ | ||
}).to.throw (Error, 'Provide the entity model'); | ||
}).to. | ||
throw (Error, 'Provide the entity model'); | ||
@@ -47,3 +46,4 @@ }); | ||
}).to.throw (Error, 'Provide the driver'); | ||
}).to. | ||
throw (Error, 'Provide the driver'); | ||
@@ -55,3 +55,7 @@ }); | ||
var user = new UserModel({name: 'Ross', email: 'ross@ps-webforge.com', id: undefined}); | ||
var user = new UserModel({ | ||
name: 'Ross', | ||
email: 'ross@ps-webforge.com', | ||
id: undefined | ||
}); | ||
@@ -77,6 +81,10 @@ driver.dispatch = function(method, url, data, callback) { | ||
backend.save(user); | ||
var saveCalled = false; | ||
backend.save(user, function(error) { | ||
saveCalled = true; | ||
expect(error).to.not.exist; | ||
}); | ||
expect(dispatched, 'dispatch is called').to.be.true; | ||
expect(published, 'publishe is called').to.be.true; | ||
expect(saveCalled, 'save callback was called').to.be.true; | ||
expect(user.id(), 'user.id').to.be.equal(7); | ||
@@ -88,3 +96,7 @@ }); | ||
var user = new UserModel({name: 'Ross', email: 'ross@ps-webforge.net', id: 7}); | ||
var user = new UserModel({ | ||
name: 'Ross', | ||
email: 'ross@ps-webforge.net', | ||
id: 7 | ||
}); | ||
@@ -107,6 +119,11 @@ driver.dispatch = function(method, url, data, callback) { | ||
backend.save(user); | ||
var saveCalled = false; | ||
backend.save(user, function(error) { | ||
saveCalled = true; | ||
expect(error).to.not.exist; | ||
}); | ||
expect(dispatched, 'dispatch is called').to.be.true; | ||
expect(published, 'published is called').to.be.true; | ||
expect(user.id(), 'user.id').to.be.equal(7); | ||
expect(saveCalled, 'save callback was called').to.be.true; | ||
}); | ||
@@ -117,5 +134,15 @@ | ||
var user1 = new UserModel({name: 'Ross', email: 'ross@ps-webforge.net', id: 7}); | ||
var user2 = new UserModel({name: 'Rachel', email: 'rachel@ps-webforge.net', id: 8}); | ||
var result = { 'users': [user1.serialize, user2.serialize()] }; | ||
var user1 = new UserModel({ | ||
name: 'Ross', | ||
email: 'ross@ps-webforge.net', | ||
id: 7 | ||
}); | ||
var user2 = new UserModel({ | ||
name: 'Rachel', | ||
email: 'rachel@ps-webforge.net', | ||
id: 8 | ||
}); | ||
var result = { | ||
'users': [user1.serialize, user2.serialize()] | ||
}; | ||
@@ -138,2 +165,67 @@ driver.dispatch = function(method, url, data, callback) { | ||
}); | ||
}); | ||
var DispatchExpectation = function(settings) { | ||
var that = this; | ||
this.dispatched = false; | ||
driver.dispatch = function(method, url, data, callback) { | ||
that.dispatched = true; | ||
expect(method, 'method').to.be.equal(settings.method); | ||
expect(url, 'url').to.be.equal(settings.url); | ||
expect(data, 'data').to.be.eql(settings.data); | ||
callback(settings.error, settings.result); | ||
}; | ||
this.wasDispatched = function() { | ||
expect(that.dispatched, 'driver did dispatch the request').to.be.true; | ||
}; | ||
this.resultEquals = function(returnedResult) { | ||
expect(returnedResult, 'result returned').to.be.eql(settings.normalizedResult || settings.result); | ||
}; | ||
}; | ||
var expectDriverToReturnSingleUser = function() { | ||
var user = new UserModel({ | ||
name: 'Rachel', | ||
email: 'rachel@ps-webforge.net', | ||
id: 8 | ||
}); | ||
return new DispatchExpectation({ | ||
result: { | ||
'user': user.serialize() | ||
}, | ||
normalizedResult: { | ||
'users': [user.serialize()] | ||
}, | ||
method: 'GET', | ||
url: '/api/users/8', | ||
data: undefined | ||
}); | ||
}; | ||
it("queries one of specific entity by single scalar", function() { | ||
var expectation = expectDriverToReturnSingleUser(); | ||
backend.get('ACME.Blog.Entities.User', 8, function(error, returnedResult) { | ||
expectation.wasDispatched(); | ||
expect(error).to.be.not.existing; | ||
expectation.resultEquals(returnedResult); | ||
}); | ||
}); | ||
it("queries one of specific entity by identifiers object", function() { | ||
var expectation = expectDriverToReturnSingleUser(); | ||
backend.get('ACME.Blog.Entities.User', {id: 8}, function(error, returnedResult) { | ||
expectation.wasDispatched(); | ||
expect(error).to.be.not.existing; | ||
expectation.resultEquals(returnedResult); | ||
}); | ||
}); | ||
}); |
@@ -162,2 +162,21 @@ var chai = require('chai'), | ||
}); | ||
it("applies nested empty entities without error to empty observables (todo: define mapping to what)", function() { | ||
var response = { | ||
"posts": [{ | ||
"id": 1, | ||
"title": "Working with associations", | ||
"author": null | ||
}] | ||
}; | ||
em.mapResponse(response); | ||
var post1 = em.find('ACME.Blog.Entities.Post', 1); | ||
expect(post1).to.have.property('author'); | ||
// to define: empty observable or just null or undefined? | ||
expect(post1.author(), 'author from post1').to.be.not.existing; | ||
}); | ||
}); | ||
@@ -164,0 +183,0 @@ }); |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
40389
1179