Refactor more functions into mixins

This commit is contained in:
Raymond Feng 2013-05-27 22:20:30 -07:00
parent 50b2036511
commit dd8512bc1f
9 changed files with 89 additions and 25 deletions

View File

@ -7,13 +7,12 @@ module.exports = DataAccessObject;
* Module dependencies * Module dependencies
*/ */
var util = require('util'); var util = require('util');
var jutil = require('./jutil');
var validations = require('./validations.js'); var validations = require('./validations.js');
var ValidationError = validations.ValidationError; var ValidationError = validations.ValidationError;
var List = require('./list.js'); var List = require('./list.js');
require('./hooks.js');
require('./relations.js'); require('./relations.js');
require('./include.js'); var Inclusion = require('./include.js');
/** /**
* DAO class - base class for all persist objects * DAO class - base class for all persist objects
@ -27,6 +26,13 @@ require('./include.js');
* @param {Object} data - initial object data * @param {Object} data - initial object data
*/ */
function DataAccessObject() { function DataAccessObject() {
if(DataAccessObject._mixins) {
var self = this;
var args = arguments;
DataAccessObject._mixins.forEach(function(m) {
m.call(self, args);
});
}
} }
@ -616,3 +622,6 @@ function defineReadonlyProp(obj, key, value) {
value: value value: value
}); });
} }
jutil.mixin(DataAccessObject, validations.Validatable);
jutil.mixin(DataAccessObject, Inclusion);

View File

@ -2,6 +2,7 @@
* Module dependencies * Module dependencies
*/ */
var ModelBuilder = require('./model-builder.js').ModelBuilder; var ModelBuilder = require('./model-builder.js').ModelBuilder;
var jutil = require('./jutil');
var ModelBaseClass = require('./model.js'); var ModelBaseClass = require('./model.js');
var DataAccessObject = require('./dao.js'); var DataAccessObject = require('./dao.js');
var List = require('./list.js'); var List = require('./list.js');
@ -53,11 +54,12 @@ function DataSource(name, settings) {
if (!(this instanceof DataSource)) { if (!(this instanceof DataSource)) {
return new DataSource(name, settings); return new DataSource(name, settings);
} }
ModelBuilder.call(this); ModelBuilder.call(this, arguments);
this.setup(name, settings); this.setup(name, settings);
// default DataAccessObject // default DataAccessObject
this.DataAccessObject = this.constructor.DataAccessObject; this.DataAccessObject = this.constructor.DataAccessObject;
this.DataAccessObject.call(this, arguments);
}; };
util.inherits(DataSource, ModelBuilder); util.inherits(DataSource, ModelBuilder);

View File

