Revert "Merge branch 'fat-models' of github.com:tgriesser/Ghost into tgriesser-fat-models"

This reverts commit 611e6a49ef, reversing
changes made to a89dc1d123.
This commit is contained in:
ErisDS 2013-05-30 23:34:53 +01:00
parent 611e6a49ef
commit 237af40f2e
27 changed files with 584 additions and 461 deletions

4
app.js
View File

@ -77,8 +77,8 @@
* Expose the standard locals that every external page should have available; * Expose the standard locals that every external page should have available;
* path, navItems and ghostGlobals * path, navItems and ghostGlobals
*/ */
ghostLocals = function (req, res, next) { ghostLocals = function(req, res, next) {
ghost.doFilter('ghostNavItems', {path: req.path, navItems: []}, function (navData) { ghost.doFilter('ghostNavItems', {path: req.path, navItems: []}, function(navData) {
// Make sure we have a locals value. // Make sure we have a locals value.
res.locals = res.locals || {}; res.locals = res.locals || {};

View File

@ -85,16 +85,6 @@
production: {} production: {}
}; };
config.globals = {
url: 'http://localhost:3333', //'http://john.onolan.org',
title: "John O'Nolan",
description: "Interactive designer, public speaker, startup advisor and writer. Living in Austria, attempting world domination via keyboard."
};
/** /**
* @property {Array} nav * @property {Array} nav
*/ */

View File

@ -182,11 +182,4 @@ $green: #9FBB58;
text-decoration: none; text-decoration: none;
} }
} }
} }
/* =============================================================================
Widgets
============================================================================= */
$widget-base-height: 300px;
$widget-base-width: 340px;

View File

@ -43,7 +43,7 @@
} }
}; };
ghost.doFilter('messWithAdmin', adminNavbar, function () { ghost.doFilter('messWithAdmin', adminNavbar, function() {
console.log('the dofilter hook called in /core/admin/controllers/index.js'); console.log('the dofilter hook called in /core/admin/controllers/index.js');
}); });

View File

@ -16,14 +16,14 @@
'homepage': function (req, res) { 'homepage': function (req, res) {
api.posts.browse().then(function (posts) { api.posts.browse().then(function (posts) {
ghost.doFilter('prePostsRender', posts.toJSON(), function (posts) { ghost.doFilter('prePostsRender', posts.toJSON(), function (posts) {
res.render('index', {posts: posts, ghostGlobals: ghost.globalConfig, navItems: res.locals.navItems}); res.render('index', {posts: posts, ghostGlobals: res.locals.ghostGlobals, navItems: res.locals.navItems});
}); });
}); });
}, },
'single': function (req, res) { 'single': function (req, res) {
api.posts.read({'slug': req.params.slug}).then(function (post) { api.posts.read({'slug': req.params.slug}).then(function (post) {
ghost.doFilter('prePostsRender', post.toJSON(), function (post) { ghost.doFilter('prePostsRender', post.toJSON(), function (post) {
res.render('single', {post: post, ghostGlobals: ghost.globalConfig, navItems: res.locals.navItems}); res.render('single', {post: post, ghostGlobals: res.locals.ghostGlobals, navItems: res.locals.navItems});
}); });
}); });
} }

View File

@ -25,7 +25,7 @@
// Allow people to overwrite the navTemplatePath // Allow people to overwrite the navTemplatePath
templatePath = templatePath || this.navTemplatePath; templatePath = templatePath || this.navTemplatePath;
return nodefn.call(fs.readFile, templatePath).then(function (navTemplateContents) { return nodefn.call(fs.readFile, templatePath).then(function(navTemplateContents) {
// TODO: Can handlebars compile async? // TODO: Can handlebars compile async?
self.navTemplateFunc = handlebars.compile(navTemplateContents.toString()); self.navTemplateFunc = handlebars.compile(navTemplateContents.toString());
}); });
@ -40,11 +40,11 @@
}; };
// A static helper method for registering with ghost // A static helper method for registering with ghost
GhostNavHelper.registerWithGhost = function (ghost) { GhostNavHelper.registerWithGhost = function(ghost) {
var templatePath = path.join(ghost.paths().frontendViews, 'nav.hbs'), var templatePath = path.join(ghost.paths().frontendViews, 'nav.hbs'),
ghostNavHelper = new GhostNavHelper(templatePath); ghostNavHelper = new GhostNavHelper(templatePath);
return ghostNavHelper.compileTemplate().then(function () { return ghostNavHelper.compileTemplate().then(function() {
ghost.registerThemeHelper("ghostNav", ghostNavHelper.renderNavItems); ghost.registerThemeHelper("ghostNav", ghostNavHelper.renderNavItems);
}); });
}; };

View File

