├── .gitignore ├── LICENSE ├── README.md ├── bower.json ├── index.js ├── lib └── backbone-relational-jsonapi.js └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # node-waf configuration 20 | .lock-wscript 21 | 22 | # Compiled binary addons (http://nodejs.org/api/addons.html) 23 | build/Release 24 | 25 | # Dependency directory 26 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git 27 | node_modules 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Luca Marchesini 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # backbone-relational-jsonapi 2 | 3 | Overloads `Backbone.Collection` and `Backbone.RelationalModel` `parse` methods to add compatibility with the [JSONapi](http://jsonapi.org/) protocol. 4 | 5 | ## Installation 6 | 7 | $ bower install backbone-relational-jsonapi 8 | 9 | ## Documentation 10 | 11 | Backbone and Backbone.Relational and Underscore need to be loaded before this library. Then, the `parse` methods are overloaded. For example, using RequireJS, you'll need to shim it as follows 12 | 13 | require.config({ 14 | paths : { 15 | 'backbone': 'path/to/backbone', 16 | 'backbone-relational': 'path/to/backbone-relational', 17 | 'backbone-relational-jsonapi': 'path/to/backbone-relational-jsonapi' 18 | } 19 | shim: { 20 | 'backbone' : { 21 | exports : 'Backbone', 22 | deps : ['jquery','underscore'] 23 | }, 24 | 'backbone-relational': { 25 | deps: ['backbone'] 26 | }, 27 | 'backbone-jsonapi' : { 28 | deps : ['backbone', 'backbone-relational','underscore'] 29 | }, 30 | } 31 | 32 | To use it, you can require it at your application's boostrap like 33 | 34 | define([ 35 | "backbone", 36 | "backbone-relational", 37 | "backbone-jsonapi" 38 | ], function(Backbone) { 39 | // Your application here 40 | }); 41 | 42 | And your instance of `Backbone` will use the library. 43 | 44 | ## Currently supported 45 | 46 | The library is currently able to parse 47 | 48 | * The `data` object 49 | * The `relationships` objects 50 | * The `included` objects 51 | 52 | ### Parsing a compound object 53 | 54 | To parse a compound object, the library first checks if something is present in the `included` object and creates the corresponding instances using the `id` and `type` attributes. To do this, it uses a common model factory that looks up the class names depending on the `type`. 55 | 56 | In your classes, specify a default `type` value, like 57 | 58 | var Tag = Backbone.RelationalModel.extend({ 59 | defaults: { 60 | type: 'tags' 61 | } 62 | } 63 | 64 | and then register this class in the model factory, like 65 | 66 | Backbone.modelFactory.registerModel(Tag); 67 | 68 | This way, compound objecs containing `tags` objects will be parsed and the included instances of `Tag` will be available to other objects for relationships. 69 | 70 | ## Meta objects 71 | 72 | Meta objects are supported and their treatment is delegated to the model or collection that is parsing the incoming data. When a `meta` object is found within the response, the function `handleMeta` is called on `this`. If the function is not defined, then the `meta` object is ignored. 73 | Be careful: this function is called before the `parse` function has actually returned, so you won't be able to access the parsed data from the `handeMeta` scope. 74 | 75 | ## Examples 76 | 77 | Here's an example of an `articles` object that can be parsed by the library 78 | 79 | { 80 | "data": { 81 | "type": "articles", 82 | "id": "1", 83 | "attributes": { 84 | "title": "The title of the article", 85 | "url": "http://article.com/article-id", 86 | "date": 1423094400, 87 | "thumbnail": "thumbnail.png", 88 | }, 89 | } 90 | "relationships": { 91 | "tags": { 92 | "data": [ 93 | { 94 | "type": "tags", 95 | "id": "10" 96 | }, 97 | ] 98 | } 99 | }, 100 | "included": [ 101 | { 102 | "type": "tags", 103 | "id": "10", 104 | "attributes": { 105 | "name": "geeks" 106 | } 107 | } 108 | ] 109 | } 110 | 111 | Which, Backbone-side would be expressed like 112 | 113 | var Article = Backbone.RelationalModel.extend({ 114 | defaults: { 115 | type: 'articles' 116 | }, 117 | relations: [{ 118 | type: Backbone.HasMany, 119 | key: 'tags', 120 | relatedModel: Tag // Refers to the Tag class defined above 121 | }] 122 | }); 123 | 124 | ## Not supported 125 | 126 | Currently, the support of the JSONapi specification is partial. Work still needs to be done. 127 | 128 | * Links 129 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "backbone-relational-jsonapi", 3 | "main": "lib/backbone-relational-jsonapi.js", 4 | "version": "0.0.7", 5 | "homepage": "https://github.com/xbill82/backbone-relational-jsonapi", 6 | "authors": [ 7 | "Luca Marchesini " 8 | ], 9 | "description": "A parsing layer for Backbone.Relational that enables data fetching in a format compliant with JSONapi 1.0.", 10 | "moduleType": [ 11 | "amd", 12 | "globals", 13 | "node" 14 | ], 15 | "keywords": [ 16 | "backbone", 17 | "relational", 18 | "jsonapi" 19 | ], 20 | "license": "MIT", 21 | "ignore": [ 22 | "**/.*", 23 | "node_modules", 24 | "bower_components", 25 | "app/lib/bower_components/", 26 | "test", 27 | "tests" 28 | ], 29 | "dependencies": { 30 | "backbone-relational": ">=0.9.0", 31 | "underscore": ">=1.5.0" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./lib/backbone-relational-jsonapi'); 2 | -------------------------------------------------------------------------------- /lib/backbone-relational-jsonapi.js: -------------------------------------------------------------------------------- 1 | ( function( root, factory ) { 2 | // Set up Backbone-relational for the environment. Start with AMD. 3 | if ( typeof define === 'function' && define.amd ) { 4 | define( [ 'exports', 'backbone', 'underscore' ], factory ); 5 | } 6 | // Next for Node.js or CommonJS. 7 | else if ( typeof exports !== 'undefined' ) { 8 | factory( exports, require( 'backbone' ), require( 'underscore' ) ); 9 | } 10 | // Finally, as a browser global. Use `root` here as it references `window`. 11 | else { 12 | factory( root, root.Backbone, root._ ); 13 | } 14 | }( this, function( exports, Backbone, _ ) { 15 | 'use strict'; 16 | 17 | var ModelFactory = function() { 18 | this.registeredModels = {}; 19 | }; 20 | 21 | ModelFactory.prototype.registerModel = function(model) { 22 | this.registeredModels[model.prototype.defaults.type] = model; 23 | } 24 | 25 | ModelFactory.prototype.findOrCreate = function(data) { 26 | if (this.registeredModels[data.type]) 27 | return this.registeredModels[data.type].findOrCreate(data, {parse: true}); 28 | } 29 | 30 | ModelFactory.prototype.createFromArray = function(items) { 31 | _.each(items, function(item) { 32 | this.findOrCreate(item); 33 | }, this); 34 | }; 35 | 36 | Backbone.modelFactory = new ModelFactory(); 37 | 38 | Backbone.Collection.prototype.parse = function(response) { 39 | if (!response) 40 | return; 41 | 42 | if (response.included) 43 | Backbone.modelFactory.createFromArray(response.included); 44 | 45 | if (response.meta && this.handleMeta) 46 | this.handleMeta(response.meta); 47 | 48 | if (!response.data) { 49 | return response; 50 | } 51 | 52 | return response.data; 53 | }; 54 | 55 | Backbone.RelationalModel.prototype.parse = function(response) { 56 | if (!response) 57 | return; 58 | 59 | if (response.included) 60 | Backbone.modelFactory.createFromArray(response.included); 61 | 62 | if (response.data) { 63 | response = response.data; 64 | } 65 | 66 | if (response.meta && this.handleMeta) 67 | this.handleMeta(response.meta); 68 | 69 | var data = response.attributes || {}; 70 | data.id = response.id; 71 | 72 | if (response.relationships) { 73 | var simplifiedRelations = _.mapObject(response.relationships, function(value) { 74 | return value.data; 75 | }); 76 | 77 | _.extend(data, simplifiedRelations); 78 | } 79 | 80 | return data; 81 | }; 82 | 83 | return Backbone; 84 | })); 85 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "backbone-relational-jsonapi", 3 | "version": "0.1.0", 4 | "description": "A parsing layer for Backbone.Relational that enables data fetching in a format compliant with JSONapi 1.0.", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [ 10 | "backbone", 11 | "relational", 12 | "jsonapi" 13 | ], 14 | "author": { 15 | "name": "Luca Marchesini", 16 | "email": "xbill82@gmail.com" 17 | }, 18 | "license": "MIT", 19 | "repository": { 20 | "type": "git", 21 | "url": "https://github.com/xbill82/backbone-relational-jsonapi" 22 | } 23 | } 24 | --------------------------------------------------------------------------------