diff --git a/lib/application.js b/lib/application.js index 590c65843..3390b54bb 100644 --- a/lib/application.js +++ b/lib/application.js @@ -122,7 +122,7 @@ app.model = function(Model, config) { Model = registry.createModel(modelConfig); // delete config options already applied - ['relations', 'base', 'acls', 'hidden'].forEach(function(prop) { + ['relations', 'base', 'acls', 'hidden', 'methods'].forEach(function(prop) { delete config[prop]; if (config.options) delete config.options[prop]; }); diff --git a/lib/registry.js b/lib/registry.js index 371cdf70b..64d079f6d 100644 --- a/lib/registry.js +++ b/lib/registry.js @@ -122,6 +122,8 @@ Registry.prototype.createModel = function(name, properties, options) { var model = BaseModel.extend(name, properties, options); model.registry = this; + this._defineRemoteMethods(model, options.methods); + // try to attach try { this.autoAttachModel(model); @@ -247,6 +249,30 @@ Registry.prototype.configureModel = function(ModelCtor, config) { 'Use `null` or `false` to mark models not attached to any data source.', modelName); } + + // Remote methods + this._defineRemoteMethods(ModelCtor, config.methods); +}; + +Registry.prototype._defineRemoteMethods = function(ModelCtor, methods) { + if (!methods) return; + if (typeof methods !== 'object') { + console.warn('Ignoring non-object "methods" setting of "%s".', + ModelCtor.modelName); + return; + } + + Object.keys(methods).forEach(function(key) { + var meta = methods[key]; + if (typeof meta.isStatic !== 'boolean') { + console.warn('Remoting metadata for "%s.%s" is missing "isStatic" ' + + 'flag, the method is registered as an instance method.', + ModelCtor.modelName, + key); + console.warn('This behaviour may change in the next major version.'); + } + ModelCtor.remoteMethod(key, meta); + }); }; /** diff --git a/test/loopback.test.js b/test/loopback.test.js index 95a54030e..9b39709e6 100644 --- a/test/loopback.test.js +++ b/test/loopback.test.js @@ -249,6 +249,30 @@ describe('loopback', function() { .to.throw(Error, new RegExp('Model not found: ' + uniqueModelName)); }); }); + + it('configures remote methods', function() { + var TestModel = loopback.createModel(uniqueModelName, {}, { + methods: { + staticMethod: { + isStatic: true, + http: { path: '/static' } + }, + instanceMethod: { + isStatic: false, + http: { path: '/instance' } + } + } + }); + + var methodNames = TestModel.sharedClass.methods().map(function(m) { + return m.stringName.replace(/^[^.]+\./, ''); // drop the class name + }); + + expect(methodNames).to.include.members([ + 'staticMethod', + 'prototype.instanceMethod' + ]); + }); }); describe('loopback.createModel(config)', function() { @@ -449,6 +473,32 @@ describe('loopback', function() { // configureModel MUST NOT change Model's base class expect(model.settings.base.name).to.equal(baseName); }); + + it('configures remote methods', function() { + var TestModel = loopback.createModel(uniqueModelName); + loopback.configureModel(TestModel, { + dataSource: null, + methods: { + staticMethod: { + isStatic: true, + http: { path: '/static' } + }, + instanceMethod: { + isStatic: false, + http: { path: '/instance' } + } + } + }); + + var methodNames = TestModel.sharedClass.methods().map(function(m) { + return m.stringName.replace(/^[^.]+\./, ''); // drop the class name + }); + + expect(methodNames).to.include.members([ + 'staticMethod', + 'prototype.instanceMethod' + ]); + }); }); describe('loopback object', function() {