knockout-sync
Advanced tools
Comparing version 0.1.14 to 0.1.15
@@ -106,4 +106,5 @@ /*global module:false */ | ||
grunt.registerTask('build-dev', ['sweepout']); | ||
grunt.registerTask('build', ['sweepout', 'requirejs']); | ||
grunt.registerTask('test', ['simplemocha']); | ||
grunt.registerTask('default', ['jshint', 'test']); | ||
grunt.registerTask('travis', ['build-dev', 'jshint', 'test']); | ||
}; |
@@ -18,2 +18,6 @@ var require = { | ||
location: '../../lib/js/shimney/JSON' | ||
}, | ||
{ | ||
name: 'jquery', | ||
location: '../../lib/js/shimney/jquery' | ||
} | ||
@@ -20,0 +24,0 @@ ] |
{ | ||
"name": "knockout-sync", | ||
"version": "0.1.14", | ||
"version": "0.1.15", | ||
"description": "A small backend using knockout mapping to sync entities with a RESTful Backend", | ||
@@ -10,3 +10,3 @@ "main": "Gruntfile.js", | ||
"scripts": { | ||
"test": "grunt test" | ||
"test": "grunt travis" | ||
}, | ||
@@ -13,0 +13,0 @@ "repository": { |
@@ -1,4 +0,9 @@ | ||
knockout-sync | ||
============= | ||
# knockout-sync [![Build Status](https://travis-ci.org/webforge-labs/knockout-sync.svg)](https://travis-ci.org/webforge-labs/knockout-sync) | ||
A small backend using knockout mapping to sync entities with a RESTful Backend | ||
## installation | ||
npm install knockout-sync | ||
use the files as Asynchronous Module Definitions (for example with requirejs). |
@@ -280,5 +280,6 @@ define(['./Exception', './EntityModel', 'lodash', 'knockout', 'knockout-mapping'], function(Exception, EntityModel, _, ko, koMapping) { | ||
_.each(entityMeta.properties, function(property) { | ||
var propertyEntityMeta; | ||
if (that.isRelatedEntity(property)) { | ||
var propertyEntityMeta = that.getEntityMeta(property.type); | ||
propertyEntityMeta = that.getEntityMeta(property.type); | ||
@@ -329,2 +330,12 @@ mapping[property.name] = { | ||
}; | ||
} else if (that.isRelatedEntityCollection(property)) { | ||
propertyEntityMeta = that.getEntityMeta(property.type); | ||
mapping[property.name] = { | ||
create: function(options) { | ||
var fqn = that.getEntityFQNFromData(propertyEntityMeta, options.data); // this will always return propertyEntityMeta.fqn per default | ||
return that.findOrHydrate(fqn, options.data); | ||
} | ||
}; | ||
} | ||
@@ -340,2 +351,6 @@ }); | ||
this.isRelatedEntityCollection = function(property) { | ||
return property.type && that.hasEntityMeta(property.type) && property.isCollection; | ||
}; | ||
this.isMappedEntity = function(entity) { | ||
@@ -342,0 +357,0 @@ return _.isObject(entity) && ko.isObservable(entity.id) && entity.fqn; |
@@ -22,2 +22,3 @@ /* jshint expr:true */ | ||
var AuthorModel = requirejs('ACME/Blog/Entities/AuthorModel'); | ||
var PostModel = requirejs('ACME/Blog/Entities/PostModel'); | ||
var TagModel = requirejs('ACME/Blog/Entities/TagModel'); | ||
@@ -196,3 +197,65 @@ var ko = requirejs('knockout'); | ||
var postWithTagsResponse = { | ||
"posts": [{ | ||
"id": 1, | ||
"title": "Working with associations", | ||
"author": { | ||
"id": 2, | ||
"name": "Alice", | ||
"email": "alice@ps-webforge.com" | ||
}, | ||
"tags": [ | ||
{ | ||
label: "doctrine" | ||
}, | ||
{ | ||
label: "ORM" | ||
} | ||
] | ||
}] | ||
}; | ||
it("can get a mapped result to a single entity without attaching it", function() { | ||
var posts = em.getMappedResponse('ACME.Blog.Entities.Post', postWithTagsResponse); | ||
expect(ko.unwrap(posts)).to.have.length(1); | ||
var expectPost = function(post) { | ||
expect(post).to.have.property('id'); | ||
expect(post.id()).to.be.equal(1); | ||
expect(post).to.have.property('author'); | ||
expect(post.author).to.be.an.instanceOf(AuthorModel); | ||
expect(post.author.id()).to.be.equal(2); | ||
expect(post).to.have.property('tags'); | ||
expect(ko.isObservable(post.tags) && post.tags.indexOf !== undefined, 'post.tags should be an observable array').to.be.ok; | ||
expect(post.tags()).to.have.length(2); | ||
expect(em.find('ACME.Blog.Entities.Post', 1), 'entity should not be attached from getMappedResponse').to.be.not.ok; | ||
}; | ||
expectPost(posts()[0]); | ||
var post = em.getSingleMappedResponse('ACME.Blog.Entities.Post', postWithTagsResponse); | ||
expectPost(post); | ||
}); | ||
it("maps entity collections of an entity to a collection of entities", function() { | ||
var posts = em.getMappedResponse('ACME.Blog.Entities.Post', postWithTagsResponse); | ||
expect(ko.unwrap(posts)).to.have.length(1); | ||
var post = posts()[0]; | ||
expect(post).to.have.property('tags'); | ||
expect(ko.isObservable(post.tags) && post.tags.indexOf !== undefined, 'post.tags should be an observable array').to.be.ok; | ||
var tag = post.tags()[0]; | ||
expect(tag, 'tag should be an entity of ACME.Blog.Entities.Tag').to.be.instanceOf(TagModel); | ||
}); | ||
it.skip("do map carefully too nested responses", function() { | ||
var response = { | ||
@@ -206,3 +269,19 @@ "posts": [{ | ||
"name": "Alice", | ||
"email": "alice@ps-webforge.com" | ||
"email": "alice@ps-webforge.com", | ||
"posts": [{ | ||
"id": 1, | ||
"title": "Working with associations", | ||
"author": null, | ||
"tags": [ | ||
{ | ||
label: "doctrine" | ||
}, | ||
{ | ||
label: "ORM" | ||
} | ||
] | ||
}] | ||
}, | ||
@@ -224,21 +303,18 @@ | ||
var expectPost = function(post) { | ||
expect(post).to.have.property('id'); | ||
expect(post.id()).to.be.equal(1); | ||
var post = posts()[0]; | ||
expect(post).to.have.property('author'); | ||
expect(post.author.id()).to.be.equal(2); | ||
expect(post).to.have.property('author'); | ||
expect(post.author).to.be.an.instanceOf(AuthorModel); | ||
expect(post).to.have.property('tags'); | ||
expect(post.tags()).to.have.length(2); | ||
expect(post.author).to.have.property('posts'); | ||
expect(em.find('ACME.Blog.Entities.Post', 1), 'entity should not be attached from getMappedResponse').to.be.not.ok; | ||
}; | ||
var nestedPosts = post.author.posts; | ||
expect(ko.isObservable(nestedPosts) && nestedPosts.indexOf !== undefined, 'post[0].author.posts should be an observable array').to.be.ok; | ||
expectPost(posts()[0]); | ||
var nestedPost = nestedPosts()[0]; | ||
var post = em.getSingleMappedResponse('ACME.Blog.Entities.Post', response); | ||
expectPost(post); | ||
expect(nestedPost).to.be.an.instanceOf(PostModel); | ||
}); | ||
it("throws an error in getSingleMappedResponse when more than one root entity is defined", function() { | ||
@@ -245,0 +321,0 @@ var response = { |
@@ -36,17 +36,25 @@ { | ||
}, | ||
"content":{ | ||
"name":"content", | ||
"type":"MarkupText" | ||
}, | ||
"author":{ | ||
"name":"author", | ||
"type":"ACME\\Blog\\Entities\\Author" | ||
"type":"ACME\\Blog\\Entities\\Author", | ||
"isCollection":false | ||
}, | ||
"revisor":{ | ||
"name":"revisor", | ||
"type":"ACME\\Blog\\Entities\\Author" | ||
"type":"ACME\\Blog\\Entities\\Author", | ||
"isCollection":false | ||
}, | ||
"categories":{ | ||
"name":"categories", | ||
"type":"ACME\\Blog\\Entities\\Category" | ||
"type":"ACME\\Blog\\Entities\\Category", | ||
"isCollection":true | ||
}, | ||
"tags":{ | ||
"name":"tags", | ||
"type":"ACME\\Blog\\Entities\\Tag" | ||
"type":"ACME\\Blog\\Entities\\Tag", | ||
"isCollection":true | ||
}, | ||
@@ -57,2 +65,6 @@ "active":{ | ||
}, | ||
"relevance":{ | ||
"name":"relevance", | ||
"type":"Float" | ||
}, | ||
"created":{ | ||
@@ -79,7 +91,9 @@ "name":"created", | ||
"name":"writtenPosts", | ||
"type":"ACME\\Blog\\Entities\\Post" | ||
"type":"ACME\\Blog\\Entities\\Post", | ||
"isCollection":true | ||
}, | ||
"revisionedPosts":{ | ||
"name":"revisionedPosts", | ||
"type":"ACME\\Blog\\Entities\\Post" | ||
"type":"ACME\\Blog\\Entities\\Post", | ||
"isCollection":true | ||
} | ||
@@ -103,3 +117,4 @@ } | ||
"name":"posts", | ||
"type":"ACME\\Blog\\Entities\\Post" | ||
"type":"ACME\\Blog\\Entities\\Post", | ||
"isCollection":true | ||
} | ||
@@ -128,7 +143,7 @@ } | ||
{ | ||
"name":"ContentStream\\Paragraph", | ||
"fqn":"ACME\\Blog\\Entities\\ContentStream\\Paragraph", | ||
"singular":"content-stream_paragraph", | ||
"plural":"content-stream_paragraphs", | ||
"tableName":"content_stream_paragraphs", | ||
"name":"Page", | ||
"fqn":"ACME\\Blog\\Entities\\Page", | ||
"singular":"page", | ||
"plural":"pages", | ||
"tableName":"pages", | ||
"extends":null, | ||
@@ -141,5 +156,10 @@ "description":null, | ||
}, | ||
"content":{ | ||
"name":"content", | ||
"slug":{ | ||
"name":"slug", | ||
"type":"String" | ||
}, | ||
"contentStreams":{ | ||
"name":"contentStreams", | ||
"type":"ACME\\Blog\\Entities\\ContentStream\\Entry", | ||
"isCollection":true | ||
} | ||
@@ -161,9 +181,71 @@ } | ||
}, | ||
"paragraphs":{ | ||
"name":"paragraphs", | ||
"type":"ACME\\Blog\\Entities\\ContentStream\\Paragraph" | ||
"entries":{ | ||
"name":"entries", | ||
"type":"ACME\\Blog\\Entities\\ContentStream\\Entry", | ||
"isCollection":true | ||
}, | ||
"page":{ | ||
"name":"page", | ||
"type":"ACME\\Blog\\Entities\\Page", | ||
"isCollection":false | ||
} | ||
} | ||
}, | ||
{ | ||
"name":"ContentStream\\Entry", | ||
"fqn":"ACME\\Blog\\Entities\\ContentStream\\Entry", | ||
"singular":"content-stream_entry", | ||
"plural":"content-stream_entries", | ||
"tableName":"content_stream_entries", | ||
"extends":null, | ||
"description":null, | ||
"properties":{ | ||
"id":{ | ||
"name":"id", | ||
"type":"Id" | ||
}, | ||
"contentStream":{ | ||
"name":"contentStream", | ||
"type":"ACME\\Blog\\Entities\\ContentStream\\Stream", | ||
"isCollection":false | ||
} | ||
} | ||
}, | ||
{ | ||
"name":"ContentStream\\Paragraph", | ||
"fqn":"ACME\\Blog\\Entities\\ContentStream\\Paragraph", | ||
"singular":"content-stream_paragraph", | ||
"plural":"content-stream_paragraphs", | ||
"tableName":"content_stream_paragraphs", | ||
"extends":"ACME\\Blog\\Entities\\ContentStream\\Entry", | ||
"description":null, | ||
"properties":{ | ||
"content":{ | ||
"name":"content", | ||
"type":"String" | ||
} | ||
} | ||
}, | ||
{ | ||
"name":"ContentStream\\TextBlock", | ||
"fqn":"ACME\\Blog\\Entities\\ContentStream\\TextBlock", | ||
"singular":"content-stream_text-block", | ||
"plural":"content-stream_text-blocks", | ||
"tableName":"content_stream_text_blocks", | ||
"extends":"ACME\\Blog\\Entities\\ContentStream\\Entry", | ||
"description":null, | ||
"properties":{ | ||
"paragraph1":{ | ||
"name":"paragraph1", | ||
"type":"ACME\\Blog\\Entities\\ContentStream\\Paragraph", | ||
"isCollection":false | ||
}, | ||
"paragraph2":{ | ||
"name":"paragraph2", | ||
"type":"ACME\\Blog\\Entities\\ContentStream\\Paragraph", | ||
"isCollection":false | ||
} | ||
} | ||
} | ||
] | ||
} |
@@ -24,7 +24,9 @@ { | ||
"id": { "type": "DefaultId" }, | ||
"content": { "type": "MarkupText" }, | ||
"author": { "type": "Author" }, | ||
"revisor": { "type": "Author", "nullable": true }, | ||
"categories": { "type": "Collection<Category>", "isOwning": true }, | ||
"categories": { "type": "Collection<Category>", "isOwning": true, "cascade": ["persist", "remove"] }, | ||
"tags": { "type": "Collection<Tag>" }, | ||
"active": { "type": "Boolean" }, | ||
"relevance": { "type": "Float", "nullable": true }, | ||
"created": { "type": "DateTime" }, | ||
@@ -68,12 +70,13 @@ "modified": { "type": "DateTime", "nullable": true } | ||
{ | ||
"name": "ContentStream\\Paragraph", | ||
"name": "Page", | ||
"properties": { | ||
"id": { "type": "DefaultId" }, | ||
"content": { "type": "String" } | ||
"slug": { "type": "String" }, | ||
"contentStreams": { "type": "Collection<ContentStream\\Entry>" } | ||
}, | ||
"constructor": ["content"] | ||
"constructor": ["slug"] | ||
}, | ||
@@ -87,8 +90,41 @@ | ||
"paragraphs": { "type": "Collection<ContentStream\\Paragraph>" } | ||
"entries": { "type": "Collection<ContentStream\\Entry>" }, | ||
"page": { "type": "Page", "nullable": true, "onDelete": "cascade" } | ||
}, | ||
"constructor": [] | ||
}, | ||
{ | ||
"name": "ContentStream\\Entry", | ||
"properties": { | ||
"id": { "type": "DefaultId" }, | ||
"contentStream": { "type": "ContentStream\\Stream" } | ||
} | ||
}, | ||
{ | ||
"name": "ContentStream\\Paragraph", | ||
"extends": "ContentStream\\Entry", | ||
"properties": { | ||
"content": { "type": "String" } | ||
}, | ||
"constructor": ["content"] | ||
}, | ||
{ | ||
"name": "ContentStream\\TextBlock", | ||
"extends": "ContentStream\\Entry", | ||
"properties": { | ||
"paragraph1": { "type": "ContentStream\\Paragraph", "relation": "ManyToOne" }, | ||
"paragraph2": { "type": "ContentStream\\Paragraph" } | ||
} | ||
} | ||
] | ||
} |
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
68553
29
1916
10