Skip to content
Browse files

custom relations and "monadic" relations

  • Loading branch information...
1 parent 144d4f8 commit 703ad799e2505661a50e8281037be3908c02b106 @masylum committed Apr 4, 2012
Showing with 75 additions and 18 deletions.
  1. +25 −1 Readme.md
  2. +26 −6 backbone.rel.js
  3. +24 −11 test/test.js
View
26 Readme.md
@@ -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
View
32 backbone.rel.js
@@ -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
@@ -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;
}
@@ -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;
}
View
35 test/test.js
@@ -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'));
+ }
};
}
});
@@ -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'}
};
}
@@ -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++) {
@@ -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'), []);
});
@@ -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));
// ...
});
@@ -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 () {

0 comments on commit 703ad79

Please sign in to comment.
Something went wrong with that request. Please try again.