Skip to content

Commit

Permalink
adding hasMany Stuff
Browse files Browse the repository at this point in the history
remove works from hasMany

Added documentation
  • Loading branch information
Scott Burch committed Jan 30, 2012
1 parent f4cb26b commit 16ac299
Show file tree
Hide file tree
Showing 10 changed files with 457 additions and 251 deletions.
100 changes: 100 additions & 0 deletions Base.js
@@ -0,0 +1,100 @@
/**
* The base constructor that is extended by each domain constructor
* Contains the basic methods save/remove...
*/
"use strict";
module.exports = function(db, name, config) {
var domain = require('couch-ar');
var helpers = require('./helpers');
var that = {};

configureHasMany();


that.serialize = function() {
var obj = Object.getOwnPropertyNames(config.properties).reduce(function(obj, prop) {
obj[prop] = that[prop];
return obj;
}, {});
obj.type = name;
obj._id = obj.id;
obj._rev = obj.rev;
return obj;
}

that.save = function(callback) {
callback = callback || function() {
}
that.beforeSave && that.beforeSave();
var out = that.serialize();
that.dateCreated = that.dateCreated || new Date();
that.lastUpdated = new Date();
db.save(that.id, that.serialize(), function(err, res) {
if (res.ok) {
that.id = res.id;
that.rev = res.rev
}
callback(err, res);
});
}

that.remove = function(callback) {
if (that.id) {
db.remove(that.id, that.rev, function(err, res) {
that.id = err ? that.id : undefined;
callback(err, res);
});
} else {
callback();
}
}
return that;

function configureHasMany() {

Object.keys(config.hasMany || {}).forEach(function(propName){
var singularPropName = propName.replace(/(.*)s$/,'$1');
var upperPropName = helpers.toUpper(propName);
var singularUpperPropName = helpers.toUpper(singularPropName);
var idsArray = that[singularPropName + 'Ids'] = [];
var model = domain[config.hasMany[propName]];
addGetter();
addAdder();
addRemover();

function addGetter() {
that['get' + upperPropName] = function(cb) {
var count = 0;
var things = [];
var ids = idsArray.slice(0);
ids.length === 0 && cb([]);
ids.forEach(function(id) {
model.findById(id, function(thing) {
things.push(thing);
count++;
count === ids.length && cb(things);
});
});
}
}

function addAdder() {
that['add' + singularUpperPropName] = function(it) {
if(it.id === undefined) {
throw 'Can not add non-persisted entity to hasMany';
}
idsArray.indexOf(it.id) === -1 && idsArray.push(it.id);
}
}

function addRemover() {
that['remove' + singularUpperPropName] = function(it) {
var idx = idsArray.indexOf(it.id);
if(idx !== -1) {
idsArray.splice(idx,1);
}
}
}
});
}
}
25 changes: 25 additions & 0 deletions README.md
Expand Up @@ -69,6 +69,9 @@ Next, create your domain files in ../testDomain like this:
lastName: {},
fullName: {}
},
hasMany: {
phoneNumbers: 'PhoneNumber'
},
views: {
firstOrLastName: {map: function(doc) {
emit(doc.firstName, doc);
Expand Down Expand Up @@ -194,6 +197,9 @@ The following example will create a view to find a user by first or last name:
firstName:{},
lastName: {},
},
hasMany: {
phoneNumbers: 'PhoneNumber'
},
views: {
firstOrLastName: {map: function(doc) {
emit(doc.firstName, doc);
Expand All @@ -208,6 +214,25 @@ This code will also add static finders:
domain.TestUser.findByFirstOrLastName('Tester',function() {});


## Associations

couch-ar now supports hasMany associations. To define a hasMany association pass a __hasMany__ in the config as shown above.
The value 'PhoneNumber' is a previously defined model.

When you supply a property for hasMany it will by default try to make it singular by chopping the 's' off of the end. The result is three new methods.

Let's assume you have defined 'phoneNumbers' as your association. The three new methods would be:

* __addPhoneNumber(pn)__
* __getPhoneNumbers(callback)__
* __removePhoneNumber(pn)__

the add and remove can take a phone number directly. However, getPhoneNumbers() requires a callback which will be passed the result.
The items passed to add and remove must be persisted before trying to add or remove them.




## License

Provided under the MIT license. In other words, do what you want with it.
Expand Down
62 changes: 6 additions & 56 deletions couch-ar.js
@@ -1,6 +1,7 @@
var cradle = require('cradle');
var fs = require('fs');

var Base = require('./Base');
var helpers = require('./helpers');
var databases = {}
var defaultDbName;

Expand Down Expand Up @@ -57,7 +58,7 @@ exports.create = function(name, config, constr) {
config.properties.lastUpdated = {};

var factory = function() {
var c = Base();
var c = Base(db, name, config);
constr && constr.call(c,c);
c.properties = config.properties;
return c;
Expand Down Expand Up @@ -93,7 +94,7 @@ exports.create = function(name, config, constr) {


function addFinders(finderName) {
var upperName = toUpper(finderName);
var upperName = helpers.toUpper(finderName);

factory['findAllBy' + upperName] = function(value, callback) {
executeView(finderName, value, callback);
Expand Down Expand Up @@ -142,22 +143,17 @@ exports.create = function(name, config, constr) {
}


function toUpper(s) {
return s[0].toUpperCase() + s.slice(1);
}


function addView(viewName, viewDef, callback) {
db.get('_design/' + name, function(err, res) {
res.views[viewName] = wrapView(name, viewDef);
saveView(res.views, callback);
});
function saveView(views, callback) {
db.save('_design/' + name, views, function(err, res) {
factory['findAllBy' + toUpper(viewName)] = function(value, callback) {
factory['findAllBy' + helpers.toUpper(viewName)] = function(value, callback) {
executeView(viewName, value, callback);
}
factory['findBy' + toUpper(viewName)] = function(value, callback) {
factory['findBy' + helpers.toUpper(viewName)] = function(value, callback) {
executeView(viewName, value, function(objects) {
callback(objects[0]);
});
Expand Down Expand Up @@ -224,52 +220,6 @@ exports.create = function(name, config, constr) {
callback && callback();
}

/**
* The base constructor that is extended by each domain constructor
* Contains the basic methods save/remove...
*/
function Base() {
var that = {};

that.serialize = function() {
var obj = Object.getOwnPropertyNames(config.properties).reduce(function(obj, prop) {
obj[prop] = that[prop];
return obj;
}, {});
obj.type = name;
obj._id = obj.id;
obj._rev = obj.rev;
return obj;
}

that.save = function(callback) {
callback = callback || function() {
}
that.beforeSave && that.beforeSave();
var out = that.serialize();
that.dateCreated = that.dateCreated || new Date();
that.lastUpdated = new Date();
db.save(that.id, that.serialize(), function(err, res) {
if (res.ok) {
that.id = res.id;
that.rev = res.rev
}
callback(err, res);
});
}

that.remove = function(callback) {
if (that.id) {
db.remove(that.id, that.rev, function(err, res) {
that.id = err ? that.id : undefined;
callback(err, res);
});
} else {
callback(err, res);
}
}
return that;
}
}


5 changes: 5 additions & 0 deletions helpers.js
@@ -0,0 +1,5 @@
module.exports = {
toUpper: function(s) {
return s[0].toUpperCase() + s.slice(1);
}
};
2 changes: 1 addition & 1 deletion test/runTests.sh
@@ -1,4 +1,4 @@
#!/bin/sh
export NODE_PATH=`pwd`/../node_modules:`pwd`/../
export NODE_PATH=`pwd`/../node_modules/jasmine-node/lib:`pwd`/../
../node_modules/jasmine-node/bin/jasmine-node spec

0 comments on commit 16ac299

Please sign in to comment.