Navigation Menu

Skip to content

Commit

Permalink
custom relations and "monadic" relations
Browse files Browse the repository at this point in the history
  • Loading branch information
masylum committed Apr 4, 2012
1 parent 144d4f8 commit 703ad79
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 18 deletions.
26 changes: 25 additions & 1 deletion Readme.md
Expand Up @@ -8,24 +8,48 @@ Backbone.Rel exposes a method `rel` that is a relationship *getter*.
You can implement your `hasMany` and `belongsTo` like in this example:

``` javascript
// models/project.js
Models.Project.hasMany = function () {
return {
users: {collection: Collections.users, id: 'project_id'}
, tasks: {collection: Collection.tasks, filter: function (task) {
return task.rel('project') = this;
}}
};
};

// models/user.js
Models.User.hasMany = function () {
return {
tasks: {collection: Collections.tasks, id: 'user_id'}
};
};

Models.User.belongsTo = function () {
return {
project: Collection.projects
};
};

// models/task.js
Models.Task.belongsTo = function () {
return {
user: Collections.users
, project: function (task) {
return Collection.projects.get(task.rel('user').project);
}
};
};

var user = new User({id: 1})
var project = new Project({id: 1})
, user = new User({id: 1, project_id: 1})
, task1 = new Task({id: 1, user_id: 1})
, task2 = new Task({id: 2, user_id: 1});

assert.equal(user.rel('tasks').length, 2);
assert.equal(user.rel('project'), project);
assert.equal(task1.rel('user'), user);
assert.equal(task1.rel('project'), project);
```

## Dependencies
Expand Down
32 changes: 26 additions & 6 deletions backbone.rel.js
Expand Up @@ -21,8 +21,22 @@
*/
function rel(key, options) {
var self = this
, keys = key.split('.')
, singularize;

// kind of monadic accesor
if (keys.length > 1) {
return _.reduce(keys, function (memo, key) {
if (typeof memo === 'undefined') {
return self.rel(key);
} else if (memo) {
return memo.rel(key);
} else {
return null;
}
}, undefined);
}

options = options || {};

// poor singularize fallback
Expand All @@ -37,10 +51,12 @@

var options = self.hasMany()[key];

function filter(el) {
return el.get(options.id) === this.id;
}

if (options) {
return options.collection.filter(function (el) {
return el.get(options.id) === self.id;
});
return options.collection.filter(_.bind(options.filter || filter, self));
} else {
return null;
}
Expand All @@ -51,10 +67,14 @@
return null;
}

var collection = self.belongsTo()[key];
var target = self.belongsTo()[key];

if (collection) {
return collection.get(self.get(singularize(key) + '_id'));
if (target) {
if (_.isFunction(target)) {
return target(self) || null;
} else {
return target.get(self.get(singularize(key) + '_id')) || null;
}
} else {
return null;
}
Expand Down
35 changes: 24 additions & 11 deletions test/test.js
Expand Up @@ -14,7 +14,9 @@ Models.Task = Backbone.Model.extend({
belongsTo: function () {
return {
user: users
, project: projects
, project: function (task) {
return projects.get(task.rel('user.project'));
}
};
}
});
Expand All @@ -36,7 +38,11 @@ Models.User = Backbone.Model.extend({
Models.Project = Backbone.Model.extend({
hasMany: function () {
return {
tasks: {collection: tasks, id: 'project_id'}
tasks: {collection: tasks, filter: function (task) {
return task.rel('project')
? task.rel('project').id === this.id
: null;
}}
, users: {collection: users, id: 'project_id'}
};
}
Expand Down Expand Up @@ -72,7 +78,11 @@ describe('Rel', function () {
}

for (var i = 0; i < 6; i++) {
tasks.add({id: i, user_id: i % 2, project_id: i % 3});
if (i === 0) {
tasks.add({id: i});
} else {
tasks.add({id: i, user_id: i % 2});
}
}

for (var i = 0; i < 2; i++) {
Expand All @@ -83,11 +93,11 @@ describe('Rel', function () {
it('returns the project for a given user', function () {
assert.equal(users.get(0).rel('project'), projects.get(0));
assert.equal(users.get(1).rel('project'), projects.get(1));
assert.deepEqual(users.get(2).rel('project'), undefined);
assert.deepEqual(users.get(2).rel('project'), null);
});

it('returns the tasks for a given user', function () {
assert.deepEqual(_.pluck(users.get(0).rel('tasks'), 'id'), [0, 2, 4]);
assert.deepEqual(_.pluck(users.get(0).rel('tasks'), 'id'), [2, 4]);
assert.deepEqual(_.pluck(users.get(1).rel('tasks'), 'id'), [1, 3, 5]);
assert.deepEqual(_.pluck(users.get(2).rel('tasks'), 'id'), []);
});
Expand All @@ -99,18 +109,21 @@ describe('Rel', function () {
});

it('returns the user for a given task', function () {
assert.deepEqual(tasks.get(0).rel('user'), users.get(0));
assert.deepEqual(tasks.get(0).rel('user'), null);
assert.deepEqual(tasks.get(1).rel('user'), users.get(1));
assert.deepEqual(tasks.get(2).rel('user'), users.get(0));
assert.deepEqual(tasks.get(3).rel('user'), users.get(1));
assert.deepEqual(tasks.get(4).rel('user'), users.get(0));
// ...
});

it('returns the project for a given task', function () {
assert.deepEqual(tasks.get(0).rel('project'), projects.get(0));
assert.throws(function () {
assert.deepEqual(tasks.get(0).rel('project'), projects.get(0));
});
assert.deepEqual(tasks.get(1).rel('project'), projects.get(1));
assert.deepEqual(tasks.get(2).rel('project'), undefined);
assert.deepEqual(tasks.get(3).rel('project'), projects.get(0));
assert.deepEqual(tasks.get(2).rel('project'), projects.get(0));
assert.deepEqual(tasks.get(3).rel('project'), projects.get(1));
// ...
});

Expand All @@ -120,8 +133,8 @@ describe('Rel', function () {
});

it('returns the tasks for a given project', function () {
assert.deepEqual(_.pluck(projects.get(0).rel('tasks'), 'id'), [0, 3]);
assert.deepEqual(_.pluck(projects.get(1).rel('tasks'), 'id'), [1, 4]);
assert.deepEqual(_.pluck(projects.get(0).rel('tasks'), 'id'), [2, 4]);
assert.deepEqual(_.pluck(projects.get(1).rel('tasks'), 'id'), [1, 3, 5]);
});

it('returns the users for a given project', function () {
Expand Down

0 comments on commit 703ad79

Please sign in to comment.