Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge pull request #84 from rpoole/master

Add support for custom clauses
  • Loading branch information...
commit 1e1b39a1a5f15e602e4eb303baf434ca7e5c9d6b 2 parents ed36345 + 66bdb5b
@joeferner authored
View
34 README.md
@@ -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)
@@ -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)
View
29 lib/model.js
@@ -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 + "'");
}
@@ -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));
View
26 lib/query.js
@@ -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'"
@@ -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 + '"');
}
@@ -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;
View
34 test/chain.js
@@ -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;
@@ -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');
@@ -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();
});
},
@@ -131,6 +157,6 @@ exports['Chain'] = nodeunit.testCase({
test.equal(results.maxAge, 23);
test.done();
});
- }
+ },
-});
+});
View
26 test/define.js
@@ -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();
+ },
});
Please sign in to comment.
Something went wrong with that request. Please try again.