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",
|
"try",
|
||||||
"catch"
|
"catch"
|
||||||
],
|
],
|
||||||
|
"disallowMultipleVarDecl": "exceptUndefined",
|
||||||
"disallowSpacesInsideObjectBrackets": null,
|
"disallowSpacesInsideObjectBrackets": null,
|
||||||
"requireSpaceAfterLineComment": true,
|
"requireSpaceAfterLineComment": true,
|
||||||
"maximumLineLength": {
|
"maximumLineLength": {
|
||||||
|
|
3
index.js
3
index.js
|
@ -97,6 +97,9 @@ var addInstructionsToBrowserify = require('./lib/bundler');
|
||||||
* @property {String} [appConfigRootDir] Directory to use when loading
|
* @property {String} [appConfigRootDir] Directory to use when loading
|
||||||
* `config.json`. Defaults to `appRootDir`.
|
* `config.json`. Defaults to `appRootDir`.
|
||||||
* @property {Object} [models] Object containing `Model` configurations.
|
* @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 {Object} [dataSources] Object containing `DataSource` definitions.
|
||||||
* @property {String} [modelsRootDir] Directory to use when loading
|
* @property {String} [modelsRootDir] Directory to use when loading
|
||||||
* `model-config.json`. Defaults to `appRootDir`.
|
* `model-config.json`. Defaults to `appRootDir`.
|
||||||
|
|
|
@ -82,7 +82,7 @@ module.exports = function compile(options) {
|
||||||
|
|
||||||
var modelSources = options.modelSources || modelsMeta.sources || ['./models'];
|
var modelSources = options.modelSources || modelsMeta.sources || ['./models'];
|
||||||
var modelInstructions = buildAllModelInstructions(
|
var modelInstructions = buildAllModelInstructions(
|
||||||
modelsRootDir, modelsConfig, modelSources);
|
modelsRootDir, modelsConfig, modelSources, options.modelDefinitions);
|
||||||
|
|
||||||
var mixinDirs = options.mixinDirs || [];
|
var mixinDirs = options.mixinDirs || [];
|
||||||
var mixinInstructions = buildAllMixinInstructions(
|
var mixinInstructions = buildAllMixinInstructions(
|
||||||
|
@ -192,8 +192,10 @@ function tryReadDir() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildAllModelInstructions(rootDir, modelsConfig, sources) {
|
function buildAllModelInstructions(rootDir, modelsConfig, sources,
|
||||||
var registry = findModelDefinitions(rootDir, sources);
|
modelDefinitions) {
|
||||||
|
var registry = verifyModelDefinitions(rootDir, modelDefinitions) ||
|
||||||
|
findModelDefinitions(rootDir, sources);
|
||||||
|
|
||||||
var modelNamesToBuild = addAllBaseModels(registry, Object.keys(modelsConfig));
|
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) {
|
function findModelDefinitions(rootDir, sources) {
|
||||||
var registry = {};
|
var registry = {};
|
||||||
|
|
||||||
|
|
|
@ -78,6 +78,207 @@ describe('compiler', function() {
|
||||||
it('has datasources definition', function() {
|
it('has datasources definition', function() {
|
||||||
expect(instructions.dataSources).to.eql(options.dataSources);
|
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() {
|
describe('from directory', function() {
|
||||||
|
@ -1737,3 +1938,9 @@ function givenMiddlewareEntrySync(config) {
|
||||||
function expectFirstMiddlewareParams(instructions) {
|
function expectFirstMiddlewareParams(instructions) {
|
||||||
return expect(instructions.middleware.middleware[0].config.params);
|
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