Merge pull request #60 from strongloop/feature/skip-builtin-loopback-models

Skip definitions of built-in loopback models
This commit is contained in:
Miroslav Bajtoš 2014-10-21 18:17:49 +02:00
commit b5c585291b
5 changed files with 96 additions and 1 deletions

View File

@ -5,6 +5,7 @@ var path = require('path');
var toposort = require('toposort');
var ConfigLoader = require('./config-loader');
var debug = require('debug')('loopback:boot:compiler');
var Module = require('module');
/**
* Gather all bootstrap-related configuration data and compile it into
@ -245,7 +246,12 @@ function findModelDefinitions(rootDir, sources) {
var registry = {};
sources.forEach(function(src) {
var srcDir = path.resolve(rootDir, src);
var srcDir = resolveSourceDir(rootDir, src);
if (!srcDir) {
debug('Skipping unknown module source dir %j', src);
return;
}
var files = tryReadDir(srcDir);
files
.filter(function(f) {
@ -267,6 +273,36 @@ function findModelDefinitions(rootDir, sources) {
return registry;
}
function resolveSourceDir(rootDir, sourceDir) {
var srcDir = path.resolve(rootDir, sourceDir);
if (fs.existsSync(srcDir))
return srcDir;
// Handle module-relative path, e.g. `loopback/common/models`
var start = sourceDir.substring(0, 2);
if (start !== './' && start !== '..') {
// Module.globalPaths is a list of globally configured paths like
// [ env.NODE_PATH values, $HOME/.node_modules, etc. ]
// Module._nodeModulePaths(rootDir) returns a list of paths like
// [ rootDir/node_modules, rootDir/../node_modules, etc. ]
var modulePaths = Module.globalPaths
.concat(Module._nodeModulePaths(rootDir));
srcDir = modulePaths
.map(function(candidateDir) {
return path.join(candidateDir, sourceDir);
})
.filter(function(candidate) {
return fs.existsSync(candidate);
})
[0];
if (srcDir)
return srcDir;
}
return undefined;
}
function loadModelDefinition(rootDir, jsonFile) {
var definition = require(jsonFile);

View File

@ -156,6 +156,10 @@ function defineModels(app, instructions) {
throw new Error('Cannot configure unknown model ' + name);
}
debug('Configuring existing model %s', name);
} else if (isBuiltinLoopBackModel(app, data)) {
model = app.loopback[name];
assert(model, 'app.loopback[model] should have been defined');
debug('Configuring built-in LoopBack model %s', name);
} else {
debug('Creating new model %s %j', name, data.definition);
model = app.loopback.createModel(data.definition);
@ -176,6 +180,15 @@ function defineModels(app, instructions) {
});
}
function isBuiltinLoopBackModel(app, data) {
// 1. Built-in models are exposed on the loopback object
if (!app.loopback[data.name]) return false;
// 2. Built-in models have a script file `loopback/{facet}/models/{name}.js`
return data.sourceFile &&
/node_modules\/loopback\/[^\/]+\/models\/[^\/]+\.js$/.test(data.sourceFile);
}
function forEachKeyedObject(obj, fn) {
if(typeof obj !== 'object') return;

View File

@ -501,6 +501,30 @@ describe('compiler', function() {
});
});
it('supports sources relative to node_modules', function() {
appdir.createConfigFilesSync({}, {}, {
User: { dataSource: 'db' }
});
var instructions = boot.compile({
appRootDir: appdir.PATH,
modelSources: [
'loopback/common/models',
'loopback/common/dir-does-not-exist'
]
});
expect(instructions.models).to.have.length(1);
expect(instructions.models[0]).to.eql({
name: 'User',
config: {
dataSource: 'db'
},
definition: require('loopback/common/models/user.json'),
sourceFile: require.resolve('loopback/common/models/user.js')
});
});
it('handles model definitions with no code', function() {
appdir.createConfigFilesSync({}, {}, {
Car: { dataSource: 'db' }

View File

@ -160,6 +160,22 @@ describe('executor', function() {
expect(actual).to.equal('not attached');
});
it('skips definition of already defined LoopBack models', function() {
var builtinModel = {
name: 'User',
definition: fs.readJsonFileSync(
require.resolve('loopback/common/models/user.json')
),
config: { dataSource: 'db' },
sourceFile: require.resolve('loopback/common/models/user.js')
};
builtinModel.definition.redefined = true;
boot.execute(app, someInstructions({ models: [ builtinModel ] }));
expect(app.models.User.settings.redefined, 'redefined').to.not.equal(true);
});
describe('with boot and models files', function() {
beforeEach(function() {
process.bootFlags = process.bootFlags || [];

View File

@ -1,4 +1,10 @@
{
"_meta": {
"sources": [
"./models",
"loopback/common/models"
]
},
"Customer": {
"dataSource": "db"
}