@ -12,7 +12,11 @@
hbs = require('express-hbs'), hbs = require('express-hbs'),
_ = require('underscore'), _ = require('underscore'),
Polyglot = require('node-polyglot'), Polyglot = require('node-polyglot'),
models = require('./shared/models'),
JsonDataProvider = require('./shared/models/dataProvider.json'),
jsonDataProvider = new JsonDataProvider(),
BookshelfDataProvider = require('./shared/models/dataProvider.bookshelf'),
bookshelfDataProvider = new BookshelfDataProvider(),
ExampleFilter = require('../content/plugins/exampleFilters'), ExampleFilter = require('../content/plugins/exampleFilters'),
Ghost, Ghost,
@ -40,6 +44,7 @@
*/ */
Ghost = function () { Ghost = function () {
var app, var app,
globals,
plugin, plugin,
polyglot; polyglot;
@ -47,6 +52,11 @@
instance = this; instance = this;
plugin = new ExampleFilter(instance).init(); plugin = new ExampleFilter(instance).init();
// Temporary loading of settings
jsonDataProvider.globals.findAll(function (error, data) {
globals = data;
});
app = express(); app = express();
polyglot = new Polyglot(); polyglot = new Polyglot();
@ -58,11 +68,11 @@
_.extend(instance, { _.extend(instance, {
app: function () { return app; }, app: function () { return app; },
config: function () { return config; }, config: function () { return config; },
globalConfig: config.globals, globals: function () { return globals; }, // there's no management here to be sure this has loaded
dataProvider: models, dataProvider: function () { return bookshelfDataProvider; },
statuses: function () { return statuses; }, statuses: function () { return statuses; },
polyglot: function () { return polyglot; }, polyglot: function () { return polyglot; },
plugin: function () { return plugin; }, plugin: function() { return plugin; },
paths: function () { paths: function () {
return { return {
'activeTheme': __dirname + '/../content/' + config.themeDir + '/' + config.activeTheme + '/', 'activeTheme': __dirname + '/../content/' + config.themeDir + '/' + config.activeTheme + '/',

View File

@ -13,7 +13,6 @@
_ = require('underscore'), _ = require('underscore'),
ghost = new Ghost(), ghost = new Ghost(),
dataProvider = ghost.dataProvider,
posts, posts,
users, users,
settings, settings,
@ -24,57 +23,57 @@
// takes filter / pagination parameters // takes filter / pagination parameters
// returns a list of posts in a json response // returns a list of posts in a json response
browse: function (options) { browse: function (options) {
return dataProvider.Post.findAll(options); return ghost.dataProvider().posts.findAll(options);
}, },
// takes an identifier (id or slug?) // takes an identifier (id or slug?)
// returns a single post in a json response // returns a single post in a json response
read: function (args) { read: function (args) {
return dataProvider.Post.findOne(args); return ghost.dataProvider().posts.findOne(args);
}, },
// takes a json object with all the properties which should be updated // takes a json object with all the properties which should be updated
// returns the resulting post in a json response // returns the resulting post in a json response
edit: function (postData) { edit: function (postData) {
return dataProvider.Post.edit(postData); return ghost.dataProvider().posts.edit(postData);
}, },
// takes a json object representing a post, // takes a json object representing a post,
// returns the resulting post in a json response // returns the resulting post in a json response
add: function (postData) { add: function (postData) {
return dataProvider.Post.add(postData); return ghost.dataProvider().posts.add(postData);
}, },
// takes an identifier (id or slug?) // takes an identifier (id or slug?)
// returns a json response with the id of the deleted post // returns a json response with the id of the deleted post
destroy: function (args) { destroy: function (args) {
return dataProvider.Post.destroy(args.id); return ghost.dataProvider().posts.destroy(args.id);
} }
}; };
// # Users // # Users
users = { users = {
add: function (postData) { add: function (postData) {
return dataProvider.Users.add(postData); return ghost.dataProvider().users.add(postData);
}, },
check: function (postData) { check: function (postData) {
return dataProvider.Users.check(postData); return ghost.dataProvider().users.check(postData);
} }
}; };
// # Settings // # Settings
settings = { settings = {
browse: function (options) { browse: function (options) {
return dataProvider.Settings.browse(options); return ghost.dataProvider().settings.browse(options);
}, },
read: function (options) { read: function (options) {
return dataProvider.Settings.read(options.key); return ghost.dataProvider().settings.read(options.key);
}, },
edit: function (options) { edit: function (options) {
return dataProvider.Settings.edit(options); return ghost.dataProvider().settings.edit(options);
} }
}; };
// categories: {}; // categories: {};
// post_categories: {}; // post_categories: {};
// requestHandler // requestHandler
// decorator for api functions which are called via an HTTP request // decorator for api functions which are called via an HTTP request
// takes the API method and wraps it so that it gets data from the request and returns a sensible JSON response // takes the API method and wraps it so that it gets data from the request and returns a sensible JSON response

View File

@ -3,8 +3,9 @@
(function () { (function () {
"use strict"; "use strict";
var when = require('when'), var when = require('when'),
knex = require('../../models/base').Knex, knex = require('../../models/knex_init'),
fixtures = require('../fixtures/001'), fixtures = require('../fixtures/001'),
up, up,
down; down;

View File

@ -1,96 +0,0 @@
(function () {
"use strict";
var GhostBookshelf,
Bookshelf = require('bookshelf'),
config = require('../../../config');
// Initializes Bookshelf as its own instance, so we can modify the Models and not mess up
// others' if they're using the library outside of ghost.
GhostBookshelf = Bookshelf.Initialize('ghost', config.database[process.env.NODE_ENV || 'development']);
// The Base Model which other Ghost objects will inherit from,
// including some convenience functions as static properties on the model.
GhostBookshelf.Model = GhostBookshelf.Model.extend({
// Base prototype properties will go here
}, {
/**
* Naive find all
* @param options (optional)
*/
findAll: function (options) {
options = options || {};
return GhostBookshelf.Collection.forge([], {model: this}).fetch(options);
},
browse: function () {
return this.findAll.apply(this, arguments);
},
/**
* Naive find one where args match
* @param args
* @param options (optional)
*/
findOne: function (args, options) {
options = options || {};
return this.forge(args).fetch(options);
},
read: function () {
return this.findOne.apply(this, arguments);
},
/**
* Naive edit
* @param editedObj
* @param options (optional)
*/
edit: function (editedObj, options) {
options = options || {};
return this.forge({id: editedObj.id}).fetch(options).then(function (foundObj) {
return foundObj.set(editedObj).save();
});
},
update: function () {
return this.edit.apply(this, arguments);
},
/**
* Naive create
* @param editedObj
* @param options (optional)
*/
add: function (newObj, options) {
options = options || {};
return this.forge(newObj).save(options);
},
create: function () {
return this.add.apply(this, arguments);
},
/**
* Naive destroy
* @param _identifier
* @param options (optional)
*/
destroy: function (_identifier, options) {
options = options || {};
return this.forge({id: _identifier}).destroy(options);
},
'delete': function () {
return this.destroy.apply(this, arguments);
}
});
module.exports = GhostBookshelf;
}());

View File

@ -0,0 +1,74 @@
/*global require, module */
(function () {
"use strict";
var _ = require('underscore'),
BookshelfBase;
/**
* The base class for interacting with bookshelf models/collections.
* Provides naive implementations of CRUD/BREAD operations.
*/
BookshelfBase = function (model, collection) {
// Bind the 'this' value for all our functions since they get messed
// up by the when.call
_.bindAll(this, 'findAll', 'browse', 'findOne', 'read', 'edit', 'add', 'destroy');
this.model = model;
this.collection = collection;
};
/**
* Naive find all
* @param args (optional)
* @param opts (optional)
*/
BookshelfBase.prototype.findAll = BookshelfBase.prototype.browse = function (opts) {
opts = opts || {};
return this.collection.forge().fetch(opts);
};
/**
* Naive find one where args match
* @param args
* @param opts (optional)
*/
BookshelfBase.prototype.findOne = BookshelfBase.prototype.read = function (args, opts) {
opts = opts || {};
return this.model.forge(args).fetch(opts);
};
/**
* Naive edit
* @param editedObj
* @param opts (optional)
*/
BookshelfBase.prototype.edit = BookshelfBase.prototype.update = function (editedObj, opts) {
opts = opts || {};
return this.model.forge({id: editedObj.id}).fetch(opts).then(function (foundObj) {
return foundObj.set(editedObj).save();
});
};
/**
* Naive add
* @param newObj
* @param opts (optional)
*/
BookshelfBase.prototype.add = BookshelfBase.prototype.create = function (newObj, opts) {
opts = opts || {};
return this.model.forge(newObj).save(opts);
};
/**
* Naive destroy
* @param _identifier
* @param opts (optional)
*/
BookshelfBase.prototype.destroy = BookshelfBase.prototype['delete'] = function (_identifier, opts) {
opts = opts || {};
return this.model.forge({id: _identifier}).destroy(opts);
};
module.exports = BookshelfBase;
}());

View File

@ -0,0 +1,52 @@
/**
* Provides access to data via the Bookshelf ORM
*/
/*globals module, require, process */
(function () {
"use strict";
var _ = require('underscore'),
knex = require('./knex_init'),
PostsProvider = require('./dataProvider.bookshelf.posts'),
UsersProvider = require('./dataProvider.bookshelf.users'),
SettingsProvider = require('./dataProvider.bookshelf.settings'),
DataProvider,
instance,
defaultOptions = {
autoInit: false
};
DataProvider = function (options) {
options = _.defaults(options || {}, defaultOptions);
if (!instance) {
instance = this;
if (options.autoInit) {
this.init();
}
}
return instance;
};
DataProvider.prototype.init = function () {
return knex.Schema.hasTable('posts').then(null, function () {
// Simple bootstraping of the data model for now.
var migration = require('../data/migration/001');
return migration.down().then(function () {
return migration.up();
});
}).then(function () {
console.log('DataProvider ready');
});
};
DataProvider.prototype.posts = new PostsProvider();
DataProvider.prototype.users = new UsersProvider();
DataProvider.prototype.settings = new SettingsProvider();
module.exports = DataProvider;
}());

View File

@ -0,0 +1,91 @@
(function () {
"use strict";
var _ = require('underscore'),
util = require('util'),
models = require('./models'),
Bookshelf = require('bookshelf'),
BaseProvider = require('./dataProvider.bookshelf.base'),
PostsProvider;
/**
* The Posts data provider implementation for Bookshelf.
*/
PostsProvider = function () {
BaseProvider.call(this, models.Post, models.Posts);
};
util.inherits(PostsProvider, BaseProvider);
/**
* Find results by page - returns an object containing the
* information about the request (page, limit), along with the
* info needed for pagination (pages, total).
*
* {
* posts: [
* {...}, {...}, {...}
* ],
* page: __,
* limit: __,
* pages: __,
* total: __
* }
*
* @params opts
*/
PostsProvider.prototype.findPage = function (opts) {
var postCollection;
// Allow findPage(n)
if (!_.isObject(opts)) {
opts = {page: opts};
}
opts = _.defaults(opts || {}, {
page: 1,
limit: 15,
where: null
});
postCollection = this.collection.forge();
// If there are where conditionals specified, add those
// to the query.
if (opts.where) {
postCollection.query('where', opts.where);
}
// Set the limit & offset for the query, fetching
// with the opts (to specify any eager relations, etc.)
// Omitting the `page`, `limit`, `where` just to be sure
// aren't used for other purposes.
return postCollection
.query('limit', opts.limit)
.query('offset', opts.limit * (opts.page - 1))
.fetch(_.omit(opts, 'page', 'limit', 'where'))
.then(function (collection) {
var qb;
// After we're done, we need to figure out what
// the limits are for the pagination values.
qb = Bookshelf.Knex(_.result(collection, 'tableName'));
if (opts.where) {
qb.where(opts.where);
}
return qb.count(_.result(collection, 'idAttribute')).then(function (resp) {
var totalPosts = resp[0].aggregate;
return {
posts: collection.toJSON(),
page: opts.page,
limit: opts.limit,
pages: Math.ceil(totalPosts / opts.limit),
total: totalPosts
};
});
});
};
module.exports = PostsProvider;
}());

View File

@ -0,0 +1,37 @@
(function () {
"use strict";
var _ = require('underscore'),
when = require('when'),
util = require('util'),
models = require('./models'),
BaseProvider = require('./dataProvider.bookshelf.base'),
SettingsProvider;
/**
* The Posts data provider implementation for Bookshelf.
*/
SettingsProvider = function () {
BaseProvider.call(this, models.Setting, models.Settings);
};
util.inherits(SettingsProvider, BaseProvider);
SettingsProvider.prototype.read = function (_key) {
// Allow for just passing the key instead of attributes
if (_.isString(_key)) {
_key = { key: _key };
}
return BaseProvider.prototype.read.call(this, _key);
};
SettingsProvider.prototype.edit = function (_data) {
return when.all(_.map(_data, function (value, key) {
return this.model.forge({ key: key }).fetch().then(function (setting) {
return setting.set('value', value).save();
});
}, this));
};
module.exports = SettingsProvider;
}());

View File

@ -0,0 +1,69 @@
(function () {
"use strict";
var util = require('util'),
_ = require('underscore'),
bcrypt = require('bcrypt-nodejs'),
models = require('./models.js'),
when = require('when'),
nodefn = require('when/node/function'),
BaseProvider = require('./dataProvider.bookshelf.base.js'),
UsersProvider;
/**
* The Users data provider implementation for Bookshelf.
*/
UsersProvider = function () {
BaseProvider.call(this, models.User, models.Users);
};
util.inherits(UsersProvider, BaseProvider);
/**
* Naive user add
* @param _user
*
* Hashes the password provided before saving to the database.
*/
UsersProvider.prototype.add = function (_user) {
var self = this,
// Clone the _user so we don't expose the hashed password unnecessarily
userData = _.extend({}, _user);
return self.model.forge({email_address: userData.email_address}).fetch().then(function (user) {
if (!!user.attributes.email_address) {
return when.reject(new Error('A user with that email address already exists.'));
}
return nodefn.call(bcrypt.hash, _user.password, null, null).then(function (hash) {
userData.password = hash;
return BaseProvider.prototype.add.call(self, userData);
});
});
};
/**
* User check
* @param _userdata
*
* Finds the user by email, and check's the password
*/
UsersProvider.prototype.check = function (_userdata) {
return this.model.forge({
email_address: _userdata.email
}).fetch().then(function (user) {
if (!!user.attributes.email_address) {
return nodefn.call(bcrypt.compare, _userdata.pw, user.get('password')).then(function (matched) {
if (!matched) {
return when.reject(new Error('Passwords do not match'));
}
return user;
});
}
return when.reject(new Error('We do not have a record for such user.'));
});
};
module.exports = UsersProvider;
}());

View File

@ -0,0 +1,51 @@
/**
* Dummy dataProvider returns hardcoded JSON data until we finish migrating settings data to a datastore
*/
/*globals module, require */
(function () {
"use strict";
var _ = require('underscore'),
when = require('when'),
DataProvider,
blogData,
instance,
d;
blogData = {
url: 'http://localhost:3333', //'http://john.onolan.org',
title: "John O'Nolan",
description: "Interactive designer, public speaker, startup advisor and writer. Living in Austria, attempting world domination via keyboard."
};
DataProvider = function () {
if (!instance) {
instance = this;
}
return instance;
};
DataProvider.prototype.globals = {};
DataProvider.prototype.globals.data = [];
DataProvider.prototype.globals.findAll = function () {
return when(this.data);
};
DataProvider.prototype.globals.save = function (globals) {
_.each(globals, function (global, key) {
this.data[key] = global;
}, this);
return when(globals);
};
/* Lets bootstrap with dummy data */
d = new DataProvider();
d.globals.save(blogData, function (error) {
if (error) { throw error; }
});
module.exports = DataProvider;
}());

View File

@ -1,26 +0,0 @@
/*global require, module */
(function () {
"use strict";
var GhostBookshelf = require('./base'),
knex = GhostBookshelf.Knex;
module.exports = {
Post: require('./post').Post,
User: require('./user').User,
Setting: require('./setting').Setting,
init: function () {
return knex.Schema.hasTable('posts').then(null, function () {
// Simple bootstraping of the data model for now.
var migration = require('../data/migration/001');
return migration.down().then(function () {
return migration.up();
});
}).then(function () {
console.log('models loaded');
});
}
};
}());

View File

@ -0,0 +1,11 @@
/*global require, module, process */
(function () {
"use strict";
var knex = require('knex'),
config = require('../../../config');
knex.Initialize(config.database[process.env.NODE_ENV || 'development']);
module.exports = knex;
}());

View File

@ -0,0 +1,95 @@
/*global require, module */
(function () {
"use strict";
// We should just be able to require bookshelf and have it reference
// the `Knex` instance bootstraped at the app initialization.
var Bookshelf = require('bookshelf'),
Showdown = require('showdown'),
converter = new Showdown.converter(),
Post,
Posts,
User,
Users,
Setting,
Settings;
Post = Bookshelf.Model.extend({
tableName: 'posts',
hasTimestamps: true,
initialize: function () {
this.on('creating', this.creating, this);
this.on('saving', this.saving, this);
},
saving: function () {
if (!this.get('title')) {
throw new Error('Post title cannot be blank');
}
this.set('content_html', converter.makeHtml(this.get('content')));
// refactoring of ghost required in order to make these details available here
// this.set('language', this.get('language') || ghost.config().defaultLang);
// this.set('status', this.get('status') || ghost.statuses().draft);
},
creating: function () {
if (!this.get('slug')) {
this.generateSlug();
}
},
generateSlug: function () {
return this.set('slug', this.get('title').replace(/\:/g, '').replace(/\s/g, '-').toLowerCase());
},
user: function () {
return this.belongsTo(User, 'created_by');
}
});
Posts = Bookshelf.Collection.extend({
model: Post
});
User = Bookshelf.Model.extend({
tableName: 'users',
hasTimestamps: true,
posts: function () {
return this.hasMany(Posts, 'created_by');
}
});
Users = Bookshelf.Collection.extend({
model: User
});
Setting = Bookshelf.Model.extend({
tableName: 'settings',
hasTimestamps: true
});
Settings = Bookshelf.Collection.extend({
model: Setting
});
module.exports = {
Post: Post,
Posts: Posts,
User: User,
Users: Users,
Setting: Setting,
Settings: Settings
};
}());

View File

@ -1,134 +0,0 @@
(function () {
"use strict";
var Post,
Posts,
_ = require('underscore'),
Showdown = require('showdown'),
converter = new Showdown.converter(),
User = require('./user').User,
GhostBookshelf = require('./base');
Post = GhostBookshelf.Model.extend({
tableName: 'posts',
hasTimestamps: true,
initialize: function () {
this.on('creating', this.creating, this);
this.on('saving', this.saving, this);
},
saving: function () {
if (!this.get('title')) {
throw new Error('Post title cannot be blank');
}
this.set('content_html', converter.makeHtml(this.get('content')));
// refactoring of ghost required in order to make these details available here
// this.set('language', this.get('language') || ghost.config().defaultLang);
// this.set('status', this.get('status') || ghost.statuses().draft);
},
creating: function () {
if (!this.get('slug')) {
this.generateSlug();
}
},
generateSlug: function () {
return this.set('slug', this.get('title').replace(/\:/g, '').replace(/\s/g, '-').toLowerCase());
},
user: function () {
return this.belongsTo(User, 'created_by');
}
}, {
/**
* Find results by page - returns an object containing the
* information about the request (page, limit), along with the
* info needed for pagination (pages, total).
*
* {
* posts: [
* {...}, {...}, {...}
* ],
* page: __,
* limit: __,
* pages: __,
* total: __
* }
*
* @params opts
*/
findPage: function (opts) {
var postCollection;
// Allow findPage(n)
if (!_.isObject(opts)) {
opts = {page: opts};
}
opts = _.defaults(opts || {}, {
page: 1,
limit: 15,
where: null
});
postCollection = Posts.forge();
// If there are where conditionals specified, add those
// to the query.
if (opts.where) {
postCollection.query('where', opts.where);
}
// Set the limit & offset for the query, fetching
// with the opts (to specify any eager relations, etc.)
// Omitting the `page`, `limit`, `where` just to be sure
// aren't used for other purposes.
return postCollection
.query('limit', opts.limit)
.query('offset', opts.limit * (opts.page - 1))
.fetch(_.omit(opts, 'page', 'limit', 'where'))
.then(function (collection) {
var qb;
// After we're done, we need to figure out what
// the limits are for the pagination values.
qb = GhostBookshelf.Knex(_.result(collection, 'tableName'));
if (opts.where) {
qb.where(opts.where);
}
return qb.count(_.result(collection, 'idAttribute')).then(function (resp) {
var totalPosts = resp[0].aggregate;
return {
posts: collection.toJSON(),
page: opts.page,
limit: opts.limit,
pages: Math.ceil(totalPosts / opts.limit),
total: totalPosts
};
});
});
}
});
Posts = GhostBookshelf.Collection.extend({
model: Post
});
module.exports = {
Post: Post,
Posts: Posts
};
}());

View File

@ -1,45 +0,0 @@
(function () {
"use strict";
var Setting,
Settings,
GhostBookshelf = require('./base'),
_ = require('underscore'),
when = require('when');
Setting = GhostBookshelf.Model.extend({
tableName: 'settings',
hasTimestamps: true
}, {
read: function (_key) {
// Allow for just passing the key instead of attributes
if (!_.isObject(_key)) {
_key = { key: _key };
}
return GhostBookshelf.Model.read.call(this, _key);
},
edit: function (_data) {
return when.all(_.map(_data, function (value, key) {
return this.forge({ key: key }).fetch().then(function (setting) {
return setting.set('value', value).save();
});
}, this));
}
});
Settings = GhostBookshelf.Collection.extend({
model: Setting
});
module.exports = {
Setting: Setting,
Settings: Settings
};
}());

View File

@ -1,72 +0,0 @@
(function () {
"use strict";
var User,
Users,
_ = require('underscore'),
when = require('when'),
nodefn = require('when/node/function'),
bcrypt = require('bcrypt-nodejs'),
Posts = require('./post').Posts,
GhostBookshelf = require('./base');
User = GhostBookshelf.Model.extend({
tableName: 'users',
hasTimestamps: true,
posts: function () {
return this.hasMany(Posts, 'created_by');
}
}, {
/**
* Naive user add
* @param _user
*
* Hashes the password provided before saving to the database.
*/
add: function (_user) {
var User = this,
// Clone the _user so we don't expose the hashed password unnecessarily
userData = _.extend({}, _user);
return nodefn.call(bcrypt.hash, _user.password, null, null).then(function (hash) {
userData.password = hash;
return GhostBookshelf.Model.add.call(User, userData);
});
},
/**
* User check
* @param _userdata
*
* Finds the user by email, and check's the password
*/
check: function (_userdata) {
return this.model.forge({
email_address: _userdata.email
}).fetch().then(function (user) {
return nodefn.call(bcrypt.compare, _userdata.pw, user.get('password')).then(function (matched) {
if (!matched) {
return when.reject(new Error('Password does not match'));
}
return user;
});
});
}
});
Users = GhostBookshelf.Collection.extend({
model: User
});
module.exports = {
User: User,
Users: Users
};
}());

View File

@ -6,20 +6,21 @@
var _ = require("underscore"), var _ = require("underscore"),
should = require('should'), should = require('should'),
helpers = require('./helpers'), helpers = require('./helpers'),
Models = require('../../shared/models'); PostProvider = require('../../shared/models/dataProvider.bookshelf.posts');
describe('Bookshelf Post Model', function () { describe('Bookshelf PostsProvider', function () {
var PostModel = Models.Post; var posts;
beforeEach(function (done) { beforeEach(function (done) {
helpers.resetData().then(function () { helpers.resetData().then(function () {
posts = new PostProvider();
done(); done();
}, done); }, done);
}); });
it('can browse', function (done) { it('can browse', function (done) {
PostModel.browse().then(function (results) { posts.browse().then(function (results) {
should.exist(results); should.exist(results);
results.length.should.equal(2); results.length.should.equal(2);
@ -31,14 +32,14 @@
it('can read', function (done) { it('can read', function (done) {
var firstPost; var firstPost;
PostModel.browse().then(function (results) { posts.browse().then(function (results) {
should.exist(results); should.exist(results);
results.length.should.be.above(0); results.length.should.be.above(0);
firstPost = results.models[0]; firstPost = results.models[0];
return PostModel.read({slug: firstPost.attributes.slug}); return posts.read({slug: firstPost.attributes.slug});
}).then(function (found) { }).then(function (found) {
should.exist(found); should.exist(found);
@ -51,7 +52,7 @@
it('can edit', function (done) { it('can edit', function (done) {
var firstPost; var firstPost;
PostModel.browse().then(function (results) { posts.browse().then(function (results) {
should.exist(results); should.exist(results);
@ -59,7 +60,7 @@
firstPost = results.models[0]; firstPost = results.models[0];
return PostModel.edit({id: firstPost.id, title: "new title"}); return posts.edit({id: firstPost.id, title: "new title"});
}).then(function (edited) { }).then(function (edited) {
@ -78,7 +79,7 @@
content: 'Test Content 1' content: 'Test Content 1'
}; };
PostModel.add(newPost).then(function (createdPost) { posts.add(newPost).then(function (createdPost) {
should.exist(createdPost); should.exist(createdPost);
createdPost.attributes.title.should.equal(newPost.title, "title is correct"); createdPost.attributes.title.should.equal(newPost.title, "title is correct");
@ -91,7 +92,7 @@
it('can delete', function (done) { it('can delete', function (done) {
var firstPostId; var firstPostId;
PostModel.browse().then(function (results) { posts.browse().then(function (results) {
should.exist(results); should.exist(results);
@ -99,11 +100,11 @@
firstPostId = results.models[0].id; firstPostId = results.models[0].id;
return PostModel.destroy(firstPostId); return posts.destroy(firstPostId);
}).then(function () { }).then(function () {
return PostModel.browse(); return posts.browse();
}).then(function (newResults) { }).then(function (newResults) {
var ids, hasDeletedId; var ids, hasDeletedId;
@ -125,7 +126,7 @@
helpers.insertMorePosts().then(function () { helpers.insertMorePosts().then(function () {
return PostModel.findPage({page: 2}); return posts.findPage({page: 2});
}).then(function (paginationResult) { }).then(function (paginationResult) {
@ -137,7 +138,7 @@
paginationResult.pages.should.equal(4); paginationResult.pages.should.equal(4);
return PostModel.findPage({page: 5}); return posts.findPage({page: 5});
}).then(function (paginationResult) { }).then(function (paginationResult) {
@ -149,7 +150,7 @@
paginationResult.pages.should.equal(4); paginationResult.pages.should.equal(4);
return PostModel.findPage({limit: 30}); return posts.findPage({limit: 30});
}).then(function (paginationResult) { }).then(function (paginationResult) {
@ -161,7 +162,7 @@
paginationResult.pages.should.equal(2); paginationResult.pages.should.equal(2);
return PostModel.findPage({limit: 10, page: 2, where: {language: 'fr'}}); return posts.findPage({limit: 10, page: 2, where: {language: 'fr'}});
}).then(function (paginationResult) { }).then(function (paginationResult) {

View File

@ -6,20 +6,21 @@
var _ = require("underscore"), var _ = require("underscore"),
should = require('should'), should = require('should'),
helpers = require('./helpers'), helpers = require('./helpers'),
Models = require('../../shared/models'); SettingProvider = require('../../shared/models/dataProvider.bookshelf.settings');
describe('Bookshelf Setting Model', function () { describe('Bookshelf SettingsProvider', function () {
var SettingModel = Models.Setting; var settings;
beforeEach(function (done) { beforeEach(function (done) {
helpers.resetData().then(function () { helpers.resetData().then(function () {
settings = new SettingProvider();
done(); done();
}, done); });
}); });
it('can browse', function (done) { it('can browse', function (done) {
SettingModel.browse().then(function (results) { settings.browse().then(function (results) {
should.exist(results); should.exist(results);
@ -32,7 +33,7 @@
it('can read', function (done) { it('can read', function (done) {
var firstSetting; var firstSetting;
SettingModel.browse().then(function (results) { settings.browse().then(function (results) {
should.exist(results); should.exist(results);
@ -40,7 +41,7 @@
firstSetting = results.models[0]; firstSetting = results.models[0];
return SettingModel.read(firstSetting.attributes.key); return settings.read(firstSetting.attributes.key);
}).then(function (found) { }).then(function (found) {
@ -57,7 +58,7 @@
var firstPost, var firstPost,
toEdit = {}; toEdit = {};
SettingModel.browse().then(function (results) { settings.browse().then(function (results) {
should.exist(results); should.exist(results);
@ -69,7 +70,7 @@
// key/value pairs // key/value pairs
toEdit[firstPost.attributes.key] = "new value"; toEdit[firstPost.attributes.key] = "new value";
return SettingModel.edit(toEdit); return settings.edit(toEdit);
}).then(function (edited) { }).then(function (edited) {
@ -93,7 +94,7 @@
editedPost, editedPost,
toEdit = {}; toEdit = {};
SettingModel.browse().then(function (results) { settings.browse().then(function (results) {
should.exist(results); should.exist(results);
@ -107,7 +108,7 @@
toEdit[firstPost.attributes.key] = "new value1"; toEdit[firstPost.attributes.key] = "new value1";
toEdit[secondPost.attributes.key] = "new value2"; toEdit[secondPost.attributes.key] = "new value2";
return SettingModel.edit(toEdit); return settings.edit(toEdit);
}).then(function (edited) { }).then(function (edited) {
@ -136,7 +137,7 @@
value: 'Test Content 1' value: 'Test Content 1'
}; };
SettingModel.add(newSetting).then(function (createdSetting) { settings.add(newSetting).then(function (createdSetting) {
should.exist(createdSetting); should.exist(createdSetting);
@ -150,7 +151,7 @@
it('can delete', function (done) { it('can delete', function (done) {
var firstSettingId; var firstSettingId;
SettingModel.browse().then(function (results) { settings.browse().then(function (results) {
should.exist(results); should.exist(results);
@ -158,11 +159,11 @@
firstSettingId = results.models[0].id; firstSettingId = results.models[0].id;
return SettingModel.destroy(firstSettingId); return settings.destroy(firstSettingId);
}).then(function () { }).then(function () {
return SettingModel.browse(); return settings.browse();
}).then(function (newResults) { }).then(function (newResults) {

View File

@ -6,21 +6,22 @@
var _ = require('underscore'), var _ = require('underscore'),
should = require('should'), should = require('should'),
helpers = require('./helpers'), helpers = require('./helpers'),
Models = require('../../shared/models'); UserProvider = require('../../shared/models/dataProvider.bookshelf.users');
describe('Bookshelf User Model', function () { describe('Bookshelf UsersProvider', function () {
var UserModel = Models.User; var users;
beforeEach(function (done) { beforeEach(function (done) {
helpers.resetData().then(function () { helpers.resetData().then(function () {
users = new UserProvider();
done(); done();
}, done); });
}); });
it('can browse', function (done) { it('can browse', function (done) {
UserModel.browse().then(function (results) { users.browse().then(function (results) {
should.exist(results); should.exist(results);
@ -34,7 +35,7 @@
it('can read', function (done) { it('can read', function (done) {
var firstUser; var firstUser;
UserModel.browse().then(function (results) { users.browse().then(function (results) {
should.exist(results); should.exist(results);
@ -42,7 +43,7 @@
firstUser = results.models[0]; firstUser = results.models[0];
return UserModel.read({email_address: firstUser.attributes.email_address}); return users.read({email_address: firstUser.attributes.email_address});
}).then(function (found) { }).then(function (found) {
@ -59,7 +60,7 @@
it('can edit', function (done) { it('can edit', function (done) {
var firstUser; var firstUser;
UserModel.browse().then(function (results) { users.browse().then(function (results) {
should.exist(results); should.exist(results);
@ -67,7 +68,7 @@
firstUser = results.models[0]; firstUser = results.models[0];
return UserModel.edit({id: firstUser.id, url: "some.newurl.com"}); return users.edit({id: firstUser.id, url: "some.newurl.com"});
}).then(function (edited) { }).then(function (edited) {
@ -86,7 +87,7 @@
email_address: "test@test1.com" email_address: "test@test1.com"
}; };
UserModel.add(userData).then(function (createdUser) { users.add(userData).then(function (createdUser) {
should.exist(createdUser); should.exist(createdUser);
@ -100,7 +101,7 @@
it('can delete', function (done) { it('can delete', function (done) {
var firstUserId; var firstUserId;
UserModel.browse().then(function (results) { users.browse().then(function (results) {
should.exist(results); should.exist(results);
@ -108,11 +109,11 @@
firstUserId = results.models[0].id; firstUserId = results.models[0].id;
return UserModel.destroy(firstUserId); return users.destroy(firstUserId);
}).then(function () { }).then(function () {
return UserModel.browse(); return users.browse();
}).then(function (newResults) { }).then(function (newResults) {
var ids, hasDeletedId; var ids, hasDeletedId;

View File

@ -0,0 +1,20 @@
/*globals describe, beforeEach, it*/
(function () {
"use strict";
var should = require('should'),
DataProvider = require('../../shared/models/dataProvider.json');
describe("dataProvider.json", function () {
it("is a singleton", function () {
var provider1 = new DataProvider(),
provider2 = new DataProvider();
should.strictEqual(provider1, provider2);
});
});
}());

View File

@ -4,7 +4,7 @@
// Use 'testing' Ghost config; unless we are running on travis (then show queries for debugging) // Use 'testing' Ghost config; unless we are running on travis (then show queries for debugging)
process.env.NODE_ENV = process.env.TRAVIS ? 'travis' : 'testing'; process.env.NODE_ENV = process.env.TRAVIS ? 'travis' : 'testing';
var knex = require('../../shared/models/base').Knex, var knex = require('knex'),
migrations = { migrations = {
one: require("../../shared/data/migration/001") one: require("../../shared/data/migration/001")
}, },