From c5c274f67bebc754e3f04c341050c831e8ca3940 Mon Sep 17 00:00:00 2001 From: Camille Reynders Date: Sat, 14 Mar 2015 10:39:55 +0100 Subject: [PATCH] Extract prepost functionality to separate module --- fields/types/azurefile/AzureFileType.js | 29 ++------- fields/types/localfile/LocalFileType.js | 54 +++------------- fields/types/localfiles/LocalFilesType.js | 55 +++------------- fields/types/s3file/S3FileType.js | 30 ++------- index.js | 30 ++------- lib/core/mount.js | 5 +- lib/prepost.js | 77 +++++++++++++++++++++++ lib/view.js | 3 +- 8 files changed, 111 insertions(+), 172 deletions(-) create mode 100644 lib/prepost.js diff --git a/fields/types/azurefile/AzureFileType.js b/fields/types/azurefile/AzureFileType.js index 71d0cc1baf..d3a40271ba 100644 --- a/fields/types/azurefile/AzureFileType.js +++ b/fields/types/azurefile/AzureFileType.js @@ -5,10 +5,10 @@ var _ = require('underscore'), moment = require('moment'), keystone = require('../../../'), - async = require('async'), util = require('util'), azure = require('azure'), utils = require('keystone-utils'), + prepost = require("../../../lib/prepost"), super_ = require('../Type'); @@ -19,15 +19,12 @@ var _ = require('underscore'), */ function azurefile(list, path, options) { + prepost.mixin(this) + .register("pre:upload"); this._underscoreMethods = ['format', 'uploadFile']; this._fixedSize = 'full'; - // event queues - this._pre = { - upload: [] - }; - // TODO: implement filtering, usage disabled for now options.nofilter = true; @@ -56,7 +53,7 @@ function azurefile(list, path, options) { // Could be more pre- hooks, just upload for now if (options.pre && options.pre.upload) { - this._pre.upload = this._pre.upload.concat(options.pre.upload); + this.pre("upload", options.pre.upload); } } @@ -76,22 +73,6 @@ Object.defineProperty(azurefile.prototype, 'azurefileconfig', { get: function() }}); -/** - * Allows you to add pre middleware after the field has been initialised - * - * @api public - */ - -azurefile.prototype.pre = function(event, fn) { - if (!this._pre[event]) { - throw new Error('AzureFile (' + this.list.key + '.' + this.path + ') error: azurefile.pre()\n\n' + - 'Event ' + event + ' is not supported.\n'); - } - this._pre[event].push(fn); - return this; -}; - - /** * Registers the field on the List's Mongoose Schema. * @@ -287,7 +268,7 @@ azurefile.prototype.uploadFile = function(item, file, update, callback) { }); }; - async.eachSeries(this._pre.upload, function(fn, next) { + this.hooks("pre:upload", function(fn, next) { fn(item, file, next); }, function(err) { if (err) return callback(err); diff --git a/fields/types/localfile/LocalFileType.js b/fields/types/localfile/LocalFileType.js index 0ab2994018..88fe1ffe0d 100644 --- a/fields/types/localfile/LocalFileType.js +++ b/fields/types/localfile/LocalFileType.js @@ -6,7 +6,7 @@ var fs = require('fs-extra'), path = require('path'), _ = require('underscore'), moment = require('moment'), - async = require('async'), + prepost = require('../../../lib/prepost'), util = require('util'), utils = require('keystone-utils'), super_ = require('../Type'); @@ -18,19 +18,11 @@ var fs = require('fs-extra'), */ function localfile(list, path, options) { - + prepost.mixin(this) + .register("pre:move", "post:move"); this._underscoreMethods = ['format', 'uploadFile']; this._fixedSize = 'full'; - // event queues - this._pre = { - move: [] // Before file is moved into final destination - }; - - this._post = { - move: [] // After file is moved into final destination - }; - // TODO: implement filtering, usage disabled for now options.nofilter = true; @@ -51,14 +43,13 @@ function localfile(list, path, options) { throw new Error('Invalid Configuration\n\n' + 'localfile fields (' + list.key + '.' + path + ') require the "dest" option to be set.'); } - // Allow hook into before and after if (options.pre && options.pre.move) { - this._pre.move = this._pre.move.concat(options.pre.move); + this.pre("move", options.pre.move); } if (options.post && options.post.move) { - this._post.move = this._post.move.concat(options.post.move); + this.post("move", options.post.move); } } @@ -70,37 +61,6 @@ function localfile(list, path, options) { util.inherits(localfile, super_); -/** - * Allows you to add pre middleware after the field has been initialised - * - * @api public - */ - -localfile.prototype.pre = function(event, fn) { - if (!this._pre[event]) { - throw new Error('localfile (' + this.list.key + '.' + this.path + ') error: localfile.pre()\n\n' + - 'Event ' + event + ' is not supported.\n'); - } - this._pre[event].push(fn); - return this; -}; - - -/** - * Allows you to add post middleware after the field has been initialised - * - * @api public - */ - -localfile.prototype.post = function(event, fn) { - if (!this._post[event]) { - throw new Error('localfile (' + this.list.key + '.' + this.path + ') error: localfile.post()\n\n' + - 'Event ' + event + ' is not supported.\n'); - } - this._post[event].push(fn); - return this; -}; - /** * Registers the field on the List's Mongoose Schema. @@ -331,7 +291,7 @@ localfile.prototype.uploadFile = function(item, file, update, callback) { }); }; - async.eachSeries(this._pre.move, function(fn, next) { + field.hooks("pre:move", function(fn, next) { fn(item, file, next); }, function(err) { @@ -340,7 +300,7 @@ localfile.prototype.uploadFile = function(item, file, update, callback) { doMove(function(err, fileData) { if (err) return callback(err); - async.eachSeries(field._post.move, function(fn, next) { + field.hooks("post:move", function(fn, next) { fn(item, file, fileData, next); }, function(err) { if (err) return callback(err); diff --git a/fields/types/localfiles/LocalFilesType.js b/fields/types/localfiles/LocalFilesType.js index 14bef87c1f..c80a98adb8 100644 --- a/fields/types/localfiles/LocalFilesType.js +++ b/fields/types/localfiles/LocalFilesType.js @@ -10,7 +10,8 @@ var fs = require('fs-extra'), util = require('util'), utils = require('keystone-utils'), super_ = require('../Type'), - async = require('async'); + async = require('async'), + prepost = require('../../../lib/prepost'); /** * localfiles FieldType Constructor @@ -19,19 +20,11 @@ var fs = require('fs-extra'), */ function localfiles(list, path, options) { - + prepost.mixin(this) + .register("pre:move", "post:move"); this._underscoreMethods = ['format', 'uploadFiles']; this._fixedSize = 'full'; - // event queues - this._pre = { - move: [] // Before file is moved into final destination - }; - - this._post = { - move: [] // After file is moved into final destination - }; - // TODO: implement filtering, usage disabled for now options.nofilter = true; @@ -55,11 +48,11 @@ function localfiles(list, path, options) { // Allow hook into before and after if (options.pre && options.pre.move) { - this._pre.move = this._pre.move.concat(options.pre.move); + this.pre("move", options.pre.move); } if (options.post && options.post.move) { - this._post.move = this._post.move.concat(options.post.move); + this.post("move", options.post.move); } } @@ -71,38 +64,6 @@ function localfiles(list, path, options) { util.inherits(localfiles, super_); -/** - * Allows you to add pre middleware after the field has been initialised - * - * @api public - */ - -localfiles.prototype.pre = function(event, fn) { - if (!this._pre[event]) { - throw new Error('localfiles (' + this.list.key + '.' + this.path + ') error: localfiles.pre()\n\n' + - 'Event ' + event + ' is not supported.\n'); - } - this._pre[event].push(fn); - return this; -}; - - -/** - * Allows you to add post middleware after the field has been initialised - * - * @api public - */ - -localfiles.prototype.post = function(event, fn) { - if (!this._post[event]) { - throw new Error('localfiles (' + this.list.key + '.' + this.path + ') error: localfiles.post()\n\n' + - 'Event ' + event + ' is not supported.\n'); - } - this._post[event].push(fn); - return this; -}; - - /** * Registers the field on the List's Mongoose Schema. * @@ -354,7 +315,7 @@ localfiles.prototype.uploadFiles = function(item, files, update, callback) { }; - async.eachSeries(field._pre.move, function(fn, next) { + field.hooks("pre:move", function(fn, next) { fn(item, file, next); }, function(err) { if (err) return processedFile(err); @@ -362,7 +323,7 @@ localfiles.prototype.uploadFiles = function(item, files, update, callback) { doMove(function(err, fileData) { if (err) return processedFile(err); - async.eachSeries(field._post.move, function(fn, next) { + field.hooks("post:move", function(fn, next) { fn(item, file, fileData, next); }, function(err) { return processedFile(err, fileData); diff --git a/fields/types/s3file/S3FileType.js b/fields/types/s3file/S3FileType.js index 0ea230b2ac..bc1c2f6afb 100644 --- a/fields/types/s3file/S3FileType.js +++ b/fields/types/s3file/S3FileType.js @@ -5,11 +5,11 @@ var _ = require('underscore'), moment = require('moment'), keystone = require('../../../'), - async = require('async'), util = require('util'), knox = require('knox'), // s3 = require('s3'), utils = require('keystone-utils'), + prepost = require('../../../lib/prepost'), super_ = require('../Type'); /** @@ -19,15 +19,11 @@ var _ = require('underscore'), */ function s3file(list, path, options) { - + prepost.mixin(this) + .register("pre:upload"); this._underscoreMethods = ['format', 'uploadFile']; this._fixedSize = 'full'; - // event queues - this._pre = { - upload: [] - }; - // TODO: implement filtering, usage disabled for now options.nofilter = true; @@ -48,7 +44,7 @@ function s3file(list, path, options) { // Could be more pre- hooks, just upload for now if (options.pre && options.pre.upload) { - this._pre.upload = this._pre.upload.concat(options.pre.upload); + this.pre("upload", options.pre.upload); } } @@ -68,22 +64,6 @@ Object.defineProperty(s3file.prototype, 's3config', { get: function() { }}); -/** - * Allows you to add pre middleware after the field has been initialised - * - * @api public - */ - -s3file.prototype.pre = function(event, fn) { - if (!this._pre[event]) { - throw new Error('S3File (' + this.list.key + '.' + this.path + ') error: s3field.pre()\n\n' + - 'Event ' + event + ' is not supported.\n'); - } - this._pre[event].push(fn); - return this; -}; - - /** * Registers the field on the List's Mongoose Schema. * @@ -298,7 +278,7 @@ s3file.prototype.uploadFile = function(item, file, update, callback) { }); }; - async.eachSeries(this._pre.upload, function(fn, next) { + this.hooks("pre:upload", function(fn, next) { fn(item, file, next); }, function(err) { if (err) return callback(err); diff --git a/index.js b/index.js index 55947c5a7c..3329c070ee 100644 --- a/index.js +++ b/index.js @@ -7,7 +7,8 @@ var fs = require('fs'), moment = require('moment'), numeral = require('numeral'), cloudinary = require('cloudinary'), - utils = require('keystone-utils'); + utils = require('keystone-utils'), + prepost = require('./lib/prepost'); var templateCache = {}; @@ -31,7 +32,8 @@ var moduleRoot = (function(_rootPath) { */ var Keystone = function() { - + prepost.mixin(this) + .register("pre:routes", "pre:render"); this.lists = {}; this.paths = {}; this._options = { @@ -45,10 +47,6 @@ var Keystone = function() { 'module root': moduleRoot, 'frame guard': 'sameorigin' }; - this._pre = { - routes: [], - render: [] - }; this._redirects = {}; // expose express @@ -108,26 +106,6 @@ var Keystone = function() { _.extend(Keystone.prototype, require('./lib/core/options')()); -/** - * Registers a pre-event handler. - * - * Valid events include: - * - `routes` - calls the function before any routes are matched, after all other middleware - * - * @param {String} event - * @param {Function} function to call - * @api public - */ - -Keystone.prototype.pre = function(event, fn) { - if (!this._pre[event]) { - throw new Error('keystone.pre() Error: event ' + event + ' does not exist.'); - } - this._pre[event].push(fn); - return this; -}; - - Keystone.prototype.prefixModel = function (key) { var modelPrefix = this.get('model prefix'); diff --git a/lib/core/mount.js b/lib/core/mount.js index a4e4d2f00f..415341c861 100644 --- a/lib/core/mount.js +++ b/lib/core/mount.js @@ -59,13 +59,13 @@ var _ = require('underscore'), multer = require('multer'), bodyParser = require('body-parser'), cookieParser = require('cookie-parser'), + prepost = require('../prepost'), compression = require('compression'); var dashes = '\n------------------------------------------------\n'; function mount(mountPath, parentApp, events) { debug('mounting'); - // Validate the express app instance if (!this.app) { @@ -416,7 +416,7 @@ function mount(mountPath, parentApp, events) { // Pre-route middleware - this._pre.routes.forEach(function(fn) { + this.hooks("pre:routes", function(fn, next) { try { debug('adding pre-route middlewares'); app.use(fn); @@ -427,6 +427,7 @@ function mount(mountPath, parentApp, events) { } throw e; } + next(); }); // Headless mode means don't bind the Keystone routes diff --git a/lib/prepost.js b/lib/prepost.js new file mode 100644 index 0000000000..fe8f67cd5f --- /dev/null +++ b/lib/prepost.js @@ -0,0 +1,77 @@ +"use strict"; + +var _ = require( "underscore" ), + async = require("async"); + +function init(){ + this.__prepost = { + pre : {}, + post : {} + }; +} + +function addEventListener(context, type, args){ + var event = args.shift(), + fn = _.flatten(args); + if( !context.__prepost[type][event] ){ + throw new Error('Event ' + event + ' is not supported.'); + } + var handlers = context.__prepost[type]; + handlers[event] = handlers[event].concat(fn); +} +var methods = { + /** + * Registers a pre-event handler. + * + * Valid events include: + * - `routes` - calls the function before any routes are matched, after all other middleware + * + * @param {String} event + * @param {Function} fn function to call + * @api public + */ + + pre: function( event, + fn ){ + addEventListener(this, "pre", _.toArray(arguments)); + return this; + }, + post: function( event, + fn ){ + addEventListener(this, "post", _.toArray(arguments)); + return this; + }, + register : function( types ){ + var args = _.flatten(_.toArray(arguments)); + _.each(args, function(event){ + var temp = event.split(":"), + type = temp[0], + eventName = temp[1]; + if( !this.__prepost[ type ] ){ + throw new Error( "Only 'pre' and 'post' types are allowed, not '"+type+"'" ); + } + this.__prepost[type][eventName] = []; + }, this); + }, + hooks : function(event, iterator, done){ + var temp = event.split(":"), + type = temp[0], + eventName = temp[1]; + async.eachSeries(this.__prepost[type][eventName], iterator, done); + } +}; + +function mixin( instance ){ + init.call( instance ); + _.extend( instance, methods ); + return instance; +} + +function create(){ + return mixin( {} ); +} + +module.exports = { + mixin : mixin, + create : create +}; diff --git a/lib/view.js b/lib/view.js index 427c8fdf1a..84524e894e 100644 --- a/lib/view.js +++ b/lib/view.js @@ -348,10 +348,11 @@ View.prototype.render = function(renderFn, locals, callback) { var preRenderQueue = []; // Add Keystone's global pre('render') queue - keystone._pre.render.forEach(function(fn) { + keystone.hooks("pre:render", function(fn, next) { preRenderQueue.push(function(next) { fn(req, res, next); }); + next(); }); this.initQueue.push(preRenderQueue);