Skip to content

Commit

Permalink
Merge pull request strongloop#321 from strongloop/feature/backport-mo…
Browse files Browse the repository at this point in the history
…del-from-config

Backport from 2.0: create model from config, configure model
  • Loading branch information
bajtos committed Jun 12, 2014
2 parents b5f0057 + 02d1c5e commit 828aec9
Show file tree
Hide file tree
Showing 10 changed files with 550 additions and 237 deletions.
2 changes: 2 additions & 0 deletions docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
"content": [
"lib/application.js",
"lib/loopback.js",
"lib/runtime.js",
"lib/registry.js",
{ "title": "Base model", "depth": 2 },
"lib/models/model.js",
"lib/models/data-model.js",
Expand Down
112 changes: 57 additions & 55 deletions lib/application.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
*/

var DataSource = require('loopback-datasource-juggler').DataSource
, ModelBuilder = require('loopback-datasource-juggler').ModelBuilder
, registry = require('./registry')
, compat = require('./compat')
, assert = require('assert')
, fs = require('fs')
Expand Down Expand Up @@ -82,43 +82,70 @@ app.disuse = function (route) {
}

/**
* Define and attach a model to the app. The `Model` will be available on the
* Attach a model to the app. The `Model` will be available on the
* `app.models` object.
*
* ```js
* var Widget = app.model('Widget', {dataSource: 'db'});
* Widget.create({name: 'pencil'});
* app.models.Widget.find(function(err, widgets) {
* console.log(widgets[0]); // => {name: 'pencil'}
* // Attach an existing model
* var User = loopback.User;
* app.model(User);
*
* // Attach an existing model, alter some aspects of the model
* var User = loopback.User;
* app.model(User, { dataSource: 'db' });
*
* // The old way: create and attach a new model (deprecated)
* var Widget = app.model('Widget', {
* dataSource: 'db',
* properties: {
* name: 'string'
* }
* });
* ```
*
* @param {String} modelName The name of the model to define.
* @param {Object|String} Model The model to attach.
* @options {Object} config The model's configuration.
* @property {String|DataSource} dataSource The `DataSource` to which to attach the model.
* @property {Object} [options] an object containing `Model` options.
* @property {ACL[]} [options.acls] an array of `ACL` definitions.
* @property {String[]} [options.hidden] **experimental** an array of properties to hide when accessed remotely.
* @property {Object} [properties] object defining the `Model` properties in [LoopBack Definition Language](http://docs.strongloop.com/loopback-datasource-juggler/#loopback-definition-language).
* @property {String|DataSource} dataSource The `DataSource` to which to
* attach the model.
* @property {Boolean} [public] whether the model should be exposed via REST API
* @property {Object} [relations] relations to add/update
* @end
* @returns {ModelConstructor} the model class
*/

app.model = function (Model, config) {
var modelName = null;
var isPublic = true;
if (arguments.length > 1) {
config = config || {};
modelName = Model;
assert(typeof modelName === 'string', 'app.model(name, config) => "name" name must be a string');
Model = modelFromConfig(modelName, config, this);
if (typeof Model === 'string') {
// create & attach the model - backwards compatibility

// create config for loopback.modelFromConfig
var modelConfig = extend({}, config);
modelConfig.options = extend({}, config.options);
modelConfig.name = Model;

// modeller does not understand `dataSource` option
delete modelConfig.dataSource;

Model = registry.createModel(modelConfig);

// delete config options already applied
['relations', 'base', 'acls', 'hidden'].forEach(function(prop) {
delete config[prop];
if (config.options) delete config.options[prop];
});
delete config.properties;
}

configureModel(Model, config, this);
isPublic = config.public !== false;
} else {
assert(typeof Model === 'function', 'app.model(Model) => Model must be a function / constructor');
modelName = Model.modelName;
assert(modelName, 'Model must have a "modelName" property');
assert(Model.prototype instanceof loopback.Model,
'Model must be a descendant of loopback.Model');
}

var modelName = Model.modelName;
this.models[modelName] =
this.models[classify(modelName)] =
this.models[camelize(modelName)] = Model;
Expand Down Expand Up @@ -504,7 +531,7 @@ app.boot = function(options) {

// try to attach models to dataSources by type
try {
require('./loopback').autoAttach();
registry.autoAttach();
} catch(e) {
if(e.name === 'AssertionError') {
console.warn(e);
Expand Down Expand Up @@ -567,44 +594,27 @@ function dataSourcesFromConfig(config, connectorRegistry) {
}
}

return require('./loopback').createDataSource(config);
return registry.createDataSource(config);
}

function modelFromConfig(name, config, app) {
var options = buildModelOptionsFromConfig(config);
var properties = config.properties;
function configureModel(ModelCtor, config, app) {
assert(ModelCtor.prototype instanceof registry.Model,
'Model must be a descendant of loopback.Model');

var ModelCtor = require('./loopback').createModel(name, properties, options);
var dataSource = config.dataSource;

if(typeof dataSource === 'string') {
dataSource = app.dataSources[dataSource];
}

assert(isDataSource(dataSource), name + ' is referencing a dataSource that does not exist: "'+ config.dataSource +'"');
assert(dataSource instanceof DataSource,
ModelCtor.modelName + ' is referencing a dataSource that does not exist: "' +
config.dataSource +'"');

ModelCtor.attachTo(dataSource);
return ModelCtor;
}
config = extend({}, config);
config.dataSource = dataSource;

function buildModelOptionsFromConfig(config) {
var options = extend({}, config.options);
for (var key in config) {
if (['properties', 'options', 'dataSource'].indexOf(key) !== -1) {
// Skip items which have special meaning
continue;
}

if (options[key] !== undefined) {
// When both `config.key` and `config.options.key` are set,
// use the latter one to preserve backwards compatibility
// with loopback 1.x
continue;
}

options[key] = config[key];
}
return options;
registry.configureModel(ModelCtor, config);
}

function requireDir(dir, basenames) {
Expand Down Expand Up @@ -676,14 +686,6 @@ function tryReadDir() {
}
}

function isModelCtor(obj) {
return typeof obj === 'function' && obj.modelName && obj.name === 'ModelCtor';
}

function isDataSource(obj) {
return obj instanceof DataSource;
}

function tryReadConfig(cwd, fileName) {
try {
return require(path.join(cwd, fileName + '.json'));
Expand Down
Loading

0 comments on commit 828aec9

Please sign in to comment.