From 94ec5c294a517c24975259c882abd38a40f7ff52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20Bajto=C5=A1?= Date: Wed, 28 May 2014 15:02:55 +0200 Subject: [PATCH] app: implement `connector()` and `connectors` Allow browserified applications to explicitly register connectors to use in data-sources via `app.connector(name, exportsFromRequire)`. Include built-in connectors like `Memory` and `Remote` in the registry. Modify `dataSourcesFromConfig()` to resolve the connector via `app.connectors` first and only then fall back to auto-require the connector module. --- lib/application.js | 36 +++++++++++++++++++++++++++++++----- lib/loopback.js | 9 +++++++++ test/app.test.js | 43 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 83 insertions(+), 5 deletions(-) diff --git a/lib/application.js b/lib/application.js index 3ceed368..65545618 100644 --- a/lib/application.js +++ b/lib/application.js @@ -196,9 +196,30 @@ app.models = function () { app.dataSource = function (name, config) { this.dataSources[name] = this.dataSources[classify(name)] = - this.dataSources[camelize(name)] = dataSourcesFromConfig(config); + this.dataSources[camelize(name)] = + dataSourcesFromConfig(config, this.connectors); } +/** + * Register a connector. + * + * When a new data-source is being added via `app.dataSource`, the connector + * name is looked up in the registered connectors first. + * + * Connectors are required to be explicitly registered only for applications + * using browserify, because browserify does not support dynamic require, + * which is used by LoopBack to automatically load the connector module. + * + * @param {String} name Name of the connector, e.g. 'mysql'. + * @param {Object} connector Connector object as returned + * by `require('loopback-connector-{name}')`. + */ +app.connector = function(name, connector) { + this.connectors[name] = + this.connectors[classify(name)] = + this.connectors[camelize(name)] = connector; +}; + /** * Get all remote objects. * @returns {Object} [Remote objects](http://apidocs.strongloop.com/strong-remoting/#remoteobjectsoptions). @@ -523,17 +544,22 @@ function camelize(str) { return stringUtils.camelize(str); } -function dataSourcesFromConfig(config) { +function dataSourcesFromConfig(config, connectorRegistry) { var connectorPath; assert(typeof config === 'object', 'cannont create data source without config object'); if(typeof config.connector === 'string') { - connectorPath = path.join(__dirname, 'connectors', config.connector+'.js'); + var name = config.connector; + if (connectorRegistry[name]) { + config.connector = connectorRegistry[name]; + } else { + connectorPath = path.join(__dirname, 'connectors', name + '.js'); - if(fs.existsSync(connectorPath)) { - config.connector = require(connectorPath); + if (fs.existsSync(connectorPath)) { + config.connector = require(connectorPath); + } } } diff --git a/lib/loopback.js b/lib/loopback.js index 81a5c20b..d2017ee7 100644 --- a/lib/loopback.js +++ b/lib/loopback.js @@ -79,6 +79,15 @@ function createApplication() { // Create a new instance of datasources registry per each app instance app.datasources = app.dataSources = {}; + // Create a new instance of connector registry per each app instance + app.connectors = {}; + + // Register built-in connectors. It's important to keep this code + // hand-written, so that all require() calls are static + // and thus browserify can process them (include connectors in the bundle) + app.connector('memory', loopback.Memory); + app.connector('remote', loopback.Remote); + return app; } diff --git a/test/app.test.js b/test/app.test.js index 09a66c5c..3d0f3f56 100644 --- a/test/app.test.js +++ b/test/app.test.js @@ -128,6 +128,14 @@ describe('app', function() { }); }); + describe('app.dataSource', function() { + it('looks up the connector in `app.connectors`', function() { + app.connector('custom', loopback.Memory); + app.dataSource('custom', { connector: 'custom' }); + expect(app.dataSources.custom.name).to.equal(loopback.Memory.name); + }); + }); + describe('app.boot([options])', function () { beforeEach(function () { app.boot({ @@ -502,4 +510,39 @@ describe('app', function() { }); }); }); + + describe('app.connectors', function() { + it('is unique per app instance', function() { + app.connectors.foo = 'bar'; + var anotherApp = loopback(); + expect(anotherApp.connectors.foo).to.equal(undefined); + }); + + it('includes Remote connector', function() { + expect(app.connectors.remote).to.equal(loopback.Remote); + }); + + it('includes Memory connector', function() { + expect(app.connectors.memory).to.equal(loopback.Memory); + }); + }); + + describe('app.connector', function() { + // any connector will do + it('adds the connector to the registry', function() { + app.connector('foo-bar', loopback.Memory); + expect(app.connectors['foo-bar']).to.equal(loopback.Memory); + }); + + it('adds a classified alias', function() { + app.connector('foo-bar', loopback.Memory); + expect(app.connectors.FooBar).to.equal(loopback.Memory); + }); + + it('adds a camelized alias', function() { + app.connector('FOO-BAR', loopback.Memory); + console.log(app.connectors); + expect(app.connectors.FOOBAR).to.equal(loopback.Memory); + }); + }); });