Merge pull request #100 from shlomiassaf:simpleModelDef
Extend options arg to support custom model definitions Close #100 Close #49
This commit is contained in:
commit
2815a42d81
1
.jscsrc
1
.jscsrc
|
@ -5,6 +5,7 @@
|
|||
"try",
|
||||
"catch"
|
||||
],
|
||||
"disallowMultipleVarDecl": "exceptUndefined",
|
||||
"disallowSpacesInsideObjectBrackets": null,
|
||||
"requireSpaceAfterLineComment": true,
|
||||
"maximumLineLength": {
|
||||
|
|
3
index.js
3
index.js
|
@ -97,6 +97,9 @@ var addInstructionsToBrowserify = require('./lib/bundler');
|
|||
* @property {String} [appConfigRootDir] Directory to use when loading
|
||||
* `config.json`. Defaults to `appRootDir`.
|
||||
* @property {Object} [models] Object containing `Model` configurations.
|
||||
* @property {Array} [modelDefinitions] List of model definitions to use.
|
||||
* When `options.modelDefinitions` is provided, loopback-boot does not
|
||||
* search filesystem and use only the models provided in this argument.
|
||||
* @property {Object} [dataSources] Object containing `DataSource` definitions.
|
||||
* @property {String} [modelsRootDir] Directory to use when loading
|
||||
* `model-config.json`. Defaults to `appRootDir`.
|
||||
|
|
|
@ -82,7 +82,7 @@ module.exports = function compile(options) {
|
|||
|
||||
var modelSources = options.modelSources || modelsMeta.sources || ['./models'];
|
||||
var modelInstructions = buildAllModelInstructions(
|
||||
modelsRootDir, modelsConfig, modelSources);
|
||||
modelsRootDir, modelsConfig, modelSources, options.modelDefinitions);
|
||||
|
||||
var mixinDirs = options.mixinDirs || [];
|
||||
var mixinInstructions = buildAllMixinInstructions(
|
||||
|
@ -192,8 +192,10 @@ function tryReadDir() {
|
|||
}
|
||||
}
|
||||
|
||||
function buildAllModelInstructions(rootDir, modelsConfig, sources) {
|
||||
var registry = findModelDefinitions(rootDir, sources);
|
||||
function buildAllModelInstructions(rootDir, modelsConfig, sources,
|
||||
modelDefinitions) {
|
||||
var registry = verifyModelDefinitions(rootDir, modelDefinitions) ||
|
||||
findModelDefinitions(rootDir, sources);
|
||||
|
||||
var modelNamesToBuild = addAllBaseModels(registry, Object.keys(modelsConfig));
|
||||
|
||||
|
@ -274,6 +276,44 @@ function sortByInheritance(instructions) {
|
|||
});
|
||||
}
|
||||
|
||||
function verifyModelDefinitions(rootDir, modelDefinitions) {
|
||||
if (!modelDefinitions || modelDefinitions.length < 1) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
var registry = {};
|
||||
modelDefinitions.forEach(function(definition, idx) {
|
||||
if (definition.sourceFile) {
|
||||
var fullPath = path.resolve(rootDir, definition.sourceFile);
|
||||
definition.sourceFile = fixFileExtension(
|
||||
fullPath,
|
||||
tryReadDir(path.dirname(fullPath)),
|
||||
true);
|
||||
if (!definition.sourceFile) {
|
||||
debug('Model source code not found: %s - %s', definition.sourceFile);
|
||||
}
|
||||
}
|
||||
|
||||
debug('Found model "%s" - %s %s',
|
||||
definition.definition.name,
|
||||
'from options',
|
||||
definition.sourceFile ?
|
||||
path.relative(rootDir, definition.sourceFile) :
|
||||
'(no source file)');
|
||||
|
||||
var modelName = definition.definition.name;
|
||||
if (!modelName) {
|
||||
debug('Skipping model definition without Model name ' +
|
||||
'(from options.modelDefinitions @ index %s)',
|
||||
idx);
|
||||
return;
|
||||
}
|
||||
registry[modelName] = definition;
|
||||
});
|
||||
|
||||
return registry;
|
||||
}
|
||||
|
||||
function findModelDefinitions(rootDir, sources) {
|
||||
var registry = {};
|
||||
|
||||
|
|
|
@ -78,6 +78,207 @@ describe('compiler', function() {
|
|||
it('has datasources definition', function() {
|
||||
expect(instructions.dataSources).to.eql(options.dataSources);
|
||||
});
|
||||
|
||||
describe('with custom model definitions', function() {
|
||||
var dataSources = {
|
||||
'the-db': { connector: 'memory' }
|
||||
};
|
||||
|
||||
it('loads model without definition', function() {
|
||||
var instruction = boot.compile({
|
||||
appRootDir: appdir.PATH,
|
||||
models: {
|
||||
'model-without-definition': {
|
||||
dataSource: 'the-db'
|
||||
}
|
||||
},
|
||||
modelDefinitions: [],
|
||||
dataSources: dataSources
|
||||
});
|
||||
expect(instruction.models[0].name)
|
||||
.to.equal('model-without-definition');
|
||||
expect(instruction.models[0].definition).to.equal(undefined);
|
||||
expect(instruction.models[0].sourceFile).to.equal(undefined);
|
||||
});
|
||||
|
||||
it('loads coffeescript models', function() {
|
||||
var modelScript = appdir.writeFileSync(
|
||||
'custom-models/coffee-model-with-definition.coffee', '');
|
||||
var instruction = boot.compile({
|
||||
appRootDir: appdir.PATH,
|
||||
models: {
|
||||
'coffee-model-with-definition': {
|
||||
dataSource: 'the-db'
|
||||
}
|
||||
},
|
||||
modelDefinitions: [
|
||||
{
|
||||
definition: {
|
||||
name: 'coffee-model-with-definition'
|
||||
},
|
||||
sourceFile: modelScript
|
||||
}
|
||||
],
|
||||
dataSources: dataSources
|
||||
});
|
||||
expect(instruction.models[0].name)
|
||||
.to.equal('coffee-model-with-definition');
|
||||
expect(instruction.models[0].definition).to.eql({
|
||||
name: 'coffee-model-with-definition'
|
||||
});
|
||||
expect(instruction.models[0].sourceFile).to.equal(modelScript);
|
||||
});
|
||||
|
||||
it('handles sourceFile path without extension (.js)', function() {
|
||||
var modelScript = appdir.writeFileSync(
|
||||
'custom-models/model-without-ext.coffee',
|
||||
'');
|
||||
var instruction = boot.compile({
|
||||
appRootDir: appdir.PATH,
|
||||
models: {
|
||||
'model-without-ext': {
|
||||
dataSource: 'the-db'
|
||||
}
|
||||
},
|
||||
modelDefinitions: [{
|
||||
definition: {
|
||||
name: 'model-without-ext'
|
||||
},
|
||||
sourceFile: pathWithoutExtension(modelScript)
|
||||
}],
|
||||
dataSources: dataSources
|
||||
});
|
||||
expect(instruction.models[0].name).to.equal('model-without-ext');
|
||||
expect(instruction.models[0].sourceFile).to.equal(modelScript);
|
||||
});
|
||||
|
||||
it('handles sourceFile path without extension (.coffee)', function() {
|
||||
var modelScript = appdir.writeFileSync(
|
||||
'custom-models/model-without-ext.coffee',
|
||||
'');
|
||||
var instruction = boot.compile({
|
||||
appRootDir: appdir.PATH,
|
||||
models: {
|
||||
'model-without-ext': {
|
||||
dataSource: 'the-db'
|
||||
}
|
||||
},
|
||||
modelDefinitions: [{
|
||||
definition: {
|
||||
name: 'model-without-ext'
|
||||
},
|
||||
sourceFile: pathWithoutExtension(modelScript)
|
||||
}],
|
||||
dataSources: dataSources
|
||||
});
|
||||
expect(instruction.models[0].name).to.equal('model-without-ext');
|
||||
expect(instruction.models[0].sourceFile).to.equal(modelScript);
|
||||
});
|
||||
|
||||
it('sets source file path if the file exist', function() {
|
||||
var modelScript = appdir.writeFileSync(
|
||||
'custom-models/model-with-definition.js',
|
||||
'');
|
||||
var instruction = boot.compile({
|
||||
appRootDir: appdir.PATH,
|
||||
models: {
|
||||
'model-with-definition': {
|
||||
dataSource: 'the-db'
|
||||
}
|
||||
},
|
||||
modelDefinitions: [
|
||||
{
|
||||
definition: {
|
||||
name: 'model-with-definition'
|
||||
},
|
||||
sourceFile: modelScript
|
||||
}
|
||||
],
|
||||
dataSources: dataSources
|
||||
});
|
||||
expect(instruction.models[0].name).to.equal('model-with-definition');
|
||||
expect(instruction.models[0].definition).not.to.equal(undefined);
|
||||
expect(instruction.models[0].sourceFile).to.equal(modelScript);
|
||||
});
|
||||
|
||||
it('does not set source file path if the file does not exist.',
|
||||
function() {
|
||||
var instruction = boot.compile({
|
||||
appRootDir: appdir.PATH,
|
||||
models: {
|
||||
'model-with-definition-with-falsey-source-file': {
|
||||
dataSource: 'the-db'
|
||||
}
|
||||
},
|
||||
modelDefinitions: [
|
||||
{
|
||||
definition: {
|
||||
name: 'model-with-definition-with-falsey-source-file'
|
||||
},
|
||||
sourceFile: appdir.resolve('custom-models',
|
||||
'file-does-not-exist.js')
|
||||
}
|
||||
],
|
||||
dataSources: dataSources
|
||||
});
|
||||
expect(instruction.models[0].name)
|
||||
.to.equal('model-with-definition-with-falsey-source-file');
|
||||
expect(instruction.models[0].definition).not.to.equal(undefined);
|
||||
expect(instruction.models[0].sourceFile).to.equal(undefined);
|
||||
});
|
||||
|
||||
it('does not set source file path if no source file supplied.',
|
||||
function() {
|
||||
var instruction = boot.compile({
|
||||
appRootDir: appdir.PATH,
|
||||
models: {
|
||||
'model-with-definition-without-source-file-property': {
|
||||
dataSource: 'the-db'
|
||||
}
|
||||
},
|
||||
modelDefinitions: [
|
||||
{
|
||||
definition: {
|
||||
name: 'model-with-definition-without-source-file-property'
|
||||
}
|
||||
// sourceFile is not set
|
||||
}
|
||||
],
|
||||
dataSources: dataSources
|
||||
});
|
||||
expect(instruction.models[0].name)
|
||||
.to.equal('model-with-definition-without-source-file-property');
|
||||
expect(instruction.models[0].definition).not.to.equal(undefined);
|
||||
expect(instruction.models[0].sourceFile).to.equal(undefined);
|
||||
});
|
||||
|
||||
it('loads models defined in `models` only.', function() {
|
||||
var instruction = boot.compile({
|
||||
appRootDir: appdir.PATH,
|
||||
models: {
|
||||
'some-model': {
|
||||
dataSource: 'the-db'
|
||||
}
|
||||
},
|
||||
modelDefinitions: [
|
||||
{
|
||||
definition: {
|
||||
name: 'some-model'
|
||||
}
|
||||
},
|
||||
{
|
||||
definition: {
|
||||
name: 'another-model'
|
||||
}
|
||||
}
|
||||
],
|
||||
dataSources: dataSources
|
||||
});
|
||||
|
||||
expect(instruction.models.map(getNameProperty))
|
||||
.to.eql(['some-model']);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('from directory', function() {
|
||||
|
@ -1737,3 +1938,9 @@ function givenMiddlewareEntrySync(config) {
|
|||
function expectFirstMiddlewareParams(instructions) {
|
||||
return expect(instructions.middleware.middleware[0].config.params);
|
||||
}
|
||||
|
||||
function pathWithoutExtension(value) {
|
||||
return path.join(
|
||||
path.dirname(value),
|
||||
path.basename(value, path.extname(value)));
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue