A few months ago, I wrote an Ember persistence library to act as an alternative to Ember-Data. It’s been open source for a while, but I haven’t really ‘released’ it yet because I’ve been trying to polish some things up. But as the old saying goes, “if you’re not embarrassed by the first release, you’ve released too late”. So with that in mind, hell, let’s show it off.
I’d like to introduce you to Ember-Graph. Ember-Graph is an Ember-Persistence library that is built with complex object graphs in mind. I wanted something that could intelligently handle all of my relationships, and Ember-Data just wasn’t cutting it. So I set out to design a library that would not only work for our application, but for the community as well. And so far I’d like to think I’ve done a pretty good job (we’ve been using in our app for 4 months).
Some of the features aren’t finished, the documentation is poor and there are no usage guides. What more could you want, amirite?! But seriously, it’s in the early stages, so go easy on me. But there is a lot of functionality there. And, because it’s early in its life, now would be the perfect time to suggest features.
So what’s the big deal? Well, I want to show you just a few features that Ember-Data doesn’t (yet) provide.
First, let’s define a couple of models to work with:
App.Post = EG.Model.extend({
title: EG.attr({ type: 'string' }),
body: EG.attr({ type: 'string', defaultValue: '' }),
posted_on: EG.attr({ type: 'date' }),
comments: EG.hasMany({
relatedType: 'comment',
inverse: 'post',
defaultValue: []
})
});
App.Comment = EG.Model.extend({
body: EG.attr({ type: 'string' }),
post: EG.hasOne({
relatedType: 'post',
inverse: 'comments',
defaultValue: null
})
});
Looks pretty familiar, huh? So let’s say you’ve got some data that you want to load into the store. Screw adapters, screw serializers and screw anything that isn’t your own code.
App.get('store').extractPayload({
post: [
{ id: '1', title: 'First Post!!1', body: 'Heh?', posted_on: new Date(), comments: ['1', '2', '3'] }
],
comment: [
{ id: '1', body: 'First comment!', post: '1' },
{ id: '2', body: 'Sigh. Why do I even come here any more?', post: '1' },
{ id: '3', body: 'guyz, first post died in like 2006 get over it', post: '1' }
]
});
Now let’s do some manipulation:
var store = App.get('store');
// getRecord skips the promise if the data is loaded already
var post = store.getRecord('post', '1');
// in both Ember-Graph and Ember-Data, this returns a promise
post.get('comments');
// but what if we don't want a promise, just the IDs? EG has got you covered
post.get('_comments'); // ['1', '2', '3']
Now for the real magic:
// let's add another comment to our post
post.addToRelationship('comments', '4');
// let's double check
post.get('_comments'); // ['1', '2', '3', '4']
// wait, comment 4 doesn't exist! let's fix that
store.extractPayload({
comment: [
{ id: '4', body: 'Fourth post!!!!11one LOL I\'m so funny. NOT.' }
]
});
// Notice how we didn't associate the comment with a post
var comment = store.getRecord('comment', '4');
comment.get('_post'); // '1'
// EG knew enough to connect that relationship for you
What about deleting or changing relationships? We do that too:
store.extractPayload({
post: [
{ id: '2', title: 'First Post: Part 2', comments: ['5'] }
]
});
// We hate comment 5, let's replace it with comment 6
var post2 = store.getRecord('post', '2');
post2.removeFromRelationship('comments', '5');
post2.addToRelationship('comments', '6');
// But what about the posts you say?
store.extractPayload({
comment: [
{ id: '5', body: 'GTFO.', post: '2' },
{ id: '6', body: 'That\'s what she said!', post: null }
]
});
var comment5 = store.getRecord('comment', '5');
var comment6 = store.getRecord('comment', '6');
comment5.get('_post'); // null
comment6.get('_post'); // '2'
// Again, Ember-Graph knew to connect the relationships
But what if we make a mistake?
comment5.rollbackRelationships();
comment6.rollbackRelationships();
comment5.get('_post'); // '2'
comment6.get('_post'); // null
What about attributes? We handle those too.
// Let's change our post title
post.set('title', 'Better Title');
// Then, let's accept a Socket.io update from the server
store.extractPayload({
post: [
{ id: '1', title: '[Closed]First Post!!1', body: 'Heh?', posted_on: new Date(), comments: ['1', '2', '3'] }
]
})
// it didn't overwrite the client-side change
post.get('title'); // 'Better Title'
// so let's discard the change
post.rollbackAttributes();
// Now it takes the last know server value
post.get('title'); // '[Closed]First Post!!1'
That’s enough examples, you get the picture. The idea is that Ember-Graph wants to handle relationships as intelligently as possible, and that includes accepting push updates from the server at any time. After that merging the data is configured with just a few options. Ember-Graph knows the importance of real-time updates and concurrent viewing/editing, so it’ll always be ready for your data.
If you have any questions, just ask. If you have bugs or feature requests, files them on Github. And if you have complaints, PM me.
Despite the fact that Ember-Data has announced their single source of truth (only a month after I created this, d’oh!), I still believe Ember-Graph serves a purpose. Hopefully you think so too.
EDIT: Sorry, but the markdown parser went haywire. The examples are a bit hard to read.