This aims to speed up both the ghost application and tests by

migration from usage of config() to just an object of config.

no relevant issue

- Change 'loadConfig' task to 'ensureConfig' to more accurately reflect
what it is actually doing.  Its sole purpose is to make sure a `config.js`
 file exists, and as such the name now reflects that purpose.

- Update config/index.js to export the ghostConfig object directly
so that it can be accessed from other modules

- Update all references of config(). to config.
This was a blind global find all and replace, treat it as such.

- Fixes to tests to support new config access method

- Allow each test to still work when invoked invidually
This commit is contained in:
Harry Wolff 2014-07-17 10:33:21 -04:00
parent a620e9017e
commit be37070fb6
43 changed files with 307 additions and 314 deletions

View File

@ -11,7 +11,6 @@ var path = require('path'),
_ = require('lodash'), _ = require('lodash'),
buildDirectory = path.resolve(process.cwd(), '.build'), buildDirectory = path.resolve(process.cwd(), '.build'),
distDirectory = path.resolve(process.cwd(), '.dist'), distDirectory = path.resolve(process.cwd(), '.dist'),
bootstrap = require('./core/bootstrap'),
// ## Build File Patterns // ## Build File Patterns
// A list of files and patterns to include when creating a release zip. // A list of files and patterns to include when creating a release zip.
@ -160,7 +159,9 @@ var path = require('path'),
// #### All Unit tests // #### All Unit tests
unit: { unit: {
src: ['core/test/unit/**/*_spec.js'] src: [
'core/test/unit/**/*_spec.js'
]
}, },
// ##### Groups of unit tests // ##### Groups of unit tests
@ -208,7 +209,9 @@ var path = require('path'),
// #### All Route tests // #### All Route tests
routes: { routes: {
src: ['core/test/functional/routes/**/*_test.js'] src: [
'core/test/functional/routes/**/*_test.js'
]
} }
}, },
@ -537,12 +540,13 @@ var path = require('path'),
cfg.express.test.options.node_env = process.env.NODE_ENV; cfg.express.test.options.node_env = process.env.NODE_ENV;
}); });
// #### Load Config *(Utility Task)* // #### Ensure Config *(Utility Task)*
// Make sure that we have a `config.js` file when running tests // Make sure that we have a `config.js` file when running tests
// Ghost requires a `config.js` file to specify the database settings etc. Ghost comes with an example file: // Ghost requires a `config.js` file to specify the database settings etc. Ghost comes with an example file:
// `config.example.js` which is copied and renamed to `config.js` by the bootstrap process // `config.example.js` which is copied and renamed to `config.js` by the bootstrap process
grunt.registerTask('loadConfig', function () { grunt.registerTask('ensureConfig', function () {
var done = this.async(); var bootstrap = require('./core/bootstrap'),
done = this.async();
bootstrap().then(function () { bootstrap().then(function () {
done(); done();
}).catch(function (err) { }).catch(function (err) {
@ -607,7 +611,7 @@ var path = require('path'),
// Unit tests do **not** touch the database. // Unit tests do **not** touch the database.
// A coverage report can be generated for these tests using the `grunt test-coverage` task. // A coverage report can be generated for these tests using the `grunt test-coverage` task.
grunt.registerTask('test-unit', 'Run unit tests (mocha)', grunt.registerTask('test-unit', 'Run unit tests (mocha)',
['clean:test', 'setTestEnv', 'loadConfig', 'mochacli:unit']); ['clean:test', 'setTestEnv', 'ensureConfig', 'mochacli:unit']);
// ### Integration tests *(sub task)* // ### Integration tests *(sub task)*
// `grunt test-integration` will run just the integration tests // `grunt test-integration` will run just the integration tests
@ -636,7 +640,7 @@ var path = require('path'),
// //
// A coverage report can be generated for these tests using the `grunt test-coverage` task. // A coverage report can be generated for these tests using the `grunt test-coverage` task.
grunt.registerTask('test-integration', 'Run integration tests (mocha + db access)', grunt.registerTask('test-integration', 'Run integration tests (mocha + db access)',
['clean:test', 'setTestEnv', 'loadConfig', 'mochacli:integration']); ['clean:test', 'setTestEnv', 'ensureConfig', 'mochacli:integration']);
// ### Route tests *(sub task)* // ### Route tests *(sub task)*
// `grunt test-routes` will run just the route tests // `grunt test-routes` will run just the route tests
@ -657,7 +661,7 @@ var path = require('path'),
// are working as expected, including checking the headers and status codes received. It is very easy and // are working as expected, including checking the headers and status codes received. It is very easy and
// quick to test many permutations of routes / urls in the system. // quick to test many permutations of routes / urls in the system.
grunt.registerTask('test-routes', 'Run functional route tests (mocha)', grunt.registerTask('test-routes', 'Run functional route tests (mocha)',
['clean:test', 'setTestEnv', 'loadConfig', 'mochacli:routes']); ['clean:test', 'setTestEnv', 'ensureConfig', 'mochacli:routes']);
// ### Functional tests for the setup process // ### Functional tests for the setup process
// `grunt test-functional-setup will run just the functional tests for the setup page. // `grunt test-functional-setup will run just the functional tests for the setup page.
@ -665,7 +669,7 @@ var path = require('path'),
// Setup only works with a brand new database, so it needs to run isolated from the rest of // Setup only works with a brand new database, so it needs to run isolated from the rest of
// the functional tests. // the functional tests.
grunt.registerTask('test-functional-setup', 'Run functional tests for setup', grunt.registerTask('test-functional-setup', 'Run functional tests for setup',
['clean:test', 'setTestEnv', 'loadConfig', 'cleanDatabase', 'express:test', ['clean:test', 'setTestEnv', 'ensureConfig', 'cleanDatabase', 'express:test',
'spawnCasperJS:setup', 'express:test:stop'] 'spawnCasperJS:setup', 'express:test:stop']
); );
@ -688,7 +692,7 @@ var path = require('path'),
// The purpose of the functional tests is to ensure that Ghost is working as is expected from a user perspective // The purpose of the functional tests is to ensure that Ghost is working as is expected from a user perspective
// including buttons and other important interactions in the admin UI. // including buttons and other important interactions in the admin UI.
grunt.registerTask('test-functional', 'Run functional interface tests (CasperJS)', grunt.registerTask('test-functional', 'Run functional interface tests (CasperJS)',
['clean:test', 'setTestEnv', 'loadConfig', 'cleanDatabase', 'express:test', 'spawnCasperJS', 'express:test:stop', ['clean:test', 'setTestEnv', 'ensureConfig', 'cleanDatabase', 'express:test', 'spawnCasperJS', 'express:test:stop',
'test-functional-setup'] 'test-functional-setup']
); );
@ -702,7 +706,7 @@ var path = require('path'),
// //
// Key areas for coverage are: helpers and theme elements, apps / GDK, the api and model layers. // Key areas for coverage are: helpers and theme elements, apps / GDK, the api and model layers.
grunt.registerTask('test-coverage', 'Generate unit and integration (mocha) tests coverage report', grunt.registerTask('test-coverage', 'Generate unit and integration (mocha) tests coverage report',
['clean:test', 'setTestEnv', 'loadConfig', 'shell:coverage']); ['clean:test', 'setTestEnv', 'ensureConfig', 'shell:coverage']);
// ## Building assets // ## Building assets

6
core/bootstrap.js vendored
View File

@ -11,8 +11,8 @@ var fs = require('fs'),
errors = require('./server/errors'), errors = require('./server/errors'),
config = require('./server/config'), config = require('./server/config'),
appRoot = config().paths.appRoot, appRoot = config.paths.appRoot,
configExample = config().paths.configExample, configExample = config.paths.configExample,
configFile; configFile;
function readConfigFile(envVal) { function readConfigFile(envVal) {
@ -122,7 +122,7 @@ function loadConfig(configFilePath) {
// Allow config file path to be taken from, in order of importance: // Allow config file path to be taken from, in order of importance:
// environment process, passed in value, default location // environment process, passed in value, default location
configFile = process.env.GHOST_CONFIG || configFilePath || config().paths.config; configFile = process.env.GHOST_CONFIG || configFilePath || config.paths.config;
/* Check for config file and copy from config.example.js /* Check for config file and copy from config.example.js
if one doesn't exist. After that, start the server. */ if one doesn't exist. After that, start the server. */

View File

@ -45,7 +45,7 @@ authentication = {
var dbHash = response.settings[0].value; var dbHash = response.settings[0].value;
return dataProvider.User.generateResetToken(email, expires, dbHash); return dataProvider.User.generateResetToken(email, expires, dbHash);
}).then(function (resetToken) { }).then(function (resetToken) {
var baseUrl = config().forceAdminSSL ? (config().urlSSL || config().url) : config().url, var baseUrl = config.forceAdminSSL ? (config.urlSSL || config.url) : config.url,
siteLink = '<a href="' + baseUrl + '">' + baseUrl + '</a>', siteLink = '<a href="' + baseUrl + '">' + baseUrl + '</a>',
resetUrl = baseUrl.replace(/\/$/, '') + '/ghost/reset/' + resetToken + '/', resetUrl = baseUrl.replace(/\/$/, '') + '/ghost/reset/' + resetToken + '/',
resetLink = '<a href="' + resetUrl + '">' + resetUrl + '</a>', resetLink = '<a href="' + resetUrl + '">' + resetUrl + '</a>',
@ -216,7 +216,7 @@ authentication = {
to: setupUser.email, to: setupUser.email,
subject: 'Your New Ghost Blog', subject: 'Your New Ghost Blog',
html: '<p><strong>Hello!</strong></p>' + html: '<p><strong>Hello!</strong></p>' +
'<p>Good news! You\'ve successfully created a brand new Ghost blog over on ' + config().url + '</p>' + '<p>Good news! You\'ve successfully created a brand new Ghost blog over on ' + config.url + '</p>' +
'<p>You can log in to your admin account with the following details:</p>' + '<p>You can log in to your admin account with the following details:</p>' +
'<p> Email Address: ' + setupUser.email + '<br>' + '<p> Email Address: ' + setupUser.email + '<br>' +
'Password: The password you chose when you signed up</p>' + 'Password: The password you chose when you signed up</p>' +

View File

@ -56,7 +56,7 @@ mail = {
sendTest: function (object, options) { sendTest: function (object, options) {
var html = '<p><strong>Hello there!</strong></p>' + var html = '<p><strong>Hello there!</strong></p>' +
'<p>Excellent!' + '<p>Excellent!' +
' You\'ve successfully setup your email config for your Ghost blog over on ' + config().url + '</p>' + ' You\'ve successfully setup your email config for your Ghost blog over on ' + config.url + '</p>' +
'<p>If you hadn\'t, you wouldn\'t be reading this email, but you are, so it looks like all is well :)</p>' + '<p>If you hadn\'t, you wouldn\'t be reading this email, but you are, so it looks like all is well :)</p>' +
'<p>xoxo</p>' + '<p>xoxo</p>' +
'<p>Team Ghost<br>' + '<p>Team Ghost<br>' +

View File

@ -135,11 +135,11 @@ readSettingsResult = function (settingsModels) {
return memo; return memo;
}, {}), }, {}),
themes = config().paths.availableThemes, themes = config.paths.availableThemes,
apps = config().paths.availableApps, apps = config.paths.availableApps,
res; res;
if (settings.activeTheme) { if (settings.activeTheme && themes) {
res = filterPaths(themes, settings.activeTheme.value); res = filterPaths(themes, settings.activeTheme.value);
settings.availableThemes = { settings.availableThemes = {
@ -149,7 +149,7 @@ readSettingsResult = function (settingsModels) {
}; };
} }
if (settings.activeApps) { if (settings.activeApps && apps) {
res = filterPaths(apps, JSON.parse(settings.activeApps.value)); res = filterPaths(apps, JSON.parse(settings.activeApps.value));
settings.availableApps = { settings.availableApps = {
@ -201,7 +201,7 @@ populateDefaultSetting = function (key) {
// Add to the settings cache // Add to the settings cache
return updateSettingsCache(readResult).then(function () { return updateSettingsCache(readResult).then(function () {
// Update theme with the new settings // Update theme with the new settings
return config.theme.update(settings, config().url); return config.theme.update(settings, config.url);
}).then(function () { }).then(function () {
// Get the result from the cache with permission checks // Get the result from the cache with permission checks
return defaultSetting; return defaultSetting;
@ -379,7 +379,7 @@ settings = {
var readResult = readSettingsResult(result); var readResult = readSettingsResult(result);
return updateSettingsCache(readResult).then(function () { return updateSettingsCache(readResult).then(function () {
return config.theme.update(settings, config().url); return config.theme.update(settings, config.url);
}).then(function () { }).then(function () {
return settingsResult(readResult, type); return settingsResult(readResult, type);
}); });

View File

@ -27,7 +27,7 @@ themes = {
return canThis(options.context).browse.theme().then(function () { return canThis(options.context).browse.theme().then(function () {
return when.all([ return when.all([
settings.read({key: 'activeTheme', context: {internal: true}}), settings.read({key: 'activeTheme', context: {internal: true}}),
config().paths.availableThemes config.paths.availableThemes
]).then(function (result) { ]).then(function (result) {
var activeTheme = result[0].settings[0].value, var activeTheme = result[0].settings[0].value,
availableThemes = result[1], availableThemes = result[1],

View File

@ -176,7 +176,7 @@ users = {
dbHash = response.settings[0].value; dbHash = response.settings[0].value;
return dataProvider.User.generateResetToken(user.email, expires, dbHash); return dataProvider.User.generateResetToken(user.email, expires, dbHash);
}).then(function (resetToken) { }).then(function (resetToken) {
var baseUrl = config().forceAdminSSL ? (config().urlSSL || config().url) : config().url, var baseUrl = config.forceAdminSSL ? (config.urlSSL || config.url) : config.url,
siteLink = '<a href="' + baseUrl + '">' + baseUrl + '</a>', siteLink = '<a href="' + baseUrl + '">' + baseUrl + '</a>',
resetUrl = baseUrl.replace(/\/$/, '') + '/ghost/signup/' + resetToken + '/', resetUrl = baseUrl.replace(/\/$/, '') + '/ghost/signup/' + resetToken + '/',
resetLink = '<a href="' + resetUrl + '">' + resetUrl + '</a>', resetLink = '<a href="' + resetUrl + '">' + resetUrl + '</a>',

View File

@ -11,7 +11,7 @@ var path = require('path'),
// Get the full path to an app by name // Get the full path to an app by name
function getAppAbsolutePath(name) { function getAppAbsolutePath(name) {
return path.join(config().paths.appPath, name); return path.join(config.paths.appPath, name);
} }
// Get a relative path to the given apps root, defaults // Get a relative path to the given apps root, defaults

View File

@ -14,6 +14,8 @@ var path = require('path'),
ghostConfig = {}, ghostConfig = {},
appRoot = path.resolve(__dirname, '../../../'), appRoot = path.resolve(__dirname, '../../../'),
corePath = path.resolve(appRoot, 'core/'), corePath = path.resolve(appRoot, 'core/'),
testingEnvs = ['testing', 'testing-mysql', 'testing-pg'],
defaultConfig = {},
knexInstance; knexInstance;
// Are we using sockets? Custom socket or the default? // Are we using sockets? Custom socket or the default?
@ -25,7 +27,7 @@ function getSocket() {
} }
function updateConfig(config) { function updateConfig(config) {
var localPath, var localPath = '',
contentPath, contentPath,
subdir; subdir;
@ -53,7 +55,7 @@ function updateConfig(config) {
// Otherwise default to default content path location // Otherwise default to default content path location
contentPath = ghostConfig.paths.contentPath || path.resolve(appRoot, 'content'); contentPath = ghostConfig.paths.contentPath || path.resolve(appRoot, 'content');
if (!knexInstance && ghostConfig.database) { if (!knexInstance && ghostConfig.database && ghostConfig.database.client) {
knexInstance = knex(ghostConfig.database); knexInstance = knex(ghostConfig.database);
} }
@ -80,8 +82,8 @@ function updateConfig(config) {
'lang': path.join(corePath, '/shared/lang/'), 'lang': path.join(corePath, '/shared/lang/'),
'debugPath': subdir + '/ghost/debug/', 'debugPath': subdir + '/ghost/debug/',
'availableThemes': ghostConfig.paths.availableThemes || [], 'availableThemes': ghostConfig.paths.availableThemes || {},
'availableApps': ghostConfig.paths.availableApps || [], 'availableApps': ghostConfig.paths.availableApps || {},
'builtScriptPath': path.join(corePath, 'built/scripts/') 'builtScriptPath': path.join(corePath, 'built/scripts/')
} }
}); });
@ -90,8 +92,6 @@ function updateConfig(config) {
// configUrl object to maintain // configUrl object to maintain
// clean depedency tree // clean depedency tree
configUrl.setConfig(ghostConfig); configUrl.setConfig(ghostConfig);
return ghostConfig;
} }
function initConfig(rawConfig) { function initConfig(rawConfig) {
@ -99,7 +99,7 @@ function initConfig(rawConfig) {
// object so we can later refer to it. // object so we can later refer to it.
// Note: this is not the entirety of config.js, // Note: this is not the entirety of config.js,
// just the object appropriate for this NODE_ENV // just the object appropriate for this NODE_ENV
ghostConfig = updateConfig(rawConfig); updateConfig(rawConfig);
return when.all([requireTree(ghostConfig.paths.themePath), requireTree(ghostConfig.paths.appPath)]).then(function (paths) { return when.all([requireTree(ghostConfig.paths.themePath), requireTree(ghostConfig.paths.appPath)]).then(function (paths) {
ghostConfig.paths.availableThemes = paths[0]; ghostConfig.paths.availableThemes = paths[0];
@ -108,23 +108,14 @@ function initConfig(rawConfig) {
}); });
} }
// Returns NODE_ENV config object if (testingEnvs.indexOf(process.env.NODE_ENV) > -1) {
function config() { defaultConfig = require('../../../config.example')[process.env.NODE_ENV];
// @TODO: get rid of require statement.
// This is currently needed for tests to load config file
// successfully. While running application we should never
// have to directly delegate to the config.js file.
if (_.isEmpty(ghostConfig)) {
try {
ghostConfig = require(path.resolve(__dirname, '../../../', 'config.js'))[process.env.NODE_ENV] || {};
} catch (ignore) {/*jslint strict: true */}
ghostConfig = updateConfig(ghostConfig);
}
return ghostConfig;
} }
module.exports = config; // Init config
updateConfig(defaultConfig);
module.exports = ghostConfig;
module.exports.init = initConfig; module.exports.init = initConfig;
module.exports.theme = theme; module.exports.theme = theme;
module.exports.getSocket = getSocket; module.exports.getSocket = getSocket;

View File

@ -91,14 +91,14 @@ frontendControllers = {
// No negative pages, or page 1 // No negative pages, or page 1
if (isNaN(pageParam) || pageParam < 1 || (pageParam === 1 && req.route.path === '/page/:page/')) { if (isNaN(pageParam) || pageParam < 1 || (pageParam === 1 && req.route.path === '/page/:page/')) {
return res.redirect(config().paths.subdir + '/'); return res.redirect(config.paths.subdir + '/');
} }
return getPostPage(options).then(function (page) { return getPostPage(options).then(function (page) {
// If page is greater than number of pages we have, redirect to last page // If page is greater than number of pages we have, redirect to last page
if (pageParam > page.meta.pagination.pages) { if (pageParam > page.meta.pagination.pages) {
return res.redirect(page.meta.pagination.pages === 1 ? config().paths.subdir + '/' : (config().paths.subdir + '/page/' + page.meta.pagination.pages + '/')); return res.redirect(page.meta.pagination.pages === 1 ? config.paths.subdir + '/' : (config.paths.subdir + '/page/' + page.meta.pagination.pages + '/'));
} }
setReqCtx(req, page.posts); setReqCtx(req, page.posts);
@ -119,7 +119,7 @@ frontendControllers = {
// Get url for tag page // Get url for tag page
function tagUrl(tag, page) { function tagUrl(tag, page) {
var url = config().paths.subdir + '/tag/' + tag + '/'; var url = config.paths.subdir + '/tag/' + tag + '/';
if (page && page > 1) { if (page && page > 1) {
url += 'page/' + page + '/'; url += 'page/' + page + '/';
@ -148,7 +148,7 @@ frontendControllers = {
filters.doFilter('prePostsRender', page.posts).then(function (posts) { filters.doFilter('prePostsRender', page.posts).then(function (posts) {
api.settings.read({key: 'activeTheme', context: {internal: true}}).then(function (response) { api.settings.read({key: 'activeTheme', context: {internal: true}}).then(function (response) {
var activeTheme = response.settings[0], var activeTheme = response.settings[0],
paths = config().paths.availableThemes[activeTheme.value], paths = config.paths.availableThemes[activeTheme.value],
view = paths.hasOwnProperty('tag.hbs') ? 'tag' : 'index', view = paths.hasOwnProperty('tag.hbs') ? 'tag' : 'index',
// Format data for template // Format data for template
@ -277,7 +277,7 @@ frontendControllers = {
function render() { function render() {
// If we're ready to render the page but the last param is 'edit' then we'll send you to the edit page. // If we're ready to render the page but the last param is 'edit' then we'll send you to the edit page.
if (params.edit === 'edit') { if (params.edit === 'edit') {
return res.redirect(config().paths.subdir + '/ghost/editor/' + post.id + '/'); return res.redirect(config.paths.subdir + '/ghost/editor/' + post.id + '/');
} else if (params.edit !== undefined) { } else if (params.edit !== undefined) {
// reject with type: 'NotFound' // reject with type: 'NotFound'
return when.reject(new errors.NotFoundError()); return when.reject(new errors.NotFoundError());
@ -288,7 +288,7 @@ frontendControllers = {
filters.doFilter('prePostsRender', post).then(function (post) { filters.doFilter('prePostsRender', post).then(function (post) {
api.settings.read({key: 'activeTheme', context: {internal: true}}).then(function (response) { api.settings.read({key: 'activeTheme', context: {internal: true}}).then(function (response) {
var activeTheme = response.settings[0], var activeTheme = response.settings[0],
paths = config().paths.availableThemes[activeTheme.value], paths = config.paths.availableThemes[activeTheme.value],
view = template.getThemeViewForPost(paths, post); view = template.getThemeViewForPost(paths, post);
res.render(view, {post: post}); res.render(view, {post: post});
@ -358,9 +358,9 @@ frontendControllers = {
if (isNaN(pageParam) || pageParam < 1 || if (isNaN(pageParam) || pageParam < 1 ||
(pageParam === 1 && (req.route.path === '/rss/:page/' || req.route.path === '/tag/:slug/rss/:page/'))) { (pageParam === 1 && (req.route.path === '/rss/:page/' || req.route.path === '/tag/:slug/rss/:page/'))) {
if (tagParam !== undefined) { if (tagParam !== undefined) {
return res.redirect(config().paths.subdir + '/tag/' + tagParam + '/rss/'); return res.redirect(config.paths.subdir + '/tag/' + tagParam + '/rss/');
} else { } else {
return res.redirect(config().paths.subdir + '/rss/'); return res.redirect(config.paths.subdir + '/rss/');
} }
} }
@ -410,9 +410,9 @@ frontendControllers = {
// If page is greater than number of pages we have, redirect to last page // If page is greater than number of pages we have, redirect to last page
if (pageParam > maxPage) { if (pageParam > maxPage) {
if (tagParam) { if (tagParam) {
return res.redirect(config().paths.subdir + '/tag/' + tagParam + '/rss/' + maxPage + '/'); return res.redirect(config.paths.subdir + '/tag/' + tagParam + '/rss/' + maxPage + '/');
} else { } else {
return res.redirect(config().paths.subdir + '/rss/' + maxPage + '/'); return res.redirect(config.paths.subdir + '/rss/' + maxPage + '/');
} }
} }

View File

@ -33,7 +33,7 @@ exporter = function () {
tables = results[1], tables = results[1],
selectOps = _.map(tables, function (name) { selectOps = _.map(tables, function (name) {
if (excludedTables.indexOf(name) < 0) { if (excludedTables.indexOf(name) < 0) {
return config().database.knex(name).select(); return config.database.knex(name).select();
} }
}); });

View File

@ -6,7 +6,7 @@ var _ = require('lodash'),
errors = require('../../errors'), errors = require('../../errors'),
sequence = require('when/sequence'), sequence = require('when/sequence'),
commands = require('./commands'), commands = require('./commands'),
versioning = require('../versioning'), versioning = require('../versioning'),
models = require('../../models'), models = require('../../models'),
fixtures = require('../fixtures'), fixtures = require('../fixtures'),
@ -44,7 +44,7 @@ backupDatabase = function backupDatabase() {
logInfo('Creating database backup'); logInfo('Creating database backup');
return dataExport().then(function (exportedData) { return dataExport().then(function (exportedData) {
// Save the exported data to the file system for download // Save the exported data to the file system for download
var fileName = path.resolve(config().paths.contentPath + '/data/' + dataExport.fileName()); var fileName = path.resolve(config.paths.contentPath + '/data/' + dataExport.fileName());
return nodefn.call(fs.writeFile, fileName, JSON.stringify(exportedData)).then(function () { return nodefn.call(fs.writeFile, fileName, JSON.stringify(exportedData)).then(function () {
logInfo('Database backup written to: ' + fileName); logInfo('Database backup written to: ' + fileName);

View File

@ -12,7 +12,7 @@ var _ = require('lodash'),
checkPostTable; checkPostTable;
doRawAndFlatten = function doRaw(query, flattenFn) { doRawAndFlatten = function doRaw(query, flattenFn) {
return config().database.knex.raw(query).then(function (response) { return config.database.knex.raw(query).then(function (response) {
return _.flatten(flattenFn(response)); return _.flatten(flattenFn(response));
}); });
}; };
@ -40,10 +40,10 @@ getColumns = function getColumns(table) {
// data type text instead of mediumtext. // data type text instead of mediumtext.
// For details see: https://github.com/TryGhost/Ghost/issues/1947 // For details see: https://github.com/TryGhost/Ghost/issues/1947
checkPostTable = function checkPostTable() { checkPostTable = function checkPostTable() {
return config().database.knex.raw('SHOW FIELDS FROM posts where Field ="html" OR Field = "markdown"').then(function (response) { return config.database.knex.raw('SHOW FIELDS FROM posts where Field ="html" OR Field = "markdown"').then(function (response) {
return _.flatten(_.map(response[0], function (entry) { return _.flatten(_.map(response[0], function (entry) {
if (entry.Type.toLowerCase() !== 'mediumtext') { if (entry.Type.toLowerCase() !== 'mediumtext') {
return config().database.knex.raw('ALTER TABLE posts MODIFY ' + entry.Field + ' MEDIUMTEXT').then(function () { return config.database.knex.raw('ALTER TABLE posts MODIFY ' + entry.Field + ' MEDIUMTEXT').then(function () {
return when.resolve(); return when.resolve();
}); });
} }

View File

@ -11,7 +11,7 @@ var _ = require('lodash'),
doRawFlattenAndPluck = function doRaw(query, name) { doRawFlattenAndPluck = function doRaw(query, name) {
return config().database.knex.raw(query).then(function (response) { return config.database.knex.raw(query).then(function (response) {
return _.flatten(_.pluck(response.rows, name)); return _.flatten(_.pluck(response.rows, name));
}); });
}; };

View File

@ -11,7 +11,7 @@ var _ = require('lodash'),
doRaw = function doRaw(query, fn) { doRaw = function doRaw(query, fn) {
return config().database.knex.raw(query).then(function (response) { return config.database.knex.raw(query).then(function (response) {
return fn(response); return fn(response);
}); });
}; };

View File

@ -44,28 +44,28 @@ function addTableColumn(tablename, table, columnname) {
} }
function addColumn(table, column) { function addColumn(table, column) {
dbConfig = dbConfig || config().database; dbConfig = dbConfig || config.database;
return dbConfig.knex.schema.table(table, function (t) { return dbConfig.knex.schema.table(table, function (t) {
addTableColumn(table, t, column); addTableColumn(table, t, column);
}); });
} }
function addUnique(table, column) { function addUnique(table, column) {
dbConfig = dbConfig || config().database; dbConfig = dbConfig || config.database;
return dbConfig.knex.schema.table(table, function (table) { return dbConfig.knex.schema.table(table, function (table) {
table.unique(column); table.unique(column);
}); });
} }
function dropUnique(table, column) { function dropUnique(table, column) {
dbConfig = dbConfig || config().database; dbConfig = dbConfig || config.database;
return dbConfig.knex.schema.table(table, function (table) { return dbConfig.knex.schema.table(table, function (table) {
table.dropUnique(column); table.dropUnique(column);
}); });
} }
function createTable(table) { function createTable(table) {
dbConfig = dbConfig || config().database; dbConfig = dbConfig || config.database;
return dbConfig.knex.schema.createTable(table, function (t) { return dbConfig.knex.schema.createTable(table, function (t) {
var columnKeys = _.keys(schema[table]); var columnKeys = _.keys(schema[table]);
_.each(columnKeys, function (column) { _.each(columnKeys, function (column) {
@ -75,12 +75,12 @@ function createTable(table) {
} }
function deleteTable(table) { function deleteTable(table) {
dbConfig = dbConfig || config().database; dbConfig = dbConfig || config.database;
return dbConfig.knex.schema.dropTableIfExists(table); return dbConfig.knex.schema.dropTableIfExists(table);
} }
function getTables() { function getTables() {
dbConfig = dbConfig || config().database; dbConfig = dbConfig || config.database;
var client = dbConfig.client; var client = dbConfig.client;
if (_.contains(_.keys(clients), client)) { if (_.contains(_.keys(clients), client)) {
@ -91,7 +91,7 @@ function getTables() {
} }
function getIndexes(table) { function getIndexes(table) {
dbConfig = dbConfig || config().database; dbConfig = dbConfig || config.database;
var client = dbConfig.client; var client = dbConfig.client;
if (_.contains(_.keys(clients), client)) { if (_.contains(_.keys(clients), client)) {
@ -102,7 +102,7 @@ function getIndexes(table) {
} }
function getColumns(table) { function getColumns(table) {
dbConfig = dbConfig || config().database; dbConfig = dbConfig || config.database;
var client = dbConfig.client; var client = dbConfig.client;
if (_.contains(_.keys(clients), client)) { if (_.contains(_.keys(clients), client)) {
@ -113,7 +113,7 @@ function getColumns(table) {
} }
function checkTables() { function checkTables() {
dbConfig = dbConfig || config().database; dbConfig = dbConfig || config.database;
var client = dbConfig.client; var client = dbConfig.client;
if (client === 'mysql') { if (client === 'mysql') {
@ -131,4 +131,4 @@ module.exports = {
dropUnique: dropUnique, dropUnique: dropUnique,
addColumn: addColumn, addColumn: addColumn,
getColumns: getColumns getColumns: getColumns
}; };

View File

@ -101,13 +101,13 @@ validateSettings = function (defaultSettings, model) {
// A Promise that will resolve to an object with a property for each installed theme. // A Promise that will resolve to an object with a property for each installed theme.
// This is necessary because certain configuration data is only available while Ghost // This is necessary because certain configuration data is only available while Ghost
// is running and at times the validations are used when it's not (e.g. tests) // is running and at times the validations are used when it's not (e.g. tests)
availableThemes = requireTree(config().paths.themePath); availableThemes = requireTree(config.paths.themePath);
validateActiveTheme = function (themeName) { validateActiveTheme = function (themeName) {
// If Ghost is running and its availableThemes collection exists // If Ghost is running and its availableThemes collection exists
// give it priority. // give it priority.
if (Object.keys(config().paths.availableThemes).length > 0) { if (config.paths.availableThemes && Object.keys(config.paths.availableThemes).length > 0) {
availableThemes = when(config().paths.availableThemes); availableThemes = when(config.paths.availableThemes);
} }
return availableThemes.then(function (themes) { return availableThemes.then(function (themes) {

View File

@ -23,7 +23,7 @@ function getDefaultDatabaseVersion() {
// The migration version number according to the database // The migration version number according to the database
// This is what the database is currently at and may need to be updated // This is what the database is currently at and may need to be updated
function getDatabaseVersion() { function getDatabaseVersion() {
var knex = config().database.knex; var knex = config.database.knex;
return knex.schema.hasTable('settings').then(function (exists) { return knex.schema.hasTable('settings').then(function (exists) {
// Check for the current version from the settings table // Check for the current version from the settings table
@ -54,7 +54,7 @@ function getDatabaseVersion() {
} }
function setDatabaseVersion() { function setDatabaseVersion() {
return config().database.knex('settings') return config.database.knex('settings')
.where('key', 'databaseVersion') .where('key', 'databaseVersion')
.update({ 'value': defaultDatabaseVersion }); .update({ 'value': defaultDatabaseVersion });
} }

View File

@ -17,7 +17,7 @@ var _ = require('lodash'),
errors, errors,
// Paths for views // Paths for views
defaultErrorTemplatePath = path.resolve(config().paths.adminViews, 'user-error.hbs'), defaultErrorTemplatePath = path.resolve(config.paths.adminViews, 'user-error.hbs'),
userErrorTemplateExists = false; userErrorTemplateExists = false;
// This is not useful but required for jshint // This is not useful but required for jshint
@ -28,7 +28,7 @@ colors.setTheme({silly: 'rainbow'});
*/ */
errors = { errors = {
updateActiveTheme: function (activeTheme) { updateActiveTheme: function (activeTheme) {
userErrorTemplateExists = config().paths.availableThemes[activeTheme].hasOwnProperty('error.hbs'); userErrorTemplateExists = config.paths.availableThemes[activeTheme].hasOwnProperty('error.hbs');
}, },
throwError: function (err) { throwError: function (err) {

View File

@ -100,7 +100,7 @@ coreHelpers.encode = function (context, str) {
// //
coreHelpers.page_url = function (context, block) { coreHelpers.page_url = function (context, block) {
/*jshint unused:false*/ /*jshint unused:false*/
var url = config().paths.subdir; var url = config.paths.subdir;
if (this.tagSlug !== undefined) { if (this.tagSlug !== undefined) {
url += '/tag/' + this.tagSlug; url += '/tag/' + this.tagSlug;
@ -173,7 +173,7 @@ coreHelpers.asset = function (context, options) {
var output = '', var output = '',
isAdmin = options && options.hash && options.hash.ghost; isAdmin = options && options.hash && options.hash.ghost;
output += config().paths.subdir + '/'; output += config.paths.subdir + '/';
if (!context.match(/^favicon\.ico$/) && !context.match(/^shared/) && !context.match(/^asset/)) { if (!context.match(/^favicon\.ico$/) && !context.match(/^shared/) && !context.match(/^asset/)) {
if (isAdmin) { if (isAdmin) {
@ -349,8 +349,8 @@ coreHelpers.excerpt = function (options) {
// Returns the config value for fileStorage. // Returns the config value for fileStorage.
coreHelpers.file_storage = function (context, options) { coreHelpers.file_storage = function (context, options) {
/*jshint unused:false*/ /*jshint unused:false*/
if (config().hasOwnProperty('fileStorage')) { if (config.hasOwnProperty('fileStorage')) {
return config().fileStorage.toString(); return config.fileStorage.toString();
} }
return 'true'; return 'true';
}; };
@ -363,8 +363,8 @@ coreHelpers.file_storage = function (context, options) {
// Returns the config value for apps. // Returns the config value for apps.
coreHelpers.apps = function (context, options) { coreHelpers.apps = function (context, options) {
/*jshint unused:false*/ /*jshint unused:false*/
if (config().hasOwnProperty('apps')) { if (config.hasOwnProperty('apps')) {
return config().apps.toString(); return config.apps.toString();
} }
return 'false'; return 'false';
}; };
@ -374,7 +374,7 @@ coreHelpers.ghost_script_tags = function () {
scriptList = _.map(scriptList, function (fileName) { scriptList = _.map(scriptList, function (fileName) {
return scriptTemplate({ return scriptTemplate({
source: config().paths.subdir + '/ghost/scripts/' + fileName, source: config.paths.subdir + '/ghost/scripts/' + fileName,
version: coreHelpers.assetHash version: coreHelpers.assetHash
}); });
}); });
@ -416,7 +416,7 @@ coreHelpers.body_class = function (options) {
return api.settings.read({context: {internal: true}, key: 'activeTheme'}).then(function (response) { return api.settings.read({context: {internal: true}, key: 'activeTheme'}).then(function (response) {
var activeTheme = response.settings[0], var activeTheme = response.settings[0],
paths = config().paths.availableThemes[activeTheme.value], paths = config.paths.availableThemes[activeTheme.value],
view; view;
if (post) { if (post) {
@ -494,7 +494,7 @@ coreHelpers.ghost_foot = function (options) {
foot = []; foot = [];
foot.push(scriptTemplate({ foot.push(scriptTemplate({
source: config().paths.subdir + '/public/' + jquery, source: config.paths.subdir + '/public/' + jquery,
version: coreHelpers.assetHash version: coreHelpers.assetHash
})); }));

View File

@ -41,7 +41,7 @@ function doFirstRun() {
'</strong>environment.', '</strong>environment.',
'Your URL is set to', 'Your URL is set to',
'<strong>' + config().url + '</strong>.', '<strong>' + config.url + '</strong>.',
'See <a href="http://docs.ghost.org/">http://docs.ghost.org</a> for instructions.' 'See <a href="http://docs.ghost.org/">http://docs.ghost.org</a> for instructions.'
]; ];
@ -76,7 +76,7 @@ function initDbHashAndFirstRun() {
// any are missing. // any are missing.
function builtFilesExist() { function builtFilesExist() {
var deferreds = [], var deferreds = [],
location = config().paths.builtScriptPath, location = config.paths.builtScriptPath,
fileNames = helpers.scriptFiles.ember; fileNames = helpers.scriptFiles.ember;
@ -127,7 +127,7 @@ function ghostStartMessages() {
console.log( console.log(
"Ghost is running...".green, "Ghost is running...".green,
"\nYour blog is now available on", "\nYour blog is now available on",
config().url, config.url,
"\nCtrl+C to shut down".grey "\nCtrl+C to shut down".grey
); );
@ -143,9 +143,9 @@ function ghostStartMessages() {
console.log( console.log(
("Ghost is running in " + process.env.NODE_ENV + "...").green, ("Ghost is running in " + process.env.NODE_ENV + "...").green,
"\nListening on", "\nListening on",
config.getSocket() || config().server.host + ':' + config().server.port, config.getSocket() || config.server.host + ':' + config.server.port,
"\nUrl configured as:", "\nUrl configured as:",
config().url, config.url,
"\nCtrl+C to shut down".grey "\nCtrl+C to shut down".grey
); );
// ensure that Ghost exits correctly on Ctrl+C // ensure that Ghost exits correctly on Ctrl+C
@ -231,7 +231,7 @@ function init(server) {
}).then(function () { }).then(function () {
// We must pass the api.settings object // We must pass the api.settings object
// into this method due to circular dependencies. // into this method due to circular dependencies.
return config.theme.update(api.settings, config().url); return config.theme.update(api.settings, config.url);
}).then(function () { }).then(function () {
return when.join( return when.join(
// Check for or initialise a dbHash. // Check for or initialise a dbHash.
@ -253,7 +253,7 @@ function init(server) {
express['static'].mime.define({'application/font-woff': ['woff']}); express['static'].mime.define({'application/font-woff': ['woff']});
// enabled gzip compression by default // enabled gzip compression by default
if (config().server.compress !== false) { if (config.server.compress !== false) {
server.use(compress()); server.use(compress());
} }
@ -271,11 +271,11 @@ function init(server) {
middleware(server, dbHash); middleware(server, dbHash);
// Log all theme errors and warnings // Log all theme errors and warnings
_.each(config().paths.availableThemes._messages.errors, function (error) { _.each(config.paths.availableThemes._messages.errors, function (error) {
errors.logError(error.message, error.context, error.help); errors.logError(error.message, error.context, error.help);
}); });
_.each(config().paths.availableThemes._messages.warns, function (warn) { _.each(config.paths.availableThemes._messages.warns, function (warn) {
errors.logWarn(warn.message, warn.context, warn.help); errors.logWarn(warn.message, warn.context, warn.help);
}); });
@ -295,8 +295,8 @@ function init(server) {
} else { } else {
httpServer = server.listen( httpServer = server.listen(
config().server.port, config.server.port,
config().server.host config.server.host
); );
} }

View File

@ -15,7 +15,7 @@ function GhostMailer(opts) {
GhostMailer.prototype.init = function () { GhostMailer.prototype.init = function () {
var self = this; var self = this;
self.state = {}; self.state = {};
if (config().mail && config().mail.transport) { if (config.mail && config.mail.transport) {
this.createTransport(); this.createTransport();
return when.resolve(); return when.resolve();
} }
@ -53,17 +53,17 @@ GhostMailer.prototype.detectSendmail = function () {
}; };
GhostMailer.prototype.createTransport = function () { GhostMailer.prototype.createTransport = function () {
this.transport = nodemailer.createTransport(config().mail.transport, _.clone(config().mail.options) || {}); this.transport = nodemailer.createTransport(config.mail.transport, _.clone(config.mail.options) || {});
}; };
GhostMailer.prototype.fromAddress = function () { GhostMailer.prototype.fromAddress = function () {
var from = config().mail && config().mail.fromaddress, var from = config.mail && config.mail.fromaddress,
domain; domain;
if (!from) { if (!from) {
// Extract the domain name from url set in config.js // Extract the domain name from url set in config.js
domain = config().url.match(new RegExp("^https?://([^/:?#]+)(?:[/:?#]|$)", "i")); domain = config.url.match(new RegExp("^https?://([^/:?#]+)(?:[/:?#]|$)", "i"));
domain = domain && domain[1]; domain = domain && domain[1];
// Default to ghost@[blog.url] // Default to ghost@[blog.url]

View File

@ -42,17 +42,17 @@ function ghostLocals(req, res, next) {
res.locals = res.locals || {}; res.locals = res.locals || {};
res.locals.version = packageInfo.version; res.locals.version = packageInfo.version;
// relative path from the URL, not including subdir // relative path from the URL, not including subdir
res.locals.relativeUrl = req.path.replace(config().paths.subdir, ''); res.locals.relativeUrl = req.path.replace(config.paths.subdir, '');
next(); next();
} }
function initThemeData(secure) { function initThemeData(secure) {
var themeConfig = config.theme(); var themeConfig = config.theme();
if (secure && config().urlSSL) { if (secure && config.urlSSL) {
// For secure requests override .url property with the SSL version // For secure requests override .url property with the SSL version
themeConfig = _.clone(themeConfig); themeConfig = _.clone(themeConfig);
themeConfig.url = config().urlSSL.replace(/\/$/, ''); themeConfig.url = config.urlSSL.replace(/\/$/, '');
} }
return themeConfig; return themeConfig;
} }
@ -61,13 +61,13 @@ function initThemeData(secure) {
// Helper for manageAdminAndTheme // Helper for manageAdminAndTheme
function activateTheme(activeTheme) { function activateTheme(activeTheme) {
var hbsOptions, var hbsOptions,
themePartials = path.join(config().paths.themePath, activeTheme, 'partials'); themePartials = path.join(config.paths.themePath, activeTheme, 'partials');
// clear the view cache // clear the view cache
expressServer.cache = {}; expressServer.cache = {};
// set view engine // set view engine
hbsOptions = { partialsDir: [ config().paths.helperTemplates ] }; hbsOptions = { partialsDir: [ config.paths.helperTemplates ] };
fs.stat(themePartials, function (err, stats) { fs.stat(themePartials, function (err, stats) {
// Check that the theme has a partials directory before trying to use it // Check that the theme has a partials directory before trying to use it
@ -89,18 +89,18 @@ function activateTheme(activeTheme) {
// Uses the URL to detect whether this response should be an admin response // Uses the URL to detect whether this response should be an admin response
// This is used to ensure the right content is served, and is not for security purposes // This is used to ensure the right content is served, and is not for security purposes
function decideContext(req, res, next) { function decideContext(req, res, next) {
res.isAdmin = req.url.lastIndexOf(config().paths.subdir + '/ghost/', 0) === 0; res.isAdmin = req.url.lastIndexOf(config.paths.subdir + '/ghost/', 0) === 0;
if (res.isAdmin) { if (res.isAdmin) {
expressServer.enable('admin'); expressServer.enable('admin');
expressServer.engine('hbs', expressServer.get('admin view engine')); expressServer.engine('hbs', expressServer.get('admin view engine'));
expressServer.set('views', config().paths.adminViews); expressServer.set('views', config.paths.adminViews);
} else { } else {
expressServer.disable('admin'); expressServer.disable('admin');
var themeData = initThemeData(req.secure); var themeData = initThemeData(req.secure);
hbs.updateTemplateOptions({ data: {blog: themeData} }); hbs.updateTemplateOptions({ data: {blog: themeData} });
expressServer.engine('hbs', expressServer.get('theme view engine')); expressServer.engine('hbs', expressServer.get('theme view engine'));
expressServer.set('views', path.join(config().paths.themePath, expressServer.get('activeTheme'))); expressServer.set('views', path.join(config.paths.themePath, expressServer.get('activeTheme')));
} }
// Pass 'secure' flag to the view engine // Pass 'secure' flag to the view engine
@ -120,7 +120,7 @@ function updateActiveTheme(req, res, next) {
// Check if the theme changed // Check if the theme changed
if (activeTheme.value !== expressServer.get('activeTheme')) { if (activeTheme.value !== expressServer.get('activeTheme')) {
// Change theme // Change theme
if (!config().paths.availableThemes.hasOwnProperty(activeTheme.value)) { if (!config.paths.availableThemes.hasOwnProperty(activeTheme.value)) {
if (!res.isAdmin) { if (!res.isAdmin) {
// Throw an error if the theme is not available, but not on the admin UI // Throw an error if the theme is not available, but not on the admin UI
return errors.throwError('The currently active theme ' + activeTheme.value + ' is missing.'); return errors.throwError('The currently active theme ' + activeTheme.value + ' is missing.');
@ -144,7 +144,7 @@ function redirectToSetup(req, res, next) {
api.authentication.isSetup().then(function (exists) { api.authentication.isSetup().then(function (exists) {
if (!exists.setup[0].status && !req.path.match(/\/ghost\/setup\//)) { if (!exists.setup[0].status && !req.path.match(/\/ghost\/setup\//)) {
return res.redirect(config().paths.subdir + '/ghost/setup/'); return res.redirect(config.paths.subdir + '/ghost/setup/');
} }
next(); next();
}).otherwise(function (err) { }).otherwise(function (err) {
@ -153,8 +153,8 @@ function redirectToSetup(req, res, next) {
} }
function isSSLrequired(isAdmin) { function isSSLrequired(isAdmin) {
var forceSSL = url.parse(config().url).protocol === 'https:' ? true : false, var forceSSL = url.parse(config.url).protocol === 'https:' ? true : false,
forceAdminSSL = (isAdmin && config().forceAdminSSL); forceAdminSSL = (isAdmin && config.forceAdminSSL);
if (forceSSL || forceAdminSSL) { if (forceSSL || forceAdminSSL) {
return true; return true;
} }
@ -166,7 +166,7 @@ function isSSLrequired(isAdmin) {
function checkSSL(req, res, next) { function checkSSL(req, res, next) {
if (isSSLrequired(res.isAdmin)) { if (isSSLrequired(res.isAdmin)) {
if (!req.secure) { if (!req.secure) {
var forceAdminSSL = config().forceAdminSSL, var forceAdminSSL = config.forceAdminSSL,
redirectUrl; redirectUrl;
// Check if forceAdminSSL: { redirect: false } is set, which means // Check if forceAdminSSL: { redirect: false } is set, which means
@ -175,7 +175,7 @@ function checkSSL(req, res, next) {
return res.send(403); return res.send(403);
} }
redirectUrl = url.parse(config().urlSSL || config().url); redirectUrl = url.parse(config.urlSSL || config.url);
return res.redirect(301, url.format({ return res.redirect(301, url.format({
protocol: 'https:', protocol: 'https:',
hostname: redirectUrl.hostname, hostname: redirectUrl.hostname,
@ -192,7 +192,7 @@ function checkSSL(req, res, next) {
// Handle requests to robots.txt and cache file // Handle requests to robots.txt and cache file
function robots() { function robots() {
var content, // file cache var content, // file cache
filePath = path.join(config().paths.corePath, '/shared/robots.txt'); filePath = path.join(config.paths.corePath, '/shared/robots.txt');
return function robots(req, res, next) { return function robots(req, res, next) {
if ('/robots.txt' === req.url) { if ('/robots.txt' === req.url) {
@ -224,9 +224,9 @@ function robots() {
} }
setupMiddleware = function (server) { setupMiddleware = function (server) {
var logging = config().logging, var logging = config.logging,
subdir = config().paths.subdir, subdir = config.paths.subdir,
corePath = config().paths.corePath, corePath = config.paths.corePath,
oauthServer = oauth2orize.createServer(); oauthServer = oauth2orize.createServer();
// silence JSHint without disabling unused check for the whole file // silence JSHint without disabling unused check for the whole file

View File

@ -43,7 +43,7 @@ var middleware = {
// SubPath is the url path starting after any default subdirectories // SubPath is the url path starting after any default subdirectories
// it is stripped of anything after the two levels `/ghost/.*?/` as the reset link has an argument // it is stripped of anything after the two levels `/ghost/.*?/` as the reset link has an argument
path = req.path.substring(config().paths.subdir.length); path = req.path.substring(config.paths.subdir.length);
/*jslint regexp:true, unparam:true*/ /*jslint regexp:true, unparam:true*/
subPath = path.replace(/^(\/.*?\/.*?\/)(.*)?/, function (match, a) { subPath = path.replace(/^(\/.*?\/.*?\/)(.*)?/, function (match, a) {
return a; return a;
@ -130,7 +130,7 @@ var middleware = {
api.settings.read({context: {internal: true}, key: 'activeTheme'}).then(function (response) { api.settings.read({context: {internal: true}, key: 'activeTheme'}).then(function (response) {
var activeTheme = response.settings[0]; var activeTheme = response.settings[0];
express['static'](path.join(config().paths.themePath, activeTheme.value), {maxAge: ONE_YEAR_MS})(req, res, next); express['static'](path.join(config.paths.themePath, activeTheme.value), {maxAge: ONE_YEAR_MS})(req, res, next);
}); });
}, },

View File

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

View File

@ -9,7 +9,7 @@ var admin = require('../controllers/admin'),
adminRoutes = function (middleware) { adminRoutes = function (middleware) {
var router = express.Router(), var router = express.Router(),
subdir = config().paths.subdir; subdir = config.paths.subdir;
// ### Admin routes // ### Admin routes
router.get(/^\/(logout|signout)\/$/, function redirect(req, res) { router.get(/^\/(logout|signout)\/$/, function redirect(req, res) {

View File

@ -9,7 +9,7 @@ var frontend = require('../controllers/frontend'),
frontendRoutes = function () { frontendRoutes = function () {
var router = express.Router(), var router = express.Router(),
subdir = config().paths.subdir; subdir = config.paths.subdir;
// ### Frontend routes // ### Frontend routes
router.get('/rss/', frontend.rss); router.get('/rss/', frontend.rss);

View File

@ -20,7 +20,7 @@ localFileStore = _.extend(baseStore, {
// - returns a promise which ultimately returns the full url to the uploaded image // - returns a promise which ultimately returns the full url to the uploaded image
'save': function (image) { 'save': function (image) {
var saved = when.defer(), var saved = when.defer(),
targetDir = this.getTargetDir(config().paths.imagesPath), targetDir = this.getTargetDir(config.paths.imagesPath),
targetFilename; targetFilename;
this.getUniqueFileName(this, image, targetDir).then(function (filename) { this.getUniqueFileName(this, image, targetDir).then(function (filename) {
@ -31,7 +31,7 @@ localFileStore = _.extend(baseStore, {
}).then(function () { }).then(function () {
// The src for the image must be in URI format, not a file system path, which in Windows uses \ // The src for the image must be in URI format, not a file system path, which in Windows uses \
// For local file system storage can use relative path so add a slash // For local file system storage can use relative path so add a slash
var fullUrl = (config().paths.subdir + '/' + config().paths.imagesRelPath + '/' + path.relative(config().paths.imagesPath, targetFilename)).replace(new RegExp('\\' + path.sep, 'g'), '/'); var fullUrl = (config.paths.subdir + '/' + config.paths.imagesRelPath + '/' + path.relative(config.paths.imagesPath, targetFilename)).replace(new RegExp('\\' + path.sep, 'g'), '/');
return saved.resolve(fullUrl); return saved.resolve(fullUrl);
}).otherwise(function (e) { }).otherwise(function (e) {
errors.logError(e); errors.logError(e);
@ -58,7 +58,7 @@ localFileStore = _.extend(baseStore, {
ONE_YEAR_MS = 365 * 24 * ONE_HOUR_MS; ONE_YEAR_MS = 365 * 24 * ONE_HOUR_MS;
// For some reason send divides the max age number by 1000 // For some reason send divides the max age number by 1000
return express['static'](config().paths.imagesPath, {maxAge: ONE_YEAR_MS}); return express['static'](config.paths.imagesPath, {maxAge: ONE_YEAR_MS});
} }
}); });

View File

@ -49,7 +49,7 @@ function updateCheckError(error) {
function updateCheckData() { function updateCheckData() {
var data = {}, var data = {},
ops = [], ops = [],
mailConfig = config().mail; mailConfig = config.mail;
ops.push(api.settings.read(_.extend(internal, {key: 'dbHash'})).otherwise(errors.rejectError)); ops.push(api.settings.read(_.extend(internal, {key: 'dbHash'})).otherwise(errors.rejectError));
ops.push(api.settings.read(_.extend(internal, {key: 'activeTheme'})).otherwise(errors.rejectError)); ops.push(api.settings.read(_.extend(internal, {key: 'activeTheme'})).otherwise(errors.rejectError));
@ -71,7 +71,7 @@ function updateCheckData() {
data.ghost_version = currentVersion; data.ghost_version = currentVersion;
data.node_version = process.versions.node; data.node_version = process.versions.node;
data.env = process.env.NODE_ENV; data.env = process.env.NODE_ENV;
data.database_type = config().database.client; data.database_type = config.database.client;
data.email_transport = mailConfig && (mailConfig.options && mailConfig.options.service ? mailConfig.options.service : mailConfig.transport); data.email_transport = mailConfig && (mailConfig.options && mailConfig.options.service ? mailConfig.options.service : mailConfig.transport);
return when.settle(ops).then(function (descriptors) { return when.settle(ops).then(function (descriptors) {
@ -81,7 +81,7 @@ function updateCheckData() {
posts = descriptors[3].value, posts = descriptors[3].value,
users = descriptors[4].value, users = descriptors[4].value,
npm = descriptors[5].value, npm = descriptors[5].value,
blogUrl = url.parse(config().url), blogUrl = url.parse(config.url),
blogId = blogUrl.hostname + blogUrl.pathname.replace(/\//, '') + hash.value; blogId = blogUrl.hostname + blogUrl.pathname.replace(/\//, '') + hash.value;
data.blog_id = crypto.createHash('md5').update(blogId).digest('hex'); data.blog_id = crypto.createHash('md5').update(blogId).digest('hex');
@ -175,7 +175,7 @@ function updateCheck() {
// 1. updateCheck is defined as false in config.js // 1. updateCheck is defined as false in config.js
// 2. we've already done a check this session // 2. we've already done a check this session
// 3. we're not in production or development mode // 3. we're not in production or development mode
if (config().updateCheck === false || _.indexOf(allowedCheckEnvironments, process.env.NODE_ENV) === -1) { if (config.updateCheck === false || _.indexOf(allowedCheckEnvironments, process.env.NODE_ENV) === -1) {
// No update check // No update check
deferred.resolve(); deferred.resolve();
} else { } else {

View File

@ -10,7 +10,7 @@ I18n = function (ghost) {
// TODO: validate // TODO: validate
var lang = ghost.settings('defaultLang'), var lang = ghost.settings('defaultLang'),
path = config().paths.lang, path = config.paths.lang,
langFilePath = path + lang + '.json'; langFilePath = path + lang + '.json';
return function (req, res, next) { return function (req, res, next) {

View File

@ -14,6 +14,7 @@ var _ = require('lodash'),
describe('Themes API', function () { describe('Themes API', function () {
var configStub, var configStub,
config,
sandbox, sandbox,
settingsReadStub; settingsReadStub;
@ -42,7 +43,7 @@ describe('Themes API', function () {
return when({ settings: [{value: 'casper'}] }); return when({ settings: [{value: 'casper'}] });
}); });
configStub = sandbox.stub().returns({ configStub = {
'paths': { 'paths': {
'subdir': '', 'subdir': '',
'availableThemes': { 'availableThemes': {
@ -54,7 +55,10 @@ describe('Themes API', function () {
} }
} }
} }
}); };
config = ThemeAPI.__get__('config');
_.extend(config, configStub);
done(); done();
}).catch(done); }).catch(done);
@ -63,12 +67,6 @@ describe('Themes API', function () {
should.exist(ThemeAPI); should.exist(ThemeAPI);
it('can browse', function (done) { it('can browse', function (done) {
var config;
config = ThemeAPI.__get__('config');
_.extend(configStub, config);
ThemeAPI.__set__('config', configStub);
ThemeAPI.browse({context: {user: 1}}).then(function (result) { ThemeAPI.browse({context: {user: 1}}).then(function (result) {
should.exist(result); should.exist(result);
result.themes.length.should.be.above(0); result.themes.length.should.be.above(0);
@ -80,12 +78,6 @@ describe('Themes API', function () {
}); });
it('can edit', function (done) { it('can edit', function (done) {
var config;
config = ThemeAPI.__get__('config');
_.extend(configStub, config);
ThemeAPI.__set__('config', configStub);
ThemeAPI.edit({themes: [{uuid: 'casper', active: true }]}, {context: {user: 1}}).then(function (result) { ThemeAPI.edit({themes: [{uuid: 'casper', active: true }]}, {context: {user: 1}}).then(function (result) {
should.exist(result); should.exist(result);
should.exist(result.themes); should.exist(result.themes);

View File

@ -164,7 +164,7 @@ describe('Settings Model', function () {
describe('populating defaults from settings.json', function () { describe('populating defaults from settings.json', function () {
beforeEach(function (done) { beforeEach(function (done) {
config().database.knex('settings').truncate().then(function () { config.database.knex('settings').truncate().then(function () {
done(); done();
}); });
}); });

View File

@ -1,11 +1,14 @@
var should = require('should'); /*globals describe, before, beforeEach, afterEach, after, it */
var when = require('when'); var should = require('should'),
var rewire = require('rewire'); rewire = require('rewire'),
var packageInfo = require('../../../package'); _ = require('lodash'),
var ghost = require('../../../core'); packageInfo = require('../../../package'),
var permissions = require('../../server/permissions'); ghost = require('../../../core'),
var testUtils = require('../utils'); config = rewire('../../../core/server/config'),
var updateCheck = rewire('../../server/update-check'); defaultConfig = require('../../../config.example')[process.env.NODE_ENV],
permissions = require('../../server/permissions'),
testUtils = require('../utils'),
updateCheck = rewire('../../server/update-check');
describe('Update Check', function () { describe('Update Check', function () {
var environmentsOrig; var environmentsOrig;
@ -14,7 +17,9 @@ describe('Update Check', function () {
environmentsOrig = updateCheck.__get__('allowedCheckEnvironments'); environmentsOrig = updateCheck.__get__('allowedCheckEnvironments');
updateCheck.__set__('allowedCheckEnvironments', ['development', 'production', 'testing']); updateCheck.__set__('allowedCheckEnvironments', ['development', 'production', 'testing']);
ghost().then(function () { _.extend(config, defaultConfig);
ghost({config: config.paths.config}).then(function () {
return testUtils.clearData(); return testUtils.clearData();
}).then(function () { }).then(function () {
done(); done();

View File

@ -35,7 +35,7 @@ describe('Bootstrap', function () {
// the test infrastructure is setup so that there is always config present, // the test infrastructure is setup so that there is always config present,
// but we want to overwrite the test to actually load config.example.js, so that any local changes // but we want to overwrite the test to actually load config.example.js, so that any local changes
// don't break the tests // don't break the tests
bootstrap.__set__('configFile', path.join(config().paths.appRoot, 'config.example.js')); bootstrap.__set__('configFile', path.join(config.paths.appRoot, 'config.example.js'));
bootstrap().then(function (config) { bootstrap().then(function (config) {
config.url.should.equal(defaultConfig.url); config.url.should.equal(defaultConfig.url);
@ -49,7 +49,7 @@ describe('Bootstrap', function () {
}); });
it('uses the passed in config file location', function (done) { it('uses the passed in config file location', function (done) {
bootstrap(path.join(config().paths.appRoot, 'config.example.js')).then(function (config) { bootstrap(path.join(config.paths.appRoot, 'config.example.js')).then(function (config) {
config.url.should.equal(defaultConfig.url); config.url.should.equal(defaultConfig.url);
config.database.client.should.equal(defaultConfig.database.client); config.database.client.should.equal(defaultConfig.database.client);
config.database.connection.should.eql(defaultConfig.database.connection); config.database.connection.should.eql(defaultConfig.database.connection);

View File

@ -1,4 +1,4 @@
/*globals describe, it, beforeEach, afterEach */ /*globals describe, it, before, beforeEach, afterEach */
/*jshint expr:true*/ /*jshint expr:true*/
var should = require('should'), var should = require('should'),
sinon = require('sinon'), sinon = require('sinon'),
@ -71,17 +71,16 @@ describe('Config', function () {
}); });
describe('Index', function () { describe('Index', function () {
// Make a copy of the default config file
// so we can restore it after every test.
// Using _.merge to recursively apply every property.
var defaultConfigFile = _.merge({}, config());
afterEach(function () { afterEach(function () {
configUpdate(defaultConfigFile); // Make a copy of the default config file
// so we can restore it after every test.
// Using _.merge to recursively apply every property.
configUpdate(_.merge({}, config));
}); });
it('should have exactly the right keys', function () { it('should have exactly the right keys', function () {
var pathConfig = config().paths; var pathConfig = config.paths;
// This will fail if there are any extra keys // This will fail if there are any extra keys
pathConfig.should.have.keys( pathConfig.should.have.keys(
@ -107,7 +106,7 @@ describe('Config', function () {
}); });
it('should have the correct values for each key', function () { it('should have the correct values for each key', function () {
var pathConfig = config().paths, var pathConfig = config.paths,
appRoot = path.resolve(__dirname, '../../../'); appRoot = path.resolve(__dirname, '../../../');
pathConfig.should.have.property('appRoot', appRoot); pathConfig.should.have.property('appRoot', appRoot);
@ -116,28 +115,28 @@ describe('Config', function () {
it('should not return a slash for subdir', function () { it('should not return a slash for subdir', function () {
configUpdate({url: 'http://my-ghost-blog.com'}); configUpdate({url: 'http://my-ghost-blog.com'});
config().paths.should.have.property('subdir', ''); config.paths.should.have.property('subdir', '');
configUpdate({url: 'http://my-ghost-blog.com/'}); configUpdate({url: 'http://my-ghost-blog.com/'});
config().paths.should.have.property('subdir', ''); config.paths.should.have.property('subdir', '');
}); });
it('should handle subdirectories properly', function () { it('should handle subdirectories properly', function () {
configUpdate({url: 'http://my-ghost-blog.com/blog'}); configUpdate({url: 'http://my-ghost-blog.com/blog'});
config().paths.should.have.property('subdir', '/blog'); config.paths.should.have.property('subdir', '/blog');
configUpdate({url: 'http://my-ghost-blog.com/blog/'}); configUpdate({url: 'http://my-ghost-blog.com/blog/'});
config().paths.should.have.property('subdir', '/blog'); config.paths.should.have.property('subdir', '/blog');
configUpdate({url: 'http://my-ghost-blog.com/my/blog'}); configUpdate({url: 'http://my-ghost-blog.com/my/blog'});
config().paths.should.have.property('subdir', '/my/blog'); config.paths.should.have.property('subdir', '/my/blog');
configUpdate({url: 'http://my-ghost-blog.com/my/blog/'}); configUpdate({url: 'http://my-ghost-blog.com/my/blog/'});
config().paths.should.have.property('subdir', '/my/blog'); config.paths.should.have.property('subdir', '/my/blog');
}); });
it('should allow specific properties to be user defined', function () { it('should allow specific properties to be user defined', function () {
var contentPath = path.join(config().paths.appRoot, 'otherContent', '/'), var contentPath = path.join(config.paths.appRoot, 'otherContent', '/'),
configFile = 'configFileDanceParty.js'; configFile = 'configFileDanceParty.js';
configUpdate({ configUpdate({
@ -147,16 +146,20 @@ describe('Config', function () {
} }
}); });
config().should.have.property('config', configFile); config.should.have.property('config', configFile);
config().paths.should.have.property('contentPath', contentPath); config.paths.should.have.property('contentPath', contentPath);
config().paths.should.have.property('themePath', contentPath + 'themes'); config.paths.should.have.property('themePath', contentPath + 'themes');
config().paths.should.have.property('appPath', contentPath + 'apps'); config.paths.should.have.property('appPath', contentPath + 'apps');
config().paths.should.have.property('imagesPath', contentPath + 'images'); config.paths.should.have.property('imagesPath', contentPath + 'images');
}); });
}); });
describe('urlFor', function () { describe('urlFor', function () {
before(function () {
configUpdate(_.merge({}, defaultConfig));
});
afterEach(function () { afterEach(function () {
configUpdate({url: defaultConfig.url}); configUpdate({url: defaultConfig.url});
}); });

View File

@ -215,24 +215,22 @@ describe('Error handling', function () {
before(function () { before(function () {
originalConfig = errors.__get__('config'); originalConfig = errors.__get__('config');
errors.__set__('config', function () { errors.__set__('config', {
return { 'paths': {
'paths': { 'themePath': '/content/themes',
'themePath': '/content/themes', 'availableThemes': {
'availableThemes': { 'casper': {
'casper': { 'assets': null,
'assets': null, 'default.hbs': '/content/themes/casper/default.hbs',
'default.hbs': '/content/themes/casper/default.hbs', 'index.hbs': '/content/themes/casper/index.hbs',
'index.hbs': '/content/themes/casper/index.hbs', 'page.hbs': '/content/themes/casper/page.hbs',
'page.hbs': '/content/themes/casper/page.hbs', 'tag.hbs': '/content/themes/casper/tag.hbs'
'tag.hbs': '/content/themes/casper/tag.hbs' },
}, 'theme-with-error': {
'theme-with-error': { 'error.hbs': ''
'error.hbs':''
}
} }
} }
}; }
}); });
errors.updateActiveTheme('casper'); errors.updateActiveTheme('casper');
}); });

View File

@ -10,6 +10,7 @@ var assert = require('assert'),
// Stuff we are testing // Stuff we are testing
api = require('../../server/api'), api = require('../../server/api'),
config = rewire('../../server/config'),
frontend = rewire('../../server/controllers/frontend'); frontend = rewire('../../server/controllers/frontend');
// To stop jshint complaining // To stop jshint complaining
@ -88,10 +89,8 @@ describe('Frontend Controller', function () {
}); });
it('Redirects to home if page number is 0 with subdirectory', function () { it('Redirects to home if page number is 0 with subdirectory', function () {
frontend.__set__('config', function() { frontend.__set__('config', {
return { paths: {subdir: '/blog'}
paths: {subdir: '/blog'}
};
}); });
var req = {params: {page: 0}, route: {path: '/page/:page/'}}; var req = {params: {page: 0}, route: {path: '/page/:page/'}};
@ -104,10 +103,8 @@ describe('Frontend Controller', function () {
}); });
it('Redirects to home if page number is 1 with subdirectory', function () { it('Redirects to home if page number is 1 with subdirectory', function () {
frontend.__set__('config', function() { frontend.__set__('config', {
return { paths: {subdir: '/blog'}
paths: {subdir: '/blog'}
};
}); });
var req = {params: {page: 1}, route: {path: '/page/:page/'}}; var req = {params: {page: 1}, route: {path: '/page/:page/'}};
@ -131,10 +128,8 @@ describe('Frontend Controller', function () {
}); });
it('Redirects to last page if page number too big with subdirectory', function (done) { it('Redirects to last page if page number too big with subdirectory', function (done) {
frontend.__set__('config', function() { frontend.__set__('config', {
return { paths: {subdir: '/blog'}
paths: {subdir: '/blog'}
};
}); });
var req = {params: {page: 4}, route: {path: '/page/:page/'}}; var req = {params: {page: 4}, route: {path: '/page/:page/'}};
@ -217,7 +212,7 @@ describe('Frontend Controller', function () {
}] }]
})); }));
frontend.__set__('config', sandbox.stub().returns({ frontend.__set__('config', {
'paths': { 'paths': {
'subdir': '', 'subdir': '',
'availableThemes': { 'availableThemes': {
@ -230,7 +225,7 @@ describe('Frontend Controller', function () {
} }
} }
} }
})); });
}); });
describe('custom tag template', function () { describe('custom tag template', function () {
@ -317,10 +312,8 @@ describe('Frontend Controller', function () {
}); });
it('Redirects to base tag page if page number is 0 with subdirectory', function () { it('Redirects to base tag page if page number is 0 with subdirectory', function () {
frontend.__set__('config', function() { frontend.__set__('config', {
return { paths: {subdir: '/blog'}
paths: {subdir: '/blog'}
};
}); });
var req = {params: {page: 0, slug: 'pumpkin'}}; var req = {params: {page: 0, slug: 'pumpkin'}};
@ -333,10 +326,8 @@ describe('Frontend Controller', function () {
}); });
it('Redirects to base tag page if page number is 1 with subdirectory', function () { it('Redirects to base tag page if page number is 1 with subdirectory', function () {
frontend.__set__('config', function() { frontend.__set__('config', {
return { paths: {subdir: '/blog'}
paths: {subdir: '/blog'}
};
}); });
var req = {params: {page: 1, slug: 'pumpkin'}}; var req = {params: {page: 1, slug: 'pumpkin'}};
@ -360,10 +351,8 @@ describe('Frontend Controller', function () {
}); });
it('Redirects to last page if page number too big with subdirectory', function (done) { it('Redirects to last page if page number too big with subdirectory', function (done) {
frontend.__set__('config', function() { frontend.__set__('config', {
return { paths: {subdir: '/blog'}
paths: {subdir: '/blog'}
};
}); });
var req = {params: {page: 4, slug: 'pumpkin'}}; var req = {params: {page: 4, slug: 'pumpkin'}};
@ -435,7 +424,7 @@ describe('Frontend Controller', function () {
}] }]
})); }));
frontend.__set__('config', sandbox.stub().returns({ frontend.__set__('config', {
'paths': { 'paths': {
'subdir': '', 'subdir': '',
'availableThemes': { 'availableThemes': {
@ -449,7 +438,7 @@ describe('Frontend Controller', function () {
} }
} }
} }
})); });
}); });
describe('static pages', function () { describe('static pages', function () {
@ -895,13 +884,10 @@ describe('Frontend Controller', function () {
describe('rss redirects', function () { describe('rss redirects', function () {
var res, var res,
apiUsersStub, apiUsersStub,
overwriteConfig = function(newConfig) { configUpdate = config.__get__('updateConfig'),
overwriteConfig = function (newConfig) {
var existingConfig = frontend.__get__('config'); var existingConfig = frontend.__get__('config');
var newConfigModule = function() { configUpdate(_.extend(existingConfig, newConfig));
return newConfig;
};
newConfigModule.urlFor = existingConfig.urlFor;
frontend.__set__('config', newConfigModule);
}; };
beforeEach(function () { beforeEach(function () {
@ -995,6 +981,8 @@ describe('Frontend Controller', function () {
}); });
it('Redirects to last page if page number too big', function (done) { it('Redirects to last page if page number too big', function (done) {
overwriteConfig({paths: {subdir: ''}});
var req = {params: {page: 4}, route: {path: '/rss/:page/'}}; var req = {params: {page: 4}, route: {path: '/rss/:page/'}};
frontend.rss(req, res, done).then(function () { frontend.rss(req, res, done).then(function () {

View File

@ -6,10 +6,13 @@ var testUtils = require('../utils'),
when = require('when'), when = require('when'),
assert = require('assert'), assert = require('assert'),
_ = require('lodash'), _ = require('lodash'),
rewire = require('rewire'),
// Stuff we are testing // Stuff we are testing
config = require('../../server/config'), config = rewire('../../server/config'),
migration = require('../../server/data/migration'), configUpdate = config.__get__('updateConfig'),
defaultConfig = rewire('../../../config.example')[process.env.NODE_ENV],
migration = rewire('../../server/data/migration'),
versioning = require('../../server/data/versioning'), versioning = require('../../server/data/versioning'),
exporter = require('../../server/data/export'), exporter = require('../../server/data/export'),
importer = require('../../server/data/import'), importer = require('../../server/data/import'),
@ -23,10 +26,14 @@ describe('Import', function () {
should.exist(exporter); should.exist(exporter);
should.exist(importer); should.exist(importer);
var sandbox, var sandbox, knex;
knex = config().database.knex;
beforeEach(function (done) { beforeEach(function (done) {
var newConfig = _.extend(config, defaultConfig);
migration.__get__('config', newConfig);
configUpdate(newConfig);
knex = config.database.knex;
sandbox = sinon.sandbox.create(); sandbox = sinon.sandbox.create();
// clear database... we need to initialise it manually for each test // clear database... we need to initialise it manually for each test
testUtils.clearData().then(function () { testUtils.clearData().then(function () {

View File

@ -14,8 +14,7 @@ var should = require('should'),
fakeConfig, fakeConfig,
fakeSettings, fakeSettings,
fakeSendmail, fakeSendmail,
sandbox = sinon.sandbox.create(), sandbox = sinon.sandbox.create();
config;
// Mock SMTP config // Mock SMTP config
SMTP = { SMTP = {
@ -39,9 +38,11 @@ SENDMAIL = {
describe('Mail', function () { describe('Mail', function () {
var overrideConfig = function (newConfig) { var overrideConfig = function (newConfig) {
mailer.__set__('config', sandbox.stub().returns( var config = rewire('../../server/config'),
_.extend({}, defaultConfig, newConfig) configUpdate = config.__get__('updateConfig'),
)); existingConfig = mailer.__get__('config');
configUpdate(_.extend(existingConfig, newConfig));
}; };
beforeEach(function () { beforeEach(function () {
@ -53,7 +54,7 @@ describe('Mail', function () {
}; };
fakeSendmail = '/fake/bin/sendmail'; fakeSendmail = '/fake/bin/sendmail';
config = sinon.stub().returns(fakeConfig); overrideConfig(fakeConfig);
sandbox.stub(mailer, 'isWindows', function () { sandbox.stub(mailer, 'isWindows', function () {
return false; return false;

View File

@ -9,7 +9,6 @@ var should = require('should'),
Polyglot = require('node-polyglot'), Polyglot = require('node-polyglot'),
api = require('../../server/api'), api = require('../../server/api'),
hbs = require('express-hbs'), hbs = require('express-hbs'),
packageInfo = require('../../../package'),
// Stuff we are testing // Stuff we are testing
handlebars = hbs.handlebars, handlebars = hbs.handlebars,
@ -21,15 +20,14 @@ describe('Core Helpers', function () {
var sandbox, var sandbox,
apiStub, apiStub,
configStub,
overrideConfig = function (newConfig) { overrideConfig = function (newConfig) {
helpers.__set__('config', function() { var existingConfig = helpers.__get__('config');
return newConfig; configUpdate(_.extend(existingConfig, newConfig));
});
}; };
beforeEach(function (done) { beforeEach(function (done) {
var adminHbs = hbs.create(); var adminHbs = hbs.create(),
existingConfig = helpers.__get__('config');
helpers = rewire('../../server/helpers'); helpers = rewire('../../server/helpers');
sandbox = sinon.sandbox.create(); sandbox = sinon.sandbox.create();
apiStub = sandbox.stub(api.settings, 'read', function () { apiStub = sandbox.stub(api.settings, 'read', function () {
@ -38,8 +36,7 @@ describe('Core Helpers', function () {
}); });
}); });
config = helpers.__get__('config'); overrideConfig({
configStub = sandbox.stub().returns({
'paths': { 'paths': {
'subdir': '', 'subdir': '',
'availableThemes': { 'availableThemes': {
@ -54,17 +51,16 @@ describe('Core Helpers', function () {
} }
} }
}); });
_.extend(configStub, config);
configStub.theme = sandbox.stub().returns({ existingConfig.theme = sandbox.stub().returns({
title: 'Ghost', title: 'Ghost',
description: 'Just a blogging platform.', description: 'Just a blogging platform.',
url: 'http://testurl.com' url: 'http://testurl.com'
}); });
helpers.__set__('config', configStub);
helpers.loadCoreHelpers(adminHbs); helpers.loadCoreHelpers(adminHbs);
// Load template helpers in handlebars // Load template helpers in handlebars
hbs.express3({ partialsDir: [config().paths.helperTemplates] }); hbs.express3({ partialsDir: [config.paths.helperTemplates] });
hbs.cachePartials(function () { hbs.cachePartials(function () {
done(); done();
}); });
@ -400,7 +396,7 @@ describe('Core Helpers', function () {
describe('ghost_head Helper', function () { describe('ghost_head Helper', function () {
// TODO: these tests should be easier to do! // TODO: these tests should be easier to do!
var configUrl = config().url; var configUrl = config.url;
afterEach(function () { afterEach(function () {
configUpdate({url: configUrl}); configUpdate({url: configUrl});
@ -625,10 +621,8 @@ describe('Core Helpers', function () {
}); });
it('can return a valid url with subdirectory', function () { it('can return a valid url with subdirectory', function () {
helpers.__set__('config', function() { _.extend(helpers.__get__('config'), {
return { paths: {'subdir': '/blog'}
paths: {'subdir': '/blog'}
};
}); });
helpers.page_url(1).should.equal('/blog/'); helpers.page_url(1).should.equal('/blog/');
helpers.page_url(2).should.equal('/blog/page/2/'); helpers.page_url(2).should.equal('/blog/page/2/');
@ -645,10 +639,8 @@ describe('Core Helpers', function () {
}); });
it('can return a valid url for tag pages with subdirectory', function () { it('can return a valid url for tag pages with subdirectory', function () {
helpers.__set__('config', function() { _.extend(helpers.__get__('config'), {
return { paths: {'subdir': '/blog'}
paths: {'subdir': '/blog'}
};
}); });
var tagContext = { var tagContext = {
tagSlug: 'pumpkin' tagSlug: 'pumpkin'
@ -671,10 +663,8 @@ describe('Core Helpers', function () {
}); });
it('can return a valid url with subdirectory', function () { it('can return a valid url with subdirectory', function () {
helpers.__set__('config', function() { _.extend(helpers.__get__('config'), {
return { paths: {'subdir': '/blog'}
paths: {'subdir': '/blog'}
};
}); });
helpers.pageUrl(1).should.equal('/blog/'); helpers.pageUrl(1).should.equal('/blog/');
helpers.pageUrl(2).should.equal('/blog/page/2/'); helpers.pageUrl(2).should.equal('/blog/page/2/');
@ -691,10 +681,8 @@ describe('Core Helpers', function () {
}); });
it('can return a valid url for tag pages with subdirectory', function () { it('can return a valid url for tag pages with subdirectory', function () {
helpers.__set__('config', function() { _.extend(helpers.__get__('config'), {
return { paths: {'subdir': '/blog'}
paths: {'subdir': '/blog'}
};
}); });
var tagContext = { var tagContext = {
tagSlug: 'pumpkin' tagSlug: 'pumpkin'
@ -1430,7 +1418,7 @@ describe('Core Helpers', function () {
describe('adminUrl', function () { describe('adminUrl', function () {
var rendered, var rendered,
configUrl = config().url; configUrl = config.url;
afterEach(function () { afterEach(function () {
configUpdate({url: configUrl}); configUpdate({url: configUrl});
@ -1519,19 +1507,15 @@ describe('Core Helpers', function () {
fileStorage.should.equal('true'); fileStorage.should.equal('true');
}); });
it('should return the config().fileStorage value when it exists', function () { it('should return the config.fileStorage value when it exists', function () {
var setting = 'file storage value', var setting = 'file storage value',
cfg = helpers.__get__('config'), cfg = helpers.__get__('config'),
fileStorage; fileStorage;
configStub = sandbox.stub().returns({ _.extend(cfg, {
fileStorage: setting fileStorage: setting
}); });
_.extend(cfg, configStub);
helpers.__set__('config', cfg);
fileStorage = helpers.file_storage(); fileStorage = helpers.file_storage();
should.exist(fileStorage); should.exist(fileStorage);
@ -1552,19 +1536,15 @@ describe('Core Helpers', function () {
apps.should.equal('false'); apps.should.equal('false');
}); });
it('should return the config().apps value when it exists', function () { it('should return the config.apps value when it exists', function () {
var setting = 'app value', var setting = 'app value',
cfg = helpers.__get__('config'), cfg = helpers.__get__('config'),
apps; apps;
configStub = sandbox.stub().returns({ _.extend(cfg, {
apps: setting apps: setting
}); });
_.extend(cfg, configStub);
helpers.__set__('config', cfg);
apps = helpers.apps(); apps = helpers.apps();
should.exist(apps); should.exist(apps);

View File

@ -3,18 +3,29 @@
var fs = require('fs-extra'), var fs = require('fs-extra'),
path = require('path'), path = require('path'),
should = require('should'), should = require('should'),
config = require('../../server/config'),
sinon = require('sinon'), sinon = require('sinon'),
localfilesystem = require('../../server/storage/localfilesystem'); rewire = require('rewire'),
_ = require('lodash'),
config = rewire('../../server/config'),
configUpdate = config.__get__('updateConfig'),
localfilesystem = rewire('../../server/storage/localfilesystem');
// To stop jshint complaining // To stop jshint complaining
should.equal(true, true); should.equal(true, true);
describe('Local File System Storage', function () { describe('Local File System Storage', function () {
var image; var image,
overrideConfig = function (newConfig) {
var existingConfig = localfilesystem.__get__('config'),
updatedConfig = _.extend(existingConfig, newConfig);
configUpdate(updatedConfig);
localfilesystem.__set__('config', updatedConfig);
};
beforeEach(function () { beforeEach(function () {
overrideConfig(config);
sinon.stub(fs, 'mkdirs').yields(); sinon.stub(fs, 'mkdirs').yields();
sinon.stub(fs, 'copy').yields(); sinon.stub(fs, 'copy').yields();
sinon.stub(fs, 'exists').yields(false); sinon.stub(fs, 'exists').yields(false);
@ -121,17 +132,17 @@ describe('Local File System Storage', function () {
}); });
describe('when a custom content path is used', function () { describe('when a custom content path is used', function () {
var origContentPath = config().paths.contentPath; var origContentPath = config.paths.contentPath;
var origImagesPath = config().paths.imagesPath; var origImagesPath = config.paths.imagesPath;
beforeEach(function () { beforeEach(function () {
config().paths.contentPath = config().paths.appRoot + '/var/ghostcms'; config.paths.contentPath = config.paths.appRoot + '/var/ghostcms';
config().paths.imagesPath = config().paths.appRoot + '/var/ghostcms/' + config().paths.imagesRelPath; config.paths.imagesPath = config.paths.appRoot + '/var/ghostcms/' + config.paths.imagesRelPath;
}); });
afterEach(function () { afterEach(function () {
config().paths.contentPath = origContentPath; config.paths.contentPath = origContentPath;
config().paths.imagesPath = origImagesPath; config.paths.imagesPath = origImagesPath;
}); });
it('should send the correct path to image', function (done) { it('should send the correct path to image', function (done) {

View File

@ -36,8 +36,8 @@ function findFreePort(port) {
// passing to forkGhost() method // passing to forkGhost() method
function forkConfig() { function forkConfig() {
// require caches values, and we want to read it fresh from the file // require caches values, and we want to read it fresh from the file
delete require.cache[config().paths.config]; delete require.cache[config.paths.config];
return _.cloneDeep(require(config().paths.config)[process.env.NODE_ENV]); return _.cloneDeep(require(config.paths.config)[process.env.NODE_ENV]);
} }
// Creates a new fork of Ghost process with a given config // Creates a new fork of Ghost process with a given config
@ -51,7 +51,7 @@ function forkGhost(newConfig, envName) {
newConfig.server.port = port; newConfig.server.port = port;
newConfig.url = url.format(_.extend(url.parse(newConfig.url), {port: port, host: null})); newConfig.url = url.format(_.extend(url.parse(newConfig.url), {port: port, host: null}));
var newConfigFile = path.join(config().paths.appRoot, 'config.test' + port + '.js'); var newConfigFile = path.join(config.paths.appRoot, 'config.test' + port + '.js');
fs.writeFile(newConfigFile, 'module.exports = {' + envName + ': ' + JSON.stringify(newConfig) + '}', function(err) { fs.writeFile(newConfigFile, 'module.exports = {' + envName + ': ' + JSON.stringify(newConfig) + '}', function(err) {
if (err) throw err; if (err) throw err;
@ -59,7 +59,7 @@ function forkGhost(newConfig, envName) {
var env = _.clone(process.env); var env = _.clone(process.env);
env['GHOST_CONFIG'] = newConfigFile; env['GHOST_CONFIG'] = newConfigFile;
env['NODE_ENV'] = envName; env['NODE_ENV'] = envName;
var child = cp.fork(path.join(config().paths.appRoot, 'index.js'), {env: env}); var child = cp.fork(path.join(config.paths.appRoot, 'index.js'), {env: env});
var pingTries = 0; var pingTries = 0;
var pingCheck; var pingCheck;

View File

@ -1,5 +1,4 @@
var knex = require('../../server/models/base').knex, var when = require('when'),
when = require('when'),
sequence = require('when/sequence'), sequence = require('when/sequence'),
nodefn = require('when/node'), nodefn = require('when/node'),
_ = require('lodash'), _ = require('lodash'),
@ -9,6 +8,7 @@ var knex = require('../../server/models/base').knex,
DataGenerator = require('./fixtures/data-generator'), DataGenerator = require('./fixtures/data-generator'),
API = require('./api'), API = require('./api'),
fork = require('./fork'), fork = require('./fork'),
config = require('../../server/config'),
teardown; teardown;
@ -22,6 +22,7 @@ function clearData() {
} }
function insertPosts() { function insertPosts() {
var knex = config.database.knex;
// ToDo: Get rid of pyramid of doom // ToDo: Get rid of pyramid of doom
return when(knex('posts').insert(DataGenerator.forKnex.posts).then(function () { return when(knex('posts').insert(DataGenerator.forKnex.posts).then(function () {
return knex('tags').insert(DataGenerator.forKnex.tags).then(function () { return knex('tags').insert(DataGenerator.forKnex.tags).then(function () {
@ -34,7 +35,8 @@ function insertMorePosts(max) {
var lang, var lang,
status, status,
posts = [], posts = [],
i, j, k = 0; i, j, k = 0,
knex = config.database.knex;
max = max || 50; max = max || 50;
@ -57,6 +59,7 @@ function insertMorePosts(max) {
function insertMorePostsTags(max) { function insertMorePostsTags(max) {
max = max || 50; max = max || 50;
var knex = config.database.knex;
return when.all([ return when.all([
// PostgreSQL can return results in any order // PostgreSQL can return results in any order
@ -79,8 +82,8 @@ function insertMorePostsTags(max) {
promises.push(DataGenerator.forKnex.createPostsTags(posts[i], injectionTagId)); promises.push(DataGenerator.forKnex.createPostsTags(posts[i], injectionTagId));
} }
return sequence(_.times(promises.length, function(index) { return sequence(_.times(promises.length, function (index) {
return function() { return function () {
return knex('posts_tags').insert(promises[index]); return knex('posts_tags').insert(promises[index]);
}; };
})); }));
@ -88,7 +91,8 @@ function insertMorePostsTags(max) {
} }
function insertDefaultUser() { function insertDefaultUser() {
var user; var user,
knex = config.database.knex;
user = DataGenerator.forKnex.createUser(DataGenerator.Content.users[0]); user = DataGenerator.forKnex.createUser(DataGenerator.Content.users[0]);
@ -99,7 +103,8 @@ function insertDefaultUser() {
function insertAdminUser() { function insertAdminUser() {
var users = [], var users = [],
userRoles = []; userRoles = [],
knex = config.database.knex;
users.push(DataGenerator.forKnex.createUser(DataGenerator.Content.users[1])); users.push(DataGenerator.forKnex.createUser(DataGenerator.Content.users[1]));
userRoles.push(DataGenerator.forKnex.createUserRole(2, 2)); userRoles.push(DataGenerator.forKnex.createUserRole(2, 2));
@ -112,7 +117,8 @@ function insertAdminUser() {
function insertEditorUser() { function insertEditorUser() {
var users = [], var users = [],
userRoles = []; userRoles = [],
knex = config.database.knex;
users.push(DataGenerator.forKnex.createUser(DataGenerator.Content.users[2])); users.push(DataGenerator.forKnex.createUser(DataGenerator.Content.users[2]));
userRoles.push(DataGenerator.forKnex.createUserRole(3, 2)); userRoles.push(DataGenerator.forKnex.createUserRole(3, 2));
@ -125,7 +131,8 @@ function insertEditorUser() {
function insertAuthorUser() { function insertAuthorUser() {
var users = [], var users = [],
userRoles = []; userRoles = [],
knex = config.database.knex;
users.push(DataGenerator.forKnex.createUser(DataGenerator.Content.users[3])); users.push(DataGenerator.forKnex.createUser(DataGenerator.Content.users[3]));
userRoles.push(DataGenerator.forKnex.createUserRole(4, 3)); userRoles.push(DataGenerator.forKnex.createUserRole(4, 3));
@ -137,7 +144,8 @@ function insertAuthorUser() {
} }
function insertDefaultApp() { function insertDefaultApp() {
var apps = []; var apps = [],
knex = config.database.knex;
apps.push(DataGenerator.forKnex.createApp(DataGenerator.Content.apps[0])); apps.push(DataGenerator.forKnex.createApp(DataGenerator.Content.apps[0]));
@ -164,13 +172,16 @@ function insertDefaultApp() {
} }
function insertApps() { function insertApps() {
var knex = config.database.knex;
return knex('apps').insert(DataGenerator.forKnex.apps).then(function () { return knex('apps').insert(DataGenerator.forKnex.apps).then(function () {
return knex('app_fields').insert(DataGenerator.forKnex.app_fields); return knex('app_fields').insert(DataGenerator.forKnex.app_fields);
}); });
} }
function insertAppWithSettings() { function insertAppWithSettings() {
var apps = [], app_settings = []; var apps = [],
app_settings = [],
knex = config.database.knex;
apps.push(DataGenerator.forKnex.createApp(DataGenerator.Content.apps[0])); apps.push(DataGenerator.forKnex.createApp(DataGenerator.Content.apps[0]));
app_settings.push(DataGenerator.forKnex.createAppSetting(DataGenerator.Content.app_settings[0])); app_settings.push(DataGenerator.forKnex.createAppSetting(DataGenerator.Content.app_settings[0]));
@ -178,9 +189,9 @@ function insertAppWithSettings() {
return knex('apps').insert(apps, 'id') return knex('apps').insert(apps, 'id')
.then(function (results) { .then(function (results) {
var appId = results[0]; var appId = results[0], i;
for (var i = 0; i < app_settings.length; i++) { for (i = 0; i < app_settings.length; i++) {
app_settings[i].app_id = appId; app_settings[i].app_id = appId;
} }
@ -188,7 +199,9 @@ function insertAppWithSettings() {
}); });
} }
function insertAppWithFields() { function insertAppWithFields() {
var apps = [], app_fields = []; var apps = [],
app_fields = [],
knex = config.database.knex;
apps.push(DataGenerator.forKnex.createApp(DataGenerator.Content.apps[0])); apps.push(DataGenerator.forKnex.createApp(DataGenerator.Content.apps[0]));
app_fields.push(DataGenerator.forKnex.createAppField(DataGenerator.Content.app_fields[0])); app_fields.push(DataGenerator.forKnex.createAppField(DataGenerator.Content.app_fields[0]));