GithubHelp home page GithubHelp logo

gomoob / backbone.hateoas Goto Github PK

View Code? Open in Web Editor NEW
12.0 12.0 3.0 629 KB

Use HAL and HATEOAS in your Backbone and Marionette applications !

License: MIT License

JavaScript 97.62% HTML 1.78% ApacheConf 0.16% PHP 0.44%

backbone.hateoas's People

Contributors

bgaillard avatar simonbaudry avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

backbone.hateoas's Issues

Add `embeddedUnsetOptions` and 'linksUnsetOptions` global options.

When we call the this.model.unsetEmbedded(rel, [options]) and this.model.unsetLink(rel, [options]) the behavior is exaclty the same as the Backbone unset(attr, [options]) (i.e the attribute stays and has an undefined value).

We've added a custom delete option which can be used to force complete deletion of the attribute (using the Javascript delete operator).

Here is an exemple with and without the delete option using an undefined address embedded relation.

Without the delete option

var user = new Hal.Model({
    id : 1,
    firstName : 'John', 
    lastName : 'Doe',
    _embedded : {
        address : {
            id : 10,
            street : '142 Rue de Rivoli', 
            postalCode : 75001,
            city : 'Paris'
        }
    }
});
user.unsetEmbedded('address');
user.toJSON({contentType: 'application/hal+json'});
// {
//     id : 1,
//     firstName : 'John', 
//     lastName : 'Doe',
//     _embedded : {
//         address : null
//    }
// }

With the delete option

var user = new Hal.Model({
    id : 1,
    firstName : 'John', 
    lastName : 'Doe',
    _embedded : {
        address : {
            id : 10,
            street : '142 Rue de Rivoli', 
            postalCode : 75001,
            city : 'Paris'
        }
    }
});
user.unsetEmbedded('address', {delete : true});
user.toJSON({contentType : 'application/hal+json'});
// {
//     id : 1,
//     firstName : 'John', 
//     lastName : 'Doe'
// }

This is good but in some project it would be easier to allow overwriting of the unset behavior on embedded and likes for the whole project.

For example.

Hal.embeddedUnsetOptions : {
    delete : true
};
Hal.linksUnsetOptions : {
    delete : true
};

// Now unsetEmbedded has the delete option set to true by default
var user = new Hal.Model({
    id : 1,
    firstName : 'John', 
    lastName : 'Doe',
    _embedded : {
        address : {
            id : 10,
            street : '142 Rue de Rivoli', 
            postalCode : 75001,
            city : 'Paris'
        }
    }
});
user.unsetEmbedded('address');
user.toJSON({contentType : 'application/hal+json'});
// {
//     id : 1,
//     firstName : 'John', 
//     lastName : 'Doe'
// }

// We could still force the option
user = new Hal.Model({
    id : 1,
    firstName : 'John', 
    lastName : 'Doe',
    _embedded : {
        address : {
            id : 10,
            street : '142 Rue de Rivoli', 
            postalCode : 75001,
            city : 'Paris'
        }
    }
});
user.unsetEmbedded('address', {delete: false});
user.toJSON({contentType: 'application/hal+json'});
// {
//     id : 1,
//     firstName : 'John', 
//     lastName : 'Doe',
//     _embedded : {
//         address : null
//    }
// }

Create gitter room

  • Create a gitter room
  • Add a gitter badge to the root README.md file
  • Add a gitter badge to the documentation and explains people are welcome to ask questions here

https://gitter.im

The Hal collection should be able to use urlMiddle

For now we are forced to specify 2 parameters when we create a Hal.Collection :

  • url : An absolute URL used to fetch the REST resource
  • rel : The name of the relation embedded in the returned HAL response.
var users = new Hal.Collection();
users.url = 'https://myserver.com/api/users';
users.rel = 'users'

It works but using absolute URLs is not good, instead we should be able to do this :

// Only one time at project initialization
Hal.urlRoot = 'https://myserver.com/api';

// Later when we want to create a collection
var users = new Hal.Collection();
users.urlMiddle = 'users';
users.rel = 'users';
users.fetch(); // https://myserver.com/api/users

// And even shorter if we suppose the default 'rel' value is equal to 'urlMiddle'
var users = new Hal.Collection();
users.urlMiddle = 'users';
users.fetch(); // https://myserver.com/api/users

// We could even allow to specify the 'urlMiddle' as options
var users = new Hal.Collection({ urlMiddle : 'users'});
users.fetch();

// But if a url is explicitly provided use it in all cases
var users = new Hal.Collection();
users.urlMiddle = 'users';
users.url = 'https://myserver.com/api-v2/users'
users.fetch(); // https://myserver.com/api-v2/users

Improve error management

For now each time we encounter an error in the library we throw a standard Javascript Error, here is a sample.

throw new Error('Invalid link identified by \'rel\'=\'' + rel + '\' !');