@ -1,12 +1,14 @@
/** /**
* Module exports * Module exports
*/ */
exports.Hookable = Hookable; module.exports = Hookable;
/** /**
* Hooks mixins for ./model.js * Hooks mixins
*/ */
var Hookable = require('./model.js');
function Hookable() {
}
/** /**
* List of hooks available * List of hooks available

View File

@ -1,7 +1,10 @@
/** /**
* Include mixin for ./model.js * Include mixin for ./model.js
*/ */
var DataAccessObject = require('./dao.js'); module.exports = Inclusion;
function Inclusion() {
}
/** /**
* Allows you to load relations of several objects and optimize numbers of requests. * Allows you to load relations of several objects and optimize numbers of requests.
@ -22,7 +25,7 @@ var DataAccessObject = require('./dao.js');
* - Passport.include(passports, {owner: [{posts: 'images'}, 'passports']}); // ... * - Passport.include(passports, {owner: [{posts: 'images'}, 'passports']}); // ...
* *
*/ */
DataAccessObject.include = function (objects, include, cb) { Inclusion.include = function (objects, include, cb) {
var self = this; var self = this;
if ( if (

View File

@ -1,9 +1,51 @@
/**
*
* @param newClass
* @param baseClass
*/
exports.inherits = function (newClass, baseClass) { exports.inherits = function (newClass, baseClass) {
Object.keys(baseClass).forEach(function (classMethod) { Object.keys(baseClass).forEach(function (classProp) {
newClass[classMethod] = baseClass[classMethod]; newClass[classProp] = baseClass[classProp];
}); });
Object.keys(baseClass.prototype).forEach(function (instanceMethod) { Object.keys(baseClass.prototype).forEach(function (instanceProp) {
newClass.prototype[instanceMethod] = baseClass.prototype[instanceMethod]; newClass.prototype[instanceProp] = baseClass.prototype[instanceProp];
}); });
}; };
/**
* Mix in the base class into the new class
* @param newClass
* @param baseClass
* @param options
*/
exports.mixin = function (newClass, baseClass, options) {
options = options || {
staticProperties: true,
instanceProperties: true,
override: true
};
if (options.staticProperties) {
Object.keys(baseClass).forEach(function (classProp) {
if (classProp !== 'super_' && (!newClass.hasOwnProperty(classProp) || options.override)) {
newClass[classProp] = baseClass[classProp];
}
});
}
if (options.instanceProperties) {
if (baseClass.prototype) {
Object.keys(baseClass.prototype).forEach(function (instanceProp) {
newClass.prototype[instanceProp] = baseClass.prototype[instanceProp];
});
}
}
if (Array.isArray(newClass._mixins)) {
newClass._mixins.push(baseClass);
} else {
newClass._mixins = [baseClass];
}
};

View File

@ -8,8 +8,9 @@ module.exports = ModelBaseClass;
*/ */
var util = require('util'); var util = require('util');
var List = require('./list.js'); var jutil = require('./jutil');
require('./hooks.js'); var List = require('./list');
var Hookable = require('./hooks');
var BASE_TYPES = ['String', 'Boolean', 'Number', 'Date', 'Text']; var BASE_TYPES = ['String', 'Boolean', 'Number', 'Date', 'Text'];
@ -242,3 +243,5 @@ function isdef(s) {
return s !== undef; return s !== undef;
} }
jutil.mixin(ModelBaseClass, Hookable);

View File

@ -7,9 +7,9 @@ var defineScope = require('./scope.js').defineScope;
/** /**
* Relations mixins for ./dao.js * Relations mixins for ./dao.js
*/ */
var Model = require('./dao.js'); var DataAccessObject = require('./dao.js');
Model.relationNameFor = function relationNameFor(foreignKey) { DataAccessObject.relationNameFor = function relationNameFor(foreignKey) {
for (var rel in this.relations) { for (var rel in this.relations) {
if (this.relations[rel].type === 'belongsTo' && this.relations[rel].keyFrom === foreignKey) { if (this.relations[rel].type === 'belongsTo' && this.relations[rel].keyFrom === foreignKey) {
return rel; return rel;
@ -20,11 +20,11 @@ Model.relationNameFor = function relationNameFor(foreignKey) {
/** /**
* Declare hasMany relation * Declare hasMany relation
* *
* @param {Model} anotherClass - class to has many * @param {DataAccessObject} anotherClass - class to has many
* @param {Object} params - configuration {as:, foreignKey:} * @param {Object} params - configuration {as:, foreignKey:}
* @example `User.hasMany(Post, {as: 'posts', foreignKey: 'authorId'});` * @example `User.hasMany(Post, {as: 'posts', foreignKey: 'authorId'});`
*/ */
Model.hasMany = function hasMany(anotherClass, params) { DataAccessObject.hasMany = function hasMany(anotherClass, params) {
var thisClass = this, thisClassName = this.modelName; var thisClass = this, thisClassName = this.modelName;
params = params || {}; params = params || {};
if (typeof anotherClass === 'string') { if (typeof anotherClass === 'string') {
@ -175,7 +175,7 @@ Model.hasMany = function hasMany(anotherClass, params) {
* *
* This optional parameter default value is false, so the related object will be loaded from cache if available. * This optional parameter default value is false, so the related object will be loaded from cache if available.
*/ */
Model.belongsTo = function (anotherClass, params) { DataAccessObject.belongsTo = function (anotherClass, params) {
params = params || {}; params = params || {};
if ('string' === typeof anotherClass) { if ('string' === typeof anotherClass) {
params.as = anotherClass; params.as = anotherClass;
@ -232,7 +232,7 @@ Model.belongsTo = function (anotherClass, params) {
if (!refresh && this.__cachedRelations && (typeof this.__cachedRelations[methodName] !== 'undefined')) { if (!refresh && this.__cachedRelations && (typeof this.__cachedRelations[methodName] !== 'undefined')) {
cachedValue = this.__cachedRelations[methodName]; cachedValue = this.__cachedRelations[methodName];
} }
if (p instanceof Model) { // acts as setter if (p instanceof DataAccessObject) { // acts as setter
this[fk] = p.id; this[fk] = p.id;
this.__cachedRelations[methodName] = p; this.__cachedRelations[methodName] = p;
} else if (typeof p === 'function') { // acts as async getter } else if (typeof p === 'function') { // acts as async getter
@ -263,7 +263,7 @@ Model.belongsTo = function (anotherClass, params) {
* *
* Post.hasAndBelongsToMany('tags'); creates connection model 'PostTag' * Post.hasAndBelongsToMany('tags'); creates connection model 'PostTag'
*/ */
Model.hasAndBelongsToMany = function hasAndBelongsToMany(anotherClass, params) { DataAccessObject.hasAndBelongsToMany = function hasAndBelongsToMany(anotherClass, params) {
params = params || {}; params = params || {};
var models = this.schema.models; var models = this.schema.models;

View File

@ -6,13 +6,13 @@ exports.defineScope = defineScope;
/** /**
* Scope mixin for ./model.js * Scope mixin for ./model.js
*/ */
var Model = require('./dao.js'); var DataAccessObject = require('./dao.js');
/** /**
* Define scope * Define scope
* TODO: describe behavior and usage examples * TODO: describe behavior and usage examples
*/ */
Model.scope = function (name, params) { DataAccessObject.scope = function (name, params) {
defineScope(this, this, name, params); defineScope(this, this, name, params);
}; };

View File

@ -18,7 +18,10 @@ exports.ValidationError = ValidationError;
* In more complicated cases it can be {Hash} of messages (for each case): * In more complicated cases it can be {Hash} of messages (for each case):
* `User.validatesLengthOf('password', { min: 6, max: 20, message: {min: 'too short', max: 'too long'}});` * `User.validatesLengthOf('password', { min: 6, max: 20, message: {min: 'too short', max: 'too long'}});`
*/ */
var Validatable = require('./dao.js'); exports.Validatable = Validatable;
function Validatable() {
}
/** /**
* Validate presence. This validation fails when validated field is blank. * Validate presence. This validation fails when validated field is blank.