Enable custom methods on singular relations
This commit is contained in:
parent
579895d1b3
commit
8f88a42395
|
@ -90,6 +90,14 @@ function extendScopeMethods(definition, scopeMethods, ext) {
|
||||||
return [].concat(customMethods || []);
|
return [].concat(customMethods || []);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function bindRelationMethods(relation, relationMethod, definition) {
|
||||||
|
var methods = definition.methods || {};
|
||||||
|
Object.keys(methods).forEach(function(m) {
|
||||||
|
if (typeof methods[m] !== 'function') return;
|
||||||
|
relationMethod[m] = methods[m].bind(relation);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Relation definition class. Use to define relationships between models.
|
* Relation definition class. Use to define relationships between models.
|
||||||
* @param {Object} definition
|
* @param {Object} definition
|
||||||
|
@ -120,6 +128,7 @@ function RelationDefinition(definition) {
|
||||||
this.options = definition.options || {};
|
this.options = definition.options || {};
|
||||||
this.scope = definition.scope;
|
this.scope = definition.scope;
|
||||||
this.embed = definition.embed === true;
|
this.embed = definition.embed === true;
|
||||||
|
this.methods = definition.methods || {};
|
||||||
}
|
}
|
||||||
|
|
||||||
RelationDefinition.prototype.toJSON = function () {
|
RelationDefinition.prototype.toJSON = function () {
|
||||||
|
@ -159,13 +168,22 @@ RelationDefinition.prototype.defineMethod = function(name, fn) {
|
||||||
var relationName = this.name;
|
var relationName = this.name;
|
||||||
var modelFrom = this.modelFrom;
|
var modelFrom = this.modelFrom;
|
||||||
var definition = this;
|
var definition = this;
|
||||||
|
var method;
|
||||||
|
if (definition.multiple) {
|
||||||
var scope = this.modelFrom.scopes[this.name];
|
var scope = this.modelFrom.scopes[this.name];
|
||||||
if (!scope) throw new Error('Unknown relation scope: ' + this.name);
|
if (!scope) throw new Error('Unknown relation scope: ' + this.name);
|
||||||
var method = scope.defineMethod(name, function() {
|
method = scope.defineMethod(name, function() {
|
||||||
var relation = new relationClass(definition, this);
|
var relation = new relationClass(definition, this);
|
||||||
return fn.apply(relation, arguments);
|
return fn.apply(relation, arguments);
|
||||||
});
|
});
|
||||||
if (fn.shared) {
|
} else {
|
||||||
|
definition.methods[name] = fn;
|
||||||
|
method = function() {
|
||||||
|
var rel = this[relationName];
|
||||||
|
return rel[name].apply(rel, arguments);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (method && fn.shared) {
|
||||||
sharedMethod(definition, name, method, fn);
|
sharedMethod(definition, name, method, fn);
|
||||||
modelFrom.prototype['__' + name + '__' + relationName] = method;
|
modelFrom.prototype['__' + name + '__' + relationName] = method;
|
||||||
}
|
}
|
||||||
|
@ -1152,7 +1170,8 @@ RelationDefinition.belongsTo = function (modelFrom, modelTo, params) {
|
||||||
properties: params.properties,
|
properties: params.properties,
|
||||||
scope: params.scope,
|
scope: params.scope,
|
||||||
options: params.options,
|
options: params.options,
|
||||||
polymorphic: polymorphic
|
polymorphic: polymorphic,
|
||||||
|
methods: params.methods
|
||||||
});
|
});
|
||||||
|
|
||||||
// Define a property for the scope so that we have 'this' for the scoped methods
|
// Define a property for the scope so that we have 'this' for the scoped methods
|
||||||
|
@ -1169,6 +1188,7 @@ RelationDefinition.belongsTo = function (modelFrom, modelTo, params) {
|
||||||
relationMethod.build = relation.build.bind(relation);
|
relationMethod.build = relation.build.bind(relation);
|
||||||
relationMethod._targetClass = definition.modelTo.modelName;
|
relationMethod._targetClass = definition.modelTo.modelName;
|
||||||
}
|
}
|
||||||
|
bindRelationMethods(relation, relationMethod, definition);
|
||||||
return relationMethod;
|
return relationMethod;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -1466,7 +1486,8 @@ RelationDefinition.hasOne = function (modelFrom, modelTo, params) {
|
||||||
properties: params.properties,
|
properties: params.properties,
|
||||||
scope: params.scope,
|
scope: params.scope,
|
||||||
options: params.options,
|
options: params.options,
|
||||||
polymorphic: polymorphic
|
polymorphic: polymorphic,
|
||||||
|
methods: params.methods
|
||||||
});
|
});
|
||||||
|
|
||||||
modelTo.dataSource.defineForeignKey(modelTo.modelName, fk, modelFrom.modelName);
|
modelTo.dataSource.defineForeignKey(modelTo.modelName, fk, modelFrom.modelName);
|
||||||
|
@ -1483,6 +1504,7 @@ RelationDefinition.hasOne = function (modelFrom, modelTo, params) {
|
||||||
relationMethod.update = relation.update.bind(relation);
|
relationMethod.update = relation.update.bind(relation);
|
||||||
relationMethod.destroy = relation.destroy.bind(relation);
|
relationMethod.destroy = relation.destroy.bind(relation);
|
||||||
relationMethod._targetClass = definition.modelTo.modelName;
|
relationMethod._targetClass = definition.modelTo.modelName;
|
||||||
|
bindRelationMethods(relation, relationMethod, definition);
|
||||||
return relationMethod;
|
return relationMethod;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -1740,7 +1762,8 @@ RelationDefinition.embedsOne = function (modelFrom, modelTo, params) {
|
||||||
properties: params.properties,
|
properties: params.properties,
|
||||||
scope: params.scope,
|
scope: params.scope,
|
||||||
options: params.options,
|
options: params.options,
|
||||||
embed: true
|
embed: true,
|
||||||
|
methods: params.methods
|
||||||
});
|
});
|
||||||
|
|
||||||
var opts = { type: modelTo };
|
var opts = { type: modelTo };
|
||||||
|
@ -1785,6 +1808,7 @@ RelationDefinition.embedsOne = function (modelFrom, modelTo, params) {
|
||||||
relationMethod.destroy = relation.destroy.bind(relation);
|
relationMethod.destroy = relation.destroy.bind(relation);
|
||||||
relationMethod.value = relation.embeddedValue.bind(relation);
|
relationMethod.value = relation.embeddedValue.bind(relation);
|
||||||
relationMethod._targetClass = definition.modelTo.modelName;
|
relationMethod._targetClass = definition.modelTo.modelName;
|
||||||
|
bindRelationMethods(relation, relationMethod, definition);
|
||||||
return relationMethod;
|
return relationMethod;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -1617,12 +1617,30 @@ describe('relations', function () {
|
||||||
(new Item).list.should.be.an.instanceOf(Function);
|
(new Item).list.should.be.an.instanceOf(Function);
|
||||||
|
|
||||||
// syntax 2 (new)
|
// syntax 2 (new)
|
||||||
Fear.belongsTo('mind');
|
Fear.belongsTo('mind', {
|
||||||
|
methods: { check: function() { return true; } }
|
||||||
|
});
|
||||||
|
|
||||||
Object.keys((new Fear).toObject()).should.containEql('mindId');
|
Object.keys((new Fear).toObject()).should.containEql('mindId');
|
||||||
(new Fear).mind.should.be.an.instanceOf(Function);
|
(new Fear).mind.should.be.an.instanceOf(Function);
|
||||||
// (new Fear).mind.build().should.be.an.instanceOf(Mind);
|
// (new Fear).mind.build().should.be.an.instanceOf(Mind);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should setup a custom method on accessor', function() {
|
||||||
|
var rel = Fear.relations['mind'];
|
||||||
|
rel.defineMethod('other', function() {
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should have setup a custom method on accessor', function() {
|
||||||
|
var f = new Fear();
|
||||||
|
f.mind.check.should.be.a.function;
|
||||||
|
f.mind.check().should.be.true;
|
||||||
|
f.mind.other.should.be.a.function;
|
||||||
|
f.mind.other().should.be.true;
|
||||||
|
});
|
||||||
|
|
||||||
it('can be used to query data', function (done) {
|
it('can be used to query data', function (done) {
|
||||||
List.hasMany('todos', {model: Item});
|
List.hasMany('todos', {model: Item});
|
||||||
db.automigrate(function () {
|
db.automigrate(function () {
|
||||||
|
@ -1823,11 +1841,29 @@ describe('relations', function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can be declared using hasOne method', function () {
|
it('can be declared using hasOne method', function () {
|
||||||
Supplier.hasOne(Account, { properties: { name: 'supplierName' } });
|
Supplier.hasOne(Account, {
|
||||||
|
properties: { name: 'supplierName' },
|
||||||
|
methods: { check: function() { return true; } }
|
||||||
|
});
|
||||||
Object.keys((new Account()).toObject()).should.containEql('supplierId');
|
Object.keys((new Account()).toObject()).should.containEql('supplierId');
|
||||||
(new Supplier()).account.should.be.an.instanceOf(Function);
|
(new Supplier()).account.should.be.an.instanceOf(Function);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should setup a custom method on accessor', function() {
|
||||||
|
var rel = Supplier.relations['account'];
|
||||||
|
rel.defineMethod('other', function() {
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should have setup a custom method on accessor', function() {
|
||||||
|
var s = new Supplier();
|
||||||
|
s.account.check.should.be.a.function;
|
||||||
|
s.account.check().should.be.true;
|
||||||
|
s.account.other.should.be.a.function;
|
||||||
|
s.account.other().should.be.true;
|
||||||
|
});
|
||||||
|
|
||||||
it('can be used to query data', function (done) {
|
it('can be used to query data', function (done) {
|
||||||
db.automigrate(function () {
|
db.automigrate(function () {
|
||||||
Supplier.create({name: 'Supplier 1'}, function (e, supplier) {
|
Supplier.create({name: 'Supplier 1'}, function (e, supplier) {
|
||||||
|
@ -2213,7 +2249,8 @@ describe('relations', function () {
|
||||||
|
|
||||||
it('can be declared using embedsOne method', function (done) {
|
it('can be declared using embedsOne method', function (done) {
|
||||||
Person.embedsOne(Passport, {
|
Person.embedsOne(Passport, {
|
||||||
default: {name: 'Anonymous'} // a bit contrived
|
default: {name: 'Anonymous'}, // a bit contrived
|
||||||
|
methods: { check: function() { return true; } }
|
||||||
});
|
});
|
||||||
Person.embedsOne(Address); // all by default
|
Person.embedsOne(Address); // all by default
|
||||||
db.automigrate(done);
|
db.automigrate(done);
|
||||||
|
@ -2228,6 +2265,21 @@ describe('relations', function () {
|
||||||
p.passportItem.destroy.should.be.a.function;
|
p.passportItem.destroy.should.be.a.function;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should setup a custom method on accessor', function() {
|
||||||
|
var rel = Person.relations['passportItem'];
|
||||||
|
rel.defineMethod('other', function() {
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should have setup a custom method on accessor', function() {
|
||||||
|
var p = new Person();
|
||||||
|
p.passportItem.check.should.be.a.function;
|
||||||
|
p.passportItem.check().should.be.true;
|
||||||
|
p.passportItem.other.should.be.a.function;
|
||||||
|
p.passportItem.other().should.be.true;
|
||||||
|
});
|
||||||
|
|
||||||
it('should behave properly without default or being set', function (done) {
|
it('should behave properly without default or being set', function (done) {
|
||||||
var p = new Person();
|
var p = new Person();
|
||||||
should.not.exist(p.address);
|
should.not.exist(p.address);
|
||||||
|
|
Loading…
Reference in New Issue