Merge pull request #3777 from hswolff/lazy-models

Move Models module to have an init method that sets up all models
This commit is contained in:
Hannah Wolfe 2014-08-19 15:24:55 +01:00
commit 09383dc181
23 changed files with 143 additions and 95 deletions

View File

@ -651,9 +651,12 @@ var path = require('path'),
// in a "new" state.
grunt.registerTask('cleanDatabase', function () {
var done = this.async(),
models = require('./core/server/models'),
migration = require('./core/server/data/migration');
migration.reset().then(function () {
return models.init();
}).then(function () {
return migration.init();
}).then(function () {
done();

View File

@ -3,23 +3,19 @@
// When run from command line.
var when = require('when'),
bootstrap = require('./bootstrap');
bootstrap = require('./bootstrap'),
server = require('./server');
process.env.NODE_ENV = process.env.NODE_ENV || 'development';
function startGhost(options) {
// When we no longer need to require('./server')
// in a callback this extra deferred object
// won't be necessary, we'll just be able to return
// the server object directly.
var deferred = when.defer();
options = options || {};
bootstrap(options.config).then(function () {
try {
var ghost = require('./server');
return ghost(options.app)
return server(options.app)
.then(deferred.resolve)
.catch(function (err) {
// We don't return the rejected promise to stop

View File

@ -5,6 +5,7 @@ var _ = require('lodash'),
config = require('../config'),
canThis = require('../permissions').canThis,
errors = require('../errors'),
Models = require('../models'),
path = require('path'),
fs = require('fs'),
templatesDir = path.resolve(__dirname, '..', 'email-templates'),
@ -59,9 +60,7 @@ mail = {
* @returns {Promise}
*/
sendTest: function (options) {
var user = require('../models/user').User;
return user.findOne({id: options.context.user}).then(function (result) {
return Models.User.findOne({id: options.context.user}).then(function (result) {
return mail.generateContent({template: 'test'}).then(function (emailContent) {
var payload = {mail: [{
message: {

View File

@ -6,13 +6,7 @@ var canThis = require('../permissions').canThis,
when = require('when'),
slugs,
// `allowedTypes` is used to define allowed slug types and map them against its model class counterpart
allowedTypes = {
post: dataProvider.Post,
tag: dataProvider.Tag,
user: dataProvider.User,
app: dataProvider.App
};
allowedTypes;
/**
* ## Slugs API Methods
@ -31,6 +25,14 @@ slugs = {
generate: function (options) {
options = options || {};
// `allowedTypes` is used to define allowed slug types and map them against its model class counterpart
allowedTypes = {
post: dataProvider.Post,
tag: dataProvider.Tag,
user: dataProvider.User,
app: dataProvider.App
};
return canThis(options.context).generate.slug().then(function () {
if (allowedTypes[options.type] === undefined) {
return when.reject(new errors.BadRequestError('Unknown slug type \'' + options.type + '\'.'));

View File

@ -24,7 +24,6 @@ App = ghostBookshelf.Model.extend({
},
permissions: function () {
// Have to use the require here because of circular dependencies
return this.belongsToMany('Permission', 'permissions_apps');
},

View File

@ -22,6 +22,7 @@ var bookshelf = require('bookshelf'),
// ### ghostBookshelf
// Initializes a new Bookshelf instance called ghostBookshelf, for reference elsewhere in Ghost.
ghostBookshelf = bookshelf(config.database.knex);
// Load the registry plugin, which helps us avoid circular dependencies
ghostBookshelf.plugin('registry');

View File

@ -1,25 +1,46 @@
var _ = require('lodash'),
when = require('when'),
var _ = require('lodash'),
when = require('when'),
requireTree = require('../require-tree'),
models;
models = {
Post: require('./post').Post,
User: require('./user').User,
Role: require('./role').Role,
Permission: require('./permission').Permission,
Permissions: require('./permission').Permissions,
Settings: require('./settings').Settings,
Tag: require('./tag').Tag,
Base: require('./base'),
App: require('./app').App,
AppField: require('./appField').AppField,
AppSetting: require('./appSetting').AppSetting,
Client: require('./client').Client,
Accesstoken: require('./accesstoken').Accesstoken,
Refreshtoken: require('./refreshtoken').Refreshtoken,
excludeFiles: ['_messages', 'basetoken.js', 'base.js', 'index.js'],
// ### init
// Scan all files in this directory and then require each one and cache
// the objects exported onto this `models` object so that every other
// module can safely access models without fear of introducing circular
// dependency issues.
// @returns {Promise}
init: function () {
return true;
var self = this;
// One off inclusion of Base file.
self.Base = require('./base');
// Require all files in this directory
return requireTree.readAll(__dirname).then(function (modelFiles) {
// For each found file, excluding those we don't want,
// we will require it and cache it here.
_.each(modelFiles, function (path, fileName) {
// Return early if this fileName is one of the ones we want
// to exclude.
if (_.contains(self.excludeFiles, fileName)) {
return;
}
// Require the file.
var file = require(path);
// Cache its `export` object onto this object.
_.extend(self, file);
});
return;
});
},
// ### deleteAllContent
// Delete all content from the database (posts, tags, tags_posts)

View File

@ -6,9 +6,6 @@ var _ = require('lodash'),
Showdown = require('showdown'),
ghostgfm = require('../../shared/lib/showdown/extensions/ghostgfm'),
converter = new Showdown.converter({extensions: [ghostgfm]}),
Tag = require('./tag').Tag,
Tags = require('./tag').Tags,
User = require('./user').User,
ghostBookshelf = require('./base'),
xmlrpc = require('../xmlrpc'),
@ -129,7 +126,7 @@ Post = ghostBookshelf.Model.extend({
return when.all(tagOps);
}
return Tags.forge().query('whereIn', 'name', _.pluck(self.myTags, 'name')).fetch(options).then(function (existingTags) {
return ghostBookshelf.collection('Tags').forge().query('whereIn', 'name', _.pluck(self.myTags, 'name')).fetch(options).then(function (existingTags) {
var doNotExist = [],
createAndAttachOperation;
@ -143,7 +140,7 @@ Post = ghostBookshelf.Model.extend({
// Create tags that don't exist and attach to post
_.each(doNotExist, function (tag) {
createAndAttachOperation = Tag.add({name: tag.name}, options).then(function (createdTag) {
createAndAttachOperation = ghostBookshelf.model('Tag').add({name: tag.name}, options).then(function (createdTag) {
createdTag = createdTag.toJSON();
// _.omit(options, 'query') is a fix for using bookshelf 0.6.8
// (https://github.com/tgriesser/bookshelf/issues/294)
@ -281,8 +278,8 @@ Post = ghostBookshelf.Model.extend({
options = options || {};
var postCollection = Posts.forge(),
tagInstance = options.tag !== undefined ? Tag.forge({slug: options.tag}) : false,
authorInstance = options.author !== undefined ? User.forge({slug: options.author}) : false;
tagInstance = options.tag !== undefined ? ghostBookshelf.model('Tag').forge({slug: options.tag}) : false,
authorInstance = options.author !== undefined ? ghostBookshelf.model('User').forge({slug: options.author}) : false;
if (options.limit) {
options.limit = parseInt(options.limit, 10) || 15;

View File

@ -8,7 +8,6 @@ var _ = require('lodash'),
crypto = require('crypto'),
validator = require('validator'),
validation = require('../data/validation'),
Role = require('./role').Role,
tokenSecurity = {},
activeStates = ['active', 'warn-1', 'warn-2', 'warn-3', 'warn-4', 'locked'],
@ -175,7 +174,7 @@ User = ghostBookshelf.Model.extend({
options = options || {};
var userCollection = Users.forge(),
roleInstance = options.role !== undefined ? Role.forge({name: options.role}) : false;
roleInstance = options.role !== undefined ? ghostBookshelf.model('Role').forge({name: options.role}) : false;
if (options.limit && options.limit !== 'all') {
options.limit = parseInt(options.limit, 10) || 15;
@ -405,7 +404,7 @@ User = ghostBookshelf.Model.extend({
if (roles.models[0].id === roleId) {
return;
}
return Role.findOne({id: roleId});
return ghostBookshelf.model('Role').findOne({id: roleId});
}).then(function (roleToAssign) {
if (roleToAssign && roleToAssign.get('name') === 'Owner') {
return when.reject(
@ -442,7 +441,7 @@ User = ghostBookshelf.Model.extend({
options = this.filterOptions(options, 'add');
options.withRelated = _.union([ 'roles' ], options.include);
return Role.findOne({name: 'Author'}, _.pick(options, 'transacting')).then(function (authorRole) {
return ghostBookshelf.model('Role').findOne({name: 'Author'}, _.pick(options, 'transacting')).then(function (authorRole) {
// Get the role we're going to assign to this user, or the author role if there isn't one
roles = data.roles || [authorRole.get('id')];
@ -800,9 +799,9 @@ User = ghostBookshelf.Model.extend({
assignUser;
// Get admin role
return Role.findOne({name: 'Administrator'}).then(function (result) {
return ghostBookshelf.model('Role').findOne({name: 'Administrator'}).then(function (result) {
adminRole = result;
return Role.findOne({name: 'Owner'});
return ghostBookshelf.model('Role').findOne({name: 'Owner'});
}).then(function (result) {
ownerRole = result;
return User.findOne({id: options.context.user});

View File

@ -1,8 +1,9 @@
var _ = require('lodash'),
config = require('./config'),
errors = require('./errors'),
http = require('http'),
xml = require('xml'),
api = require('./api'),
config = require('./config'),
errors = require('./errors'),
pingList;
// ToDo: Make this configurable
@ -29,7 +30,7 @@ function ping(post) {
}
// Need to require here because of circular dependency
return config.urlForPost(require('./api').settings, post, true).then(function (url) {
return config.urlForPost(api.settings, post, true).then(function (url) {
// Build XML object.
pingXML = xml({

View File

@ -4,9 +4,9 @@ var testUtils = require('../../utils'),
should = require('should'),
// Stuff we are testing
dbAPI = require('../../../server/api/db'),
TagModel = require('../../../server/models').Tag,
PostModel = require('../../../server/models').Post;
dbAPI = require('../../../server/api/db'),
ModelTag = require('../../../server/models/tag'),
ModelPost = require('../../../server/models/post');
@ -24,12 +24,12 @@ describe('DB API', function () {
result.db.should.be.instanceof(Array);
result.db.should.be.empty;
}).then(function () {
return TagModel.findAll(testUtils.context.owner).then(function (results) {
return ModelTag.Tag.findAll(testUtils.context.owner).then(function (results) {
should.exist(results);
results.length.should.equal(0);
});
}).then(function () {
return PostModel.findAll(testUtils.context.owner).then(function (results) {
return ModelPost.Post.findAll(testUtils.context.owner).then(function (results) {
should.exist(results);
results.length.should.equal(0);
done();
@ -43,12 +43,12 @@ describe('DB API', function () {
result.db.should.be.instanceof(Array);
result.db.should.be.empty;
}).then(function () {
return TagModel.findAll(testUtils.context.admin).then(function (results) {
return ModelTag.Tag.findAll(testUtils.context.admin).then(function (results) {
should.exist(results);
results.length.should.equal(0);
});
}).then(function () {
return PostModel.findAll(testUtils.context.admin).then(function (results) {
return ModelPost.Post.findAll(testUtils.context.admin).then(function (results) {
should.exist(results);
results.length.should.equal(0);
done();

View File

@ -7,7 +7,7 @@ var testUtils = require('../../utils'),
_ = require('lodash'),
// Stuff we are testing
UserModel = require('../../../server/models').User,
ModelUser = require('../../../server/models'),
UserAPI = require('../../../server/api/users'),
mail = require('../../../server/api/mail'),
@ -26,7 +26,7 @@ describe('Users API', function () {
it('dateTime fields are returned as Date objects', function (done) {
var userData = testUtils.DataGenerator.forModel.users[0];
UserModel.check({ email: userData.email, password: userData.password }).then(function (user) {
ModelUser.User.check({ email: userData.email, password: userData.password }).then(function (user) {
return UserAPI.read({ id: user.id });
}).then(function (response) {
response.users[0].created_at.should.be.an.instanceof(Date);
@ -339,7 +339,7 @@ describe('Users API', function () {
beforeEach(function () {
newUser = _.clone(testUtils.DataGenerator.forKnex.createUser(testUtils.DataGenerator.Content.users[4]));
sandbox.stub(UserModel, 'gravatarLookup', function (userData) {
sandbox.stub(ModelUser.User, 'gravatarLookup', function (userData) {
return when.resolve(userData);
});

View File

@ -4,7 +4,7 @@ var testUtils = require('../../utils'),
should = require('should'),
// Stuff we are testing
AppFieldsModel = require('../../../server/models').AppField,
AppFieldsModel = require('../../../server/models/appField').AppField,
context = testUtils.context.admin;
describe('App Fields Model', function () {
@ -13,7 +13,9 @@ describe('App Fields Model', function () {
afterEach(testUtils.teardown);
beforeEach(testUtils.setup('app_field'));
should.exist(AppFieldsModel);
before(function () {
should.exist(AppFieldsModel);
});
it('can findAll', function (done) {
AppFieldsModel.findAll().then(function (results) {

View File

@ -4,7 +4,7 @@ var testUtils = require('../../utils'),
should = require('should'),
// Stuff we are testing
AppSettingModel = require('../../../server/models').AppSetting,
AppSettingModel = require('../../../server/models/appSetting').AppSetting,
context = testUtils.context.admin;
describe('App Setting Model', function () {
@ -13,7 +13,9 @@ describe('App Setting Model', function () {
afterEach(testUtils.teardown);
beforeEach(testUtils.setup('app_setting'));
should.exist(AppSettingModel);
before(function () {
should.exist(AppSettingModel);
});
it('can findAll', function (done) {
AppSettingModel.findAll().then(function (results) {

View File

@ -6,7 +6,7 @@ var testUtils = require('../../utils'),
_ = require('lodash'),
// Stuff we are testing
AppModel = require('../../../server/models').App,
AppModel = require('../../../server/models/app').App,
context = testUtils.context.admin;
describe('App Model', function () {
@ -15,7 +15,9 @@ describe('App Model', function () {
afterEach(testUtils.teardown);
beforeEach(testUtils.setup('app'));
should.exist(AppModel);
before(function () {
should.exist(AppModel);
});
it('can findAll', function (done) {
AppModel.findAll().then(function (results) {

View File

@ -4,7 +4,7 @@ var testUtils = require('../../utils'),
should = require('should'),
// Stuff we are testing
PermissionModel = require('../../../server/models').Permission,
PermissionModel = require('../../../server/models/permission').Permission,
context = testUtils.context.admin;
describe('Permission Model', function () {
@ -13,7 +13,9 @@ describe('Permission Model', function () {
afterEach(testUtils.teardown);
beforeEach(testUtils.setup('permission'));
should.exist(PermissionModel);
before(function () {
should.exist(PermissionModel);
});
it('can findAll', function (done) {
PermissionModel.findAll().then(function (foundPermissions) {

View File

@ -6,7 +6,7 @@ var testUtils = require('../../utils'),
_ = require('lodash'),
// Stuff we are testing
PostModel = require('../../../server/models').Post,
PostModel = require('../../../server/models/post').Post,
DataGenerator = testUtils.DataGenerator,
context = testUtils.context.owner;
@ -19,7 +19,9 @@ describe('Post Model', function () {
afterEach(testUtils.teardown);
beforeEach(testUtils.setup('owner', 'posts', 'apps'));
should.exist(PostModel);
before(function () {
should.exist(PostModel);
});
function extractFirstPost(posts) {
return _.filter(posts, { id: 1 })[0];
@ -507,7 +509,9 @@ describe('Post Model', function () {
afterEach(testUtils.teardown);
beforeEach(testUtils.setup('posts:mu'));
should.exist(PostModel);
before(function () {
should.exist(PostModel);
});
it('can destroy multiple posts by author', function (done) {

View File

@ -4,7 +4,7 @@ var testUtils = require('../../utils'),
should = require('should'),
// Stuff we are testing
RoleModel = require('../../../server/models').Role,
RoleModel = require('../../../server/models/role').Role,
context = testUtils.context.admin;
describe('Role Model', function () {
@ -14,7 +14,9 @@ describe('Role Model', function () {
beforeEach(testUtils.setup('role'));
should.exist(RoleModel);
before(function () {
should.exist(RoleModel);
});
it('can findAll', function (done) {
RoleModel.findAll().then(function (foundRoles) {

View File

@ -4,7 +4,7 @@ var testUtils = require('../../utils'),
should = require('should'),
// Stuff we are testing
SettingsModel = require('../../../server/models').Settings,
SettingsModel = require('../../../server/models/settings').Settings,
config = require('../../../server/config'),
context = testUtils.context.admin;
@ -14,7 +14,9 @@ describe('Settings Model', function () {
afterEach(testUtils.teardown);
beforeEach(testUtils.setup('settings'));
should.exist(SettingsModel);
before(function () {
should.exist(SettingsModel);
});
describe('API', function () {

View File

@ -6,10 +6,11 @@ var testUtils = require('../../utils'),
_ = require('lodash'),
// Stuff we are testing
Models = require('../../../server/models'),
TagModel = Models.Tag,
PostModel = Models.Post,
context = testUtils.context.admin;
ModelsTag = require('../../../server/models/tag'),
ModelsPost = require('../../../server/models/post'),
context = testUtils.context.admin,
TagModel,
PostModel;
describe('Tag Model', function () {
// Keep the DB clean
@ -17,7 +18,13 @@ describe('Tag Model', function () {
afterEach(testUtils.teardown);
beforeEach(testUtils.setup());
should.exist(TagModel);
before(function () {
TagModel = ModelsTag.Tag;
PostModel = ModelsPost.Post;
should.exist(TagModel);
should.exist(PostModel);
});
it('uses Date objects for dateTime fields', function (done) {
TagModel.add(testUtils.DataGenerator.forModel.tags[0], context).then(function (tag) {

View File

@ -8,8 +8,8 @@ var testUtils = require('../../utils'),
_ = require('lodash'),
// Stuff we are testing
UserModel = require('../../../server/models').User,
RoleModel = require('../../../server/models').Role,
UserModel = require('../../../server/models/user').User,
RoleModel = require('../../../server/models/role').Role,
context = testUtils.context.admin,
sandbox = sinon.sandbox.create();
@ -22,7 +22,9 @@ describe('User Model', function run() {
sandbox.restore();
});
should.exist(UserModel);
before(function () {
should.exist(UserModel);
});
describe('Registration', function runRegistration() {
beforeEach(testUtils.setup('roles'));

View File

@ -7,17 +7,20 @@ var testUtils = require('../utils'),
_ = require('lodash'),
// Stuff we are testing
ModelPermission = require('../../server/models').Permission,
ModelPermissions = require('../../server/models').Permissions,
permissions = require('../../server/permissions'),
effectivePerms = require('../../server/permissions/effective'),
context = testUtils.context.owner,
Models = require('../../server/models'),
permissions = require('../../server/permissions'),
effectivePerms = require('../../server/permissions/effective'),
context = testUtils.context.owner,
sandbox = sinon.sandbox.create();
sandbox = sinon.sandbox.create();
// TODO move to integrations or stub
describe('Permissions', function () {
before(function (done) {
Models.init().then(done).catch(done);
});
afterEach(function () {
sandbox.restore();
});
@ -27,8 +30,8 @@ describe('Permissions', function () {
return testUtils.DataGenerator.forKnex.createPermission(testPerm);
});
sandbox.stub(ModelPermission, 'findAll', function () {
return when(ModelPermissions.forge(permissions));
sandbox.stub(Models.Permission, 'findAll', function () {
return when(Models.Permissions.forge(permissions));
});
});

View File

@ -5,7 +5,7 @@ var when = require('when'),
fs = require('fs-extra'),
path = require('path'),
migration = require('../../server/data/migration/'),
settings = require('../../server/models').Settings,
Models = require('../../server/models'),
SettingsAPI = require('../../server/api/settings'),
permissions = require('../../server/permissions'),
permsFixtures = require('../../server/data/fixtures/permissions/permissions.json'),
@ -357,7 +357,7 @@ toDoList = {
'posts:mu': function insertMultiAuthorPosts() { return fixtures.insertMultiAuthorPosts(); },
'apps': function insertApps() { return fixtures.insertApps(); },
'settings': function populateSettings() {
return settings.populateDefaults().then(function () { return SettingsAPI.updateSettingsCache(); });
return Models.Settings.populateDefaults().then(function () { return SettingsAPI.updateSettingsCache(); });
},
'users:roles': function createUsersWithRoles() { return fixtures.createUsersWithRoles(); },
'users': function createExtraUsers() { return fixtures.createExtraUsers(); },
@ -434,7 +434,9 @@ setup = function setup() {
args = arguments;
return function (done) {
return initFixtures.apply(self, args).then(function () {
return Models.init().then(function () {
return initFixtures.apply(self, args);
}).then(function () {
done();
}).catch(done);
};