From 55d8ff75b4b885d371189948a85b62a4345db1fa Mon Sep 17 00:00:00 2001 From: Jacob Gable Date: Sun, 9 Jun 2013 11:16:25 -0500 Subject: [PATCH] Filter priorities Add the ability to specify a priority level when registering filters. Also change doFilter to execute filters in priority order. Closes #86 --- core/frontend/filters/index.js | 3 +- core/ghost.js | 53 +++++++++++++++++++++++++--------- core/test/ghost/ghost_spec.js | 50 ++++++++++++++++++++++++++++++++ 3 files changed, 92 insertions(+), 14 deletions(-) diff --git a/core/frontend/filters/index.js b/core/frontend/filters/index.js index 9768e41d6f..217301a149 100644 --- a/core/frontend/filters/index.js +++ b/core/frontend/filters/index.js @@ -2,10 +2,11 @@ "use strict"; var _ = require('underscore'), + defaultCoreFilterPriority = 4, coreFilters; coreFilters = function (ghost) { - ghost.registerFilter('ghostNavItems', function (args) { + ghost.registerFilter('ghostNavItems', defaultCoreFilterPriority, function (args) { var selectedItem; // Set the nav items based on the config diff --git a/core/ghost.js b/core/ghost.js index 917e4c7a2a..dacb950f53 100644 --- a/core/ghost.js +++ b/core/ghost.js @@ -18,8 +18,18 @@ ExampleFilter = require('../content/plugins/exampleFilters'), Ghost, instance, + defaults, statuses; + // ## Default values + /** + * A hash of default values to use instead of 'magic' numbers/strings. + * @type {Object} + */ + defaults = { + filterPriority: 5, + maxPriority: 9 + }; // ## Article Statuses /** @@ -121,14 +131,20 @@ /** * @param {string} name + * @param {integer} priority * @param {Function} fn */ - Ghost.prototype.registerFilter = function (name, fn) { - if (!this.filterCallbacks.hasOwnProperty(name)) { - this.filterCallbacks[name] = []; + Ghost.prototype.registerFilter = function (name, priority, fn) { + // Curry the priority optional parameter to a default of 5 + if (_.isFunction(priority)) { + fn = priority; + priority = defaults.filterPriority; } - console.log('registering filter for ', name); - this.filterCallbacks[name].push(fn); + + this.filterCallbacks[name] = this.filterCallbacks[name] || {}; + this.filterCallbacks[name][priority] = this.filterCallbacks[name][priority] || []; + + this.filterCallbacks[name][priority].push(fn); }; /** @@ -138,17 +154,25 @@ * @return {method} callback */ Ghost.prototype.doFilter = function (name, args, callback) { - var fn; + var callbacks = this.filterCallbacks[name]; - if (this.filterCallbacks.hasOwnProperty(name)) { - for (fn in this.filterCallbacks[name]) { - if (this.filterCallbacks[name].hasOwnProperty(fn)) { - console.log('doing filter for ', name); - args = this.filterCallbacks[name][fn](args); - } - } + // Bug out early if no callbacks by that name + if (!callbacks) { + return callback(args); } + _.times(defaults.maxPriority + 1, function (priority) { + // Bug out if no handlers on this priority + if (!_.isArray(callbacks[priority])) { + return; + } + + // Call each handler for this priority level + _.each(callbacks[priority], function (filterHandler) { + args = filterHandler(args); + }); + }); + callback(args); }; @@ -180,5 +204,8 @@ }; }; + // TODO: Expose the defaults for other people to see/manipulate as a static value? + // Ghost.defaults = defaults; + module.exports = Ghost; }()); \ No newline at end of file diff --git a/core/test/ghost/ghost_spec.js b/core/test/ghost/ghost_spec.js index 22d7f2e77f..84c27b1a85 100644 --- a/core/test/ghost/ghost_spec.js +++ b/core/test/ghost/ghost_spec.js @@ -46,6 +46,56 @@ }); + it("can register filters with specific priority", function () { + var ghost = new Ghost(), + filterName = 'test', + filterPriority = 9, + testFilterHandler = sinon.spy(); + + ghost.registerFilter(filterName, filterPriority, testFilterHandler); + + should.exist(ghost.filterCallbacks[filterName]); + should.exist(ghost.filterCallbacks[filterName][filterPriority]); + + ghost.filterCallbacks[filterName][filterPriority].should.include(testFilterHandler); + }); + + it("can register filters with default priority", function () { + var ghost = new Ghost(), + filterName = 'test', + defaultPriority = 5, + testFilterHandler = sinon.spy(); + + ghost.registerFilter(filterName, testFilterHandler); + + should.exist(ghost.filterCallbacks[filterName]); + should.exist(ghost.filterCallbacks[filterName][defaultPriority]); + + ghost.filterCallbacks[filterName][defaultPriority].should.include(testFilterHandler); + }); + + it("executes filters in priority order", function (done) { + var ghost = new Ghost(), + filterName = 'testpriority', + testFilterHandler1 = sinon.spy(), + testFilterHandler2 = sinon.spy(), + testFilterHandler3 = sinon.spy(); + + ghost.registerFilter(filterName, 0, testFilterHandler1); + ghost.registerFilter(filterName, 2, testFilterHandler2); + ghost.registerFilter(filterName, 9, testFilterHandler3); + + ghost.doFilter(filterName, null, function () { + + testFilterHandler1.calledBefore(testFilterHandler2).should.equal(true); + testFilterHandler2.calledBefore(testFilterHandler3).should.equal(true); + + testFilterHandler3.called.should.equal(true); + + done(); + }); + }); + }); }()); \ No newline at end of file