Merge pull request #46 from strongloop/feature/proxy-shared-methods

Clone shared methods so that they can be customized per model
This commit is contained in:
Raymond Feng 2013-12-05 15:39:41 -08:00
commit 96ca0079cc
3 changed files with 70 additions and 12 deletions

View File

@ -90,14 +90,14 @@ function DataSource(name, settings, modelBuilder) {
var connector = this.connector;
// DataAccessObject - connector defined or supply the default
this.DataAccessObject = (connector && connector.DataAccessObject) ? connector.DataAccessObject : this.constructor.DataAccessObject;
this.DataAccessObject.apply(this, arguments);
var dao = (connector && connector.DataAccessObject) || this.constructor.DataAccessObject;
this.DataAccessObject = function() {};
// define DataAccessObject methods
Object.keys(this.DataAccessObject).forEach(function (name) {
var fn = this.DataAccessObject[name];
Object.keys(dao).forEach(function (name) {
var fn = dao[name];
this.DataAccessObject[name] = fn;
if(typeof fn === 'function') {
this.defineOperation(name, {
accepts: fn.accepts,
@ -111,11 +111,10 @@ function DataSource(name, settings, modelBuilder) {
}.bind(this));
// define DataAccessObject.prototype methods
Object.keys(this.DataAccessObject.prototype).forEach(function (name) {
var fn = this.DataAccessObject.prototype[name];
Object.keys(dao.prototype).forEach(function (name) {
var fn = dao.prototype[name];
this.DataAccessObject.prototype[name] = fn;
if(typeof fn === 'function') {
this.defineOperation(name, {
prototype: true,
accepts: fn.accepts,
@ -127,8 +126,11 @@ function DataSource(name, settings, modelBuilder) {
});
}
}.bind(this));
}
util.inherits(DataSource, EventEmitter);
// allow child classes to supply a data access object
@ -501,7 +503,7 @@ DataSource.prototype.mixin = function (ModelCtor) {
var DAO = this.DataAccessObject;
// mixin DAO
jutil.mixin(ModelCtor, DAO);
jutil.mixin(ModelCtor, DAO, {proxyFunctions : true});
// decorate operations as alias functions
Object.keys(ops).forEach(function (name) {

View File

@ -42,13 +42,25 @@ exports.mixin = function (newClass, mixinClass, options) {
options = options || {
staticProperties: true,
instanceProperties: true,
override: false
override: false,
proxyFunctions: false
};
if(options.staticProperties === undefined) {
options.staticProperties = true;
}
if(options.instanceProperties === undefined) {
options.instanceProperties = true;
}
if (options.staticProperties) {
Object.keys(mixinClass).forEach(function (classProp) {
if (classProp !== 'super_' && classProp !== '_mixins' && (!newClass.hasOwnProperty(classProp) || options.override)) {
var pd = Object.getOwnPropertyDescriptor(mixinClass, classProp);
if(options.proxyFunctions && pd.writable && typeof pd.value === 'function') {
pd.value = exports.proxy(pd.value);
}
Object.defineProperty(newClass, classProp, pd);
}
});
@ -59,6 +71,9 @@ exports.mixin = function (newClass, mixinClass, options) {
Object.keys(mixinClass.prototype).forEach(function (instanceProp) {
if (!newClass.prototype.hasOwnProperty(instanceProp) || options.override) {
var pd = Object.getOwnPropertyDescriptor(mixinClass.prototype, instanceProp);
if(options.proxyFunctions && pd.writable && typeof pd.value === 'function') {
pd.value = exports.proxy(pd.value);
}
Object.defineProperty(newClass.prototype, instanceProp, pd);
}
});
@ -68,3 +83,13 @@ exports.mixin = function (newClass, mixinClass, options) {
return newClass;
};
exports.proxy = function(fn) {
var f = function() {
return fn.apply(this, arguments);
};
Object.keys(fn).forEach(function(x) {
f[x] = fn[x];
});
return f;
};

View File

@ -673,3 +673,34 @@ describe('DataSource constructor', function(){
});
});
describe('Injected methods from connectors', function(){
it('are not shared across models for remote methods', function() {
var ds = new DataSource('memory');
var M1 = ds.createModel('M1');
var M2 = ds.createModel('M2');
// Remotable methods are not shared across models
assert.notEqual(M1.create, M2.create, 'Remotable methods are not shared');
assert.equal(M1.create.shared, true, 'M1.create is remotable');
assert.equal(M2.create.shared, true, 'M2.create is remotable');
M1.create.shared = false;
assert.equal(M1.create.shared, false, 'M1.create should be local now');
assert.equal(M2.create.shared, true, 'M2.create should stay remotable');
});
it('are not shared across models for non-remote methods', function() {
var ds = new DataSource('memory');
var M1 = ds.createModel('M1');
var M2 = ds.createModel('M2');
var m1 = M1.prototype.save;
var m2 = M2.prototype.save;
assert.notEqual(m1, m2, 'non-remote methods are not shared');
assert.equal(!!m1.shared, false, 'M1.save is not remotable');
assert.equal(!!m2.shared, false, 'M2.save is not remotable');
m1.shared = true;
assert.equal(m1.shared, true, 'M1.save is now remotable');
assert.equal(!!m2.shared, false, 'M2.save is not remotable');
});
});