Skip to content

Commit 7d67477

Browse files
author
Miroslav Bajtoš
committed
refactor: extract runtime and registry
Move isBrowser and isServer from lib/loopback to a new file lib/runtime. Move all Model and DataSource related methods like `createModel` and `createDataSource` to lib/registry. Remove the circular dependency between lib/application and lib/loopback, by loading lib/registry and/or lib/runtime instead of lib/loopback where appropriate This commit is only moving the code around, the functionality should not be changed at all.
1 parent 19425b8 commit 7d67477

File tree

6 files changed

+350
-274
lines changed

6 files changed

+350
-274
lines changed

docs.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
"content": [
44
"lib/application.js",
55
"lib/loopback.js",
6+
"lib/runtime.js",
7+
"lib/registry.js",
68
{ "title": "Base model", "depth": 2 },
79
"lib/models/model.js",
810
"lib/models/data-model.js",

lib/application.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
*/
44

55
var DataSource = require('loopback-datasource-juggler').DataSource
6-
, loopback = require('../')
6+
, registry = require('./registry')
77
, compat = require('./compat')
88
, assert = require('assert')
99
, fs = require('fs')
@@ -128,7 +128,7 @@ app.model = function (Model, config) {
128128
// modeller does not understand `dataSource` option
129129
delete modelConfig.dataSource;
130130

131-
Model = loopback.createModelFromConfig(modelConfig);
131+
Model = registry.createModelFromConfig(modelConfig);
132132

133133
// delete config options already applied
134134
['relations', 'base', 'acls', 'hidden'].forEach(function(prop) {
@@ -531,7 +531,7 @@ app.boot = function(options) {
531531

532532
// try to attach models to dataSources by type
533533
try {
534-
require('./loopback').autoAttach();
534+
registry.autoAttach();
535535
} catch(e) {
536536
if(e.name === 'AssertionError') {
537537
console.warn(e);
@@ -594,11 +594,11 @@ function dataSourcesFromConfig(config, connectorRegistry) {
594594
}
595595
}
596596

597-
return require('./loopback').createDataSource(config);
597+
return registry.createDataSource(config);
598598
}
599599

600600
function configureModel(ModelCtor, config, app) {
601-
assert(ModelCtor.prototype instanceof loopback.Model,
601+
assert(ModelCtor.prototype instanceof registry.Model,
602602
'Model must be a descendant of loopback.Model');
603603

604604
var dataSource = config.dataSource;
@@ -614,7 +614,7 @@ function configureModel(ModelCtor, config, app) {
614614
config = extend({}, config);
615615
config.dataSource = dataSource;
616616

617-
loopback.configureModel(ModelCtor, config);
617+
registry.configureModel(ModelCtor, config);
618618
}
619619

620620
function requireDir(dir, basenames) {

lib/loopback.js

Lines changed: 13 additions & 266 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
*/
44

55
var express = require('express')
6+
, proto = require('./application')
67
, fs = require('fs')
78
, ejs = require('ejs')
89
, EventEmitter = require('events').EventEmitter
@@ -29,18 +30,6 @@ var express = require('express')
2930

3031
var loopback = exports = module.exports = createApplication;
3132

32-
/**
33-
* True if running in a browser environment; false otherwise.
34-
*/
35-
36-
loopback.isBrowser = typeof window !== 'undefined';
37-
38-
/**
39-
* True if running in a server environment; false otherwise.
40-
*/
41-
42-
loopback.isServer = !loopback.isBrowser;
43-
4433
/**
4534
* Framework version.
4635
*/
@@ -68,9 +57,6 @@ loopback.compat = require('./compat');
6857
function createApplication() {
6958
var app = express();
7059

71-
// Defer loading of `./application` until all `loopback` static methods
72-
// are defined, because `./application` depends on loopback.
73-
var proto = require('./application');
7460
merge(app, proto);
7561

7662
// Create a new instance of models registry per each app instance
@@ -93,17 +79,23 @@ function createApplication() {
9379
return app;
9480
}
9581

82+
function mixin(source) {
83+
for (var key in source) {
84+
var desc = Object.getOwnPropertyDescriptor(source, key);
85+
Object.defineProperty(loopback, key, desc);
86+
}
87+
}
88+
89+
mixin(require('./runtime'));
90+
mixin(require('./registry'));
91+
9692
/*!
9793
* Expose express.middleware as loopback.*
9894
* for example `loopback.errorHandler` etc.
9995
*/
10096

101-
for (var key in express) {
102-
Object.defineProperty(
103-
loopback
104-
, key
105-
, Object.getOwnPropertyDescriptor(express, key));
106-
}
97+
mixin(express);
98+
10799

108100
/*!
109101
* Expose additional loopback middleware
@@ -129,143 +121,6 @@ if (loopback.isServer) {
129121

130122
loopback.errorHandler.title = 'Loopback';
131123

132-
/**
133-
* Create a data source with passing the provided options to the connector.
134-
*
135-
* @param {String} name Optional name.
136-
* @options {Object} Data Source options
137-
* @property {Object} connector LoopBack connector.
138-
* @property {*} Other properties See the relevant connector documentation.
139-
*/
140-
141-
loopback.createDataSource = function (name, options) {
142-
var ds = new DataSource(name, options, loopback.Model.modelBuilder);
143-
ds.createModel = function (name, properties, settings) {
144-
var ModelCtor = loopback.createModel(name, properties, settings);
145-
ModelCtor.attachTo(ds);
146-
return ModelCtor;
147-
};
148-
149-
if(ds.settings && ds.settings.defaultForType) {
150-
loopback.setDefaultDataSourceForType(ds.settings.defaultForType, ds);
151-
}
152-
153-
return ds;
154-
};
155-
156-
/**
157-
* Create a named vanilla JavaScript class constructor with an attached set of properties and options.
158-
*
159-
* @param {String} name Unique name.
160-
* @param {Object} properties
161-
* @param {Object} options (optional)
162-
*/
163-
164-
loopback.createModel = function (name, properties, options) {
165-
options = options || {};
166-
var BaseModel = options.base || options.super;
167-
168-
if(typeof BaseModel === 'string') {
169-
BaseModel = loopback.getModel(BaseModel);
170-
}
171-
172-
BaseModel = BaseModel || loopback.Model;
173-
174-
var model = BaseModel.extend(name, properties, options);
175-
176-
// try to attach
177-
try {
178-
loopback.autoAttachModel(model);
179-
} catch(e) {}
180-
181-
return model;
182-
};
183-
184-
/**
185-
* Create a model as described by the configuration object.
186-
*
187-
* @example
188-
*
189-
* ```js
190-
* loopback.createModelFromConfig({
191-
* name: 'Author',
192-
* properties: {
193-
* firstName: 'string',
194-
* lastName: 'string
195-
* },
196-
* relations: {
197-
* books: {
198-
* model: 'Book',
199-
* type: 'hasAndBelongsToMany'
200-
* }
201-
* }
202-
* });
203-
* ```
204-
*
205-
* @options {Object} model configuration
206-
* @property {String} name Unique name.
207-
* @property {Object=} properties Model properties
208-
* @property {Object=} options Model options. Options can be specified on the
209-
* top level config object too. E.g. `{ base: 'User' }` is the same as
210-
* `{ options: { base: 'User' } }`.
211-
*/
212-
loopback.createModelFromConfig = function(config) {
213-
var name = config.name;
214-
var properties = config.properties;
215-
var options = buildModelOptionsFromConfig(config);
216-
217-
assert(typeof name === 'string',
218-
'The model-config property `name` must be a string');
219-
220-
return loopback.createModel(name, properties, options);
221-
};
222-
223-
function buildModelOptionsFromConfig(config) {
224-
var options = merge({}, config.options);
225-
for (var key in config) {
226-
if (['name', 'properties', 'options'].indexOf(key) !== -1) {
227-
// Skip items which have special meaning
228-
continue;
229-
}
230-
231-
if (options[key] !== undefined) {
232-
// When both `config.key` and `config.options.key` are set,
233-
// use the latter one
234-
continue;
235-
}
236-
237-
options[key] = config[key];
238-
}
239-
return options;
240-
}
241-
242-
/**
243-
* Alter an existing Model class.
244-
* @param {Model} ModelCtor The model constructor to alter.
245-
* @options {Object} Additional configuration to apply
246-
* @property {DataSource} dataSource Attach the model to a dataSource.
247-
* @property {Object} relations Model relations to add/update.
248-
*/
249-
loopback.configureModel = function(ModelCtor, config) {
250-
var settings = ModelCtor.settings;
251-
252-
if (config.relations) {
253-
var relations = settings.relations = settings.relations || {};
254-
Object.keys(config.relations).forEach(function(key) {
255-
relations[key] = merge(relations[key] || {}, config.relations[key]);
256-
});
257-
}
258-
259-
// It's important to attach the datasource after we have updated
260-
// configuration, so that the datasource picks up updated relations
261-
if (config.dataSource) {
262-
assert(config.dataSource instanceof DataSource,
263-
'Cannot configure ' + ModelCtor.modelName +
264-
': config.dataSource must be an instance of loopback.DataSource');
265-
ModelCtor.attachTo(config.dataSource);
266-
}
267-
};
268-
269124
/**
270125
* Add a remote method to a model.
271126
* @param {Function} fn
@@ -298,119 +153,11 @@ loopback.template = function (file) {
298153
return ejs.compile(str);
299154
};
300155

301-
/**
302-
* Get an in-memory data source. Use one if it already exists.
303-
*
304-
* @param {String} [name] The name of the data source. If not provided, the `'default'` is used.
305-
*/
306-
307-
loopback.memory = function (name) {
308-
name = name || 'default';
309-
var memory = (
310-
this._memoryDataSources
311-
|| (this._memoryDataSources = {})
312-
)[name];
313-
314-
if(!memory) {
315-
memory = this._memoryDataSources[name] = loopback.createDataSource({
316-
connector: loopback.Memory
317-
});
318-
}
319-
320-
return memory;
321-
};
322-
323-
/**
324-
* Look up a model class by name from all models created by loopback.createModel()
325-
* @param {String} modelName The model name
326-
* @returns {Model} The model class
327-
*/
328-
loopback.getModel = function(modelName) {
329-
return loopback.Model.modelBuilder.models[modelName];
330-
};
331-
332-
/**
333-
* Look up a model class by the base model class. The method can be used by LoopBack
334-
* to find configured models in models.json over the base model.
335-
* @param {Model} The base model class
336-
* @returns {Model} The subclass if found or the base class
337-
*/
338-
loopback.getModelByType = function(modelType) {
339-
assert(typeof modelType === 'function', 'The model type must be a constructor');
340-
var models = loopback.Model.modelBuilder.models;
341-
for(var m in models) {
342-
if(models[m].prototype instanceof modelType) {
343-
return models[m];
344-
}
345-
}
346-
return modelType;
347-
};
348-
349-
/**
350-
* Set the default `dataSource` for a given `type`.
351-
* @param {String} type The datasource type
352-
* @param {Object|DataSource} dataSource The data source settings or instance
353-
* @returns {DataSource} The data source instance
354-
*/
355-
356-
loopback.setDefaultDataSourceForType = function(type, dataSource) {
357-
var defaultDataSources = this.defaultDataSources || (this.defaultDataSources = {});
358-
359-
if(!(dataSource instanceof DataSource)) {
360-
dataSource = this.createDataSource(dataSource);
361-
}
362-
363-
defaultDataSources[type] = dataSource;
364-
return dataSource;
365-
};
366-
367-
/**
368-
* Get the default `dataSource` for a given `type`.
369-
* @param {String} type The datasource type
370-
* @returns {DataSource} The data source instance
371-
*/
372-
373-
loopback.getDefaultDataSourceForType = function(type) {
374-
return this.defaultDataSources && this.defaultDataSources[type];
375-
};
376-
377-
/**
378-
* Attach any model that does not have a dataSource to
379-
* the default dataSource for the type the Model requests
380-
*/
381-
382-
loopback.autoAttach = function() {
383-
var models = this.Model.modelBuilder.models;
384-
assert.equal(typeof models, 'object', 'Cannot autoAttach without a models object');
385-
386-
Object.keys(models).forEach(function(modelName) {
387-
var ModelCtor = models[modelName];
388-
389-
// Only auto attach if the model doesn't have an explicit data source
390-
if(ModelCtor && (!(ModelCtor.dataSource instanceof DataSource))) {
391-
loopback.autoAttachModel(ModelCtor);
392-
}
393-
});
394-
};
395-
396-
loopback.autoAttachModel = function(ModelCtor) {
397-
if(ModelCtor.autoAttach) {
398-
var ds = loopback.getDefaultDataSourceForType(ModelCtor.autoAttach);
399-
400-
assert(ds instanceof DataSource, 'cannot autoAttach model "'
401-
+ ModelCtor.modelName
402-
+ '". No dataSource found of type ' + ModelCtor.autoAttach);
403-
404-
ModelCtor.attachTo(ds);
405-
}
406-
};
407156

408157
/*!
409158
* Built in models / services
410159
*/
411160

412-
loopback.Model = require('./models/model');
413-
loopback.DataModel = require('./models/data-model');
414161
loopback.Email = require('./models/email');
415162
loopback.User = require('./models/user');
416163
loopback.Application = require('./models/application');

0 commit comments

Comments
 (0)