Skip to content

Commit

Permalink
Merge pull request #84 from rpoole/master
Browse files Browse the repository at this point in the history
Add support for custom clauses
  • Loading branch information
joeferner committed Jul 22, 2013
2 parents ed36345 + 66bdb5b commit 1e1b39a
Show file tree
Hide file tree
Showing 5 changed files with 135 additions and 14 deletions.
34 changes: 34 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ You can install using Node Package Manager (npm):
* [update](#modelUpdate)
* [delete](#modelDelete)
* [getById](#modelGetById)
* [defineClause](#modelDefineClause)
* [onSave](#modelOnSave)
* [onLoad](#modelOnLoad)
* [Associated Object Properties](#associatedObjectProperties)
Expand Down Expand Up @@ -586,6 +587,39 @@ Person.getById(connection, 1, function(err, person) {
// person is the person with id equal to 1. Or null if not found
});
```
<a name="modelDefineClause" />
### Model.defineClause(clauseName, clauses)

Creates a custom method that is a composition of clauses. `this` is set to refer to the query.
you're constructing.

__Arguments__

* clauseName - The name of the clause to be attached to the model
* clauses - The function that describes the clause composition using a query.

__Example__
```javascript
Person.defineClause('clauseName', function(arg1, arg2, ...) {
return this.where('id < ?', arg1).orderBy('id').limit(5);
});

Person.clauseName(5).all(connection, function(err, people) {
// All the people with id < 5, ordered by id and limited to 5
});

Person.defineClause('clauseName2', function(connection, callback) {
return this
.where('id < 5')
.orderBy('id')
.limit(5)
.all(connection, callback);
});

Person.clauseName2(connection, function(err, people) {
// All the people with id < 5, ordered by id and limited to 5
});
```
<a name="modelOnSave" />
### Model.onSave(obj, connection, callback)

Expand Down
29 changes: 24 additions & 5 deletions lib/model.js
Original file line number Diff line number Diff line change
Expand Up @@ -106,10 +106,10 @@ function addAssociationMethod (obj, associationName, association) {
switch (association.type) {
case "hasMany":
addHasManyAssociationMethod(obj, associationName, association);
break;
break;
case "hasOne":
addHasOneAssociationMethod(obj, associationName, association);
break;
break;
default:
throw new Error("Invalid association type '" + association.type + "'");
}
Expand Down Expand Up @@ -214,17 +214,36 @@ exports.define = function (name, columnDefs, opts) {
Model.tableName = opts.tableName || inflection.pluralize(name);
Model.associations = {};
Model.columns = {};
Model.customClauses = {};

Model.eventEmmiter = new events.EventEmitter();
var n;
for (n in events.EventEmitter.prototype) {
Model[n] = events.EventEmitter.prototype[n];
/*
Model[n] = function() {
Model.eventEmmiter.apply(Model.eventEmmiter, arguments);
}*/
Model[n] = function() {
Model.eventEmmiter.apply(Model.eventEmmiter, arguments);
}*/
}

Model.defineClause = function(name, chainFn) {
if (Model[name] || Query[name] || this.customClauses[name]) {
throw new Error('You cannot define a custom clause using that name as one already exists.');
}

this.customClauses[name] = chainFn;

// add it to the model
Model[name] = function() {
var query = this.using(null);
return chainFn.apply(query, arguments);
};

Model[name] = persistUtil.bind(name, Model[name], Model);

return this;
};

Model.normalizeColumnDef = function (propertyName, columnDef) {
if (!columnDef) {
throw new Error(util.format('Invalid column definition for property "%s" of model "%s"', propertyName, this.modelName));
Expand Down
26 changes: 22 additions & 4 deletions lib/query.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,26 @@ var Query = function(connection, model) {
}
}
}

this._defineCustomClauses();
};

Query.prototype._defineCustomClauses = function() {
var customClauses = this.model.customClauses;

for (var name in customClauses) {
(function(name, query) {
var chainFn = customClauses[name];
var fn = function() {
return chainFn.apply(query, arguments);
};

query[name] = persistUtil.bind(name, fn, query);

})(name, this);
}
}

// "name = ?", "bob"
// "name = ? AND age = ?", ["bob", 6]
// "name = 'bob'"
Expand Down Expand Up @@ -83,8 +101,8 @@ Query.prototype.include = function() {
if (typeof (arguments[0]) === 'string') {
var associationPropertyName = arguments[0];
var association = this.model.associations[associationPropertyName]
|| this.model.associations[inflection.singularize(associationPropertyName)]
|| this.model.associations[inflection.pluralize(associationPropertyName)];
|| this.model.associations[inflection.singularize(associationPropertyName)]
|| this.model.associations[inflection.pluralize(associationPropertyName)];
if (!association) {
throw new Error('Could not find association "' + associationPropertyName + '" off of "' + this.model.modelName + '"');
}
Expand Down Expand Up @@ -141,8 +159,8 @@ Query.prototype.leftJoin = function(otherTable, otherTableId, thisTableId) {
Query.prototype._join = function(type, otherTable, otherTableId, thisTableId) {
if (arguments.length === 2) {
var association = this.model.associations[arguments[1]]
|| this.model.associations[inflection.singularize(arguments[1])]
|| this.model.associations[inflection.pluralize(arguments[1])];
|| this.model.associations[inflection.singularize(arguments[1])]
|| this.model.associations[inflection.pluralize(arguments[1])];
otherTable = association.model.tableName;
if (association.type === 'hasOne') {
otherTableId = this.model.getIdColumn().dbColumnName;
Expand Down
34 changes: 30 additions & 4 deletions test/chain.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,14 @@ exports['Chain'] = nodeunit.testCase({
"age": type.INTEGER
}).hasMany(this.Phone);

this.Person.defineClause('testClause', function(age) {
return this.where('age = ?', age || 21).where('name like "%Bob%"');
});

this.Person.defineClause('testClause2', function(connection, callback) {
return this.where('age = ?', 21).where('name like "%Bob%"').all(connection, callback);
});

testUtils.connect(persist, {}, function(err, connection) {
if(err) { console.log(err); return; }
self.connection = connection;
Expand Down Expand Up @@ -61,9 +69,15 @@ exports['Chain'] = nodeunit.testCase({
self.Phone.deleteAll,
self.Phone.all,
self.Person.first,
persist.runSqlAll('SELECT * FROM People')
persist.runSqlAll('SELECT * FROM People'),
self.Person.testClause(21).all,
self.Person.limit(5).testClause().all,
self.Person.limit(5).testClause2,
], function(err, results) {
if(err) { console.error(err); return; }
if (err) {
console.error(err);
return;
}

// person3.save
test.equal(results[0].name, 'fred');
Expand Down Expand Up @@ -115,6 +129,18 @@ exports['Chain'] = nodeunit.testCase({
// Person.first
test.ok(results[14].length, 5);

// Person.testClause
test.ok(results[15].length, 1);
test.ok(results[15][0].name, "Bob O'Neill");

// Person.limit(5).testClause
test.ok(results[16].length, 1);
test.ok(results[16][0].name, "Bob O'Neill");

// Person.limit(5).testClause2
test.ok(results[17].length, 1);
test.ok(results[17][0].name, "Bob O'Neill");

test.done();
});
},
Expand All @@ -131,6 +157,6 @@ exports['Chain'] = nodeunit.testCase({
test.equal(results.maxAge, 23);
test.done();
});
}
},

});
});
26 changes: 25 additions & 1 deletion test/define.js
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,30 @@ exports['Define'] = nodeunit.testCase({
test.equals(person.phones.length, 0);

test.done();
}
},

"defineClause": function(test) {
var Person = persist.define("Person", {
"name": type.STRING
}).defineClause('testClause', {
where: 'id = 1',
});

assert.isNotNullOrUndefined(Person.testClause, "Person.testClause is null or undefined");
test.done();
},

"defineClauseError": function(test) {
test.throws(function(){
var Person = persist.define("Person", {
"name": type.STRING
});

Person.property = 'hi';

Person.defineClause('property', {});
}, 'defineClause did not throw an error.');

test.done();
},
});

0 comments on commit 1e1b39a

Please sign in to comment.