It works but has several drawbacks :

  • We cannot differenciate a standard error from an error thrown by the library using instanceof
  • Sometimes we want to transport several context objects and informations with an error to give more details about it, this is not possible with the Error class ;
  • Throwing an Error object when an error is encountered is not always wanted by developers, instead it could be cool to have an error handler which could be overriden (to add a logger for example).

So, to improve error management we propose here to create an Hal.Error class and an Hal.ErrorHandler class.

Missing first page error when a model is added to an empty HAL collection

If we fetche an HAL Collection from server side and this HAL collection is empty then if we try to add a model on this empty collection the following exception is encountered.

Uncaught RangeError: No link found for page 1
Backbone.PageableCollection.Backbone.Collection.extend._checkState @ backbone.paginator.js:588
collectionEventHandler @ backbone.paginator.js:433
triggerEvents @ backbone.js:209
Backbone.Events.trigger @ backbone.js:148
_.extend._onModelEvent @ backbone.js:946
triggerEvents @ backbone.js:210
Backbone.Events.trigger @ backbone.js:149
_.extend.set @ backbone.js:749
_.extend.add @ backbone.js:638
Marionette.LayoutView.extend._onUserProductRatingSaveSuccess @ list-child-view.js:724
proxy @ jquery.js:512
options.success @ backbone.js:486
fire @ jquery.js:3099
self.fireWith @ jquery.js:3211
done @ jquery.js:8264
(anonymous function) @ jquery.js:8605
XMLHttpRequest.oXMLHttpRequest.onload @ oauth.js:107

Null or undefined links should be authorized

The Backbone has(attribute) method checks if an attribute is not null or not undefined. Also to check for the existence of a HAL link we use the has(attribute) method on the Hal.Links container.

But setting a null or undefined link throws an error which is not normal, also not authorizing null or undefined links will lead to problems if the unsetLink method is called (same problem as #5).

Updates the link implementation to authorize null and undefined links and update the hasLink -> With a not empty model unit test accordingly.

Improve management of rel on Hal.Collection

When we create an Hal.Collection we use a rel property to indicate which name is used for the embedded array of elements.

For example if we have the following HAL collection.

{
    "_embedded" :  {
        "users" : [ ... ]
    },
    "_links" : { ... }
    "page" : 1
    "page_count" : 1
    "page_size" : 12
    "total_items": 2
    }
}

To fetch this collection we have to write the following code.

var collection = new Hal.Collection();
collection.rel = 'users';
collection.url = 'https://myserver.com/rest/users'

Here if the developer configure a bad rel the collection fetch will succeed but with an empty collection. This is very annoying and we can consider this is a bug.

We should improve the Hal.Collection the following way :

  • Most Hal collections have only one element in their _embedded section, so by default provided the rel should be optional and the Hal.Collection should be able to pick the only _embedded element and check if its an array. If it is it should consider this array corresponds to the collection elements. If the rel parameter is not provided and at least 2 elements are in the _embedded section then the Hal.Collection should throw a detailed error message ;
  • If a rel element is provided and the Hal.Collection does not found any key corresponding to this rel in the _embedded section then the Hal.Collection should throw an error with a detailed message.

The library should extend Backbone.Model and Backbone.Collection

Having specific Hal.Model and Hal.Collection objects is good, but we could do it better by extending the Backbone.Model and Backbone.Collection types directly.

In fact having additionnal Hal.Model and Hal.Collection types adds complexity in the code and confusion.

For exemple we should be able to do this but its not possible :

var halModel = new Hal.Model();
halModel.setEmbedded('address', new Backbone.Model());
halModel.getEmbedded('address').setEmbedded('city', new Backbone.Mode());

The solution would be to extend the Backbone.Model and Backbone.Collection types the same way Backbone.Stickit does it.

Then we should be able to simply do :

var model = new Backbone.Model();
model.setEmbedded('address', new Backbone.Model());
model.getEmbedded('address').setEmbedded('city', new Backbone.Mode());

// Standard 'application/json' serialization
model.toJSON(); 

// Custom 'application/hal+json' serialization
model.toJSON({contentType : 'application/hal+json'});

For example :

// Export onto Backbone object
Backbone.Hal = Hal;

// Backbone.Model Mixins
// --------------------
Hal.ModelMixin = {
    ...
    hasEmbedded : function(rel) {
        return this._embedded.has(rel);
    },
    ...
};

// Backbone.Collection Mixins
// --------------------
Hal.CollectionMixin = {
...
};

_.extend(Backbone.Model.prototype, Hal.ModelMixin);
_.extend(Backbone.Collection.prototype, Hal.CollectionMixin);

Then to let old version of backbone.hateoas we could simply do this :

Hal.Model = Backbone.Model;
Hal.Collection = Backbone.Collection;

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.