From 8ac8d7de94d37df89195be8deb15cc03cec720bd Mon Sep 17 00:00:00 2001 From: Andrew Stewart Date: Mon, 22 Jun 2015 18:57:34 -0700 Subject: [PATCH] Update config to act as subscribable data store --- lib/config.js | 53 +++++++++++++++++++++++-- spec/lib/config.spec.js | 85 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 134 insertions(+), 4 deletions(-) create mode 100644 spec/lib/config.spec.js diff --git a/lib/config.js b/lib/config.js index 69a11c4..98387bb 100644 --- a/lib/config.js +++ b/lib/config.js @@ -1,8 +1,53 @@ "use strict"; -module.exports = { - logging: {}, +var _ = require("./utils/helpers"); - // are we in TDR test mode? Used to stub out adaptors/drivers. - testMode: false +var config = module.exports = {}, + callbacks = []; + +// default data +config.logging = {}; +config.testMode = false; + +/** + * Updates the Config, and triggers handler callbacks + * + * @param {Object} data new configuration information to set + * @return {void} + */ +config.update = function update(data) { + var forbidden = ["update", "subscribe", "unsubscribe"]; + + Object.keys(data).forEach(function(key) { + if (~forbidden.indexOf(key)) { delete data[key]; } + }); + + if (!Object.keys(data).length) { + return; + } + + _.extend(config, data); + + callbacks.forEach(function(callback) { callback(data); }); +}; + +/** + * Subscribes a function to be called whenever the config is updated + * + * @param {Function} callback function to be called with updated data + * @return {void} + */ +config.subscribe = function subscribe(callback) { + callbacks.push(callback); +}; + +/** + * Unsubscribes a callback from configuration changes + * + * @param {Function} callback function to unsubscribe from changes + * @return {void} + */ +config.unsubscribe = function unsubscribe(callback) { + var idx = callbacks.indexOf(callback); + if (idx >= 0) { callbacks.splice(idx, 1); } }; diff --git a/spec/lib/config.spec.js b/spec/lib/config.spec.js new file mode 100644 index 0000000..e26facb --- /dev/null +++ b/spec/lib/config.spec.js @@ -0,0 +1,85 @@ +"use strict"; + +var config = lib("config"); + +describe("config", function() { + it("contains configuration options", function() { + expect(config.logging).to.be.an("object"); + expect(config.testMode).to.be.eql(false); + }); + + describe("#update", function() { + var callback; + + beforeEach(function() { + callback = spy(); + config.subscribe(callback); + }); + + afterEach(function() { + config.unsubscribe(callback); + delete config.newValue; + }); + + it("updates the configuration", function() { + expect(config.newValue).to.be.eql(undefined); + config.update({ newValue: "value" }); + expect(config.newValue).to.be.eql("value"); + }); + + it("notifies subscribers of changes", function() { + var update = { newValue: "value" }; + expect(callback).to.not.be.called; + config.update(update); + expect(callback).to.be.calledWith(update); + }); + + it("rejects changes that conflict with config functions", function() { + config.update({ update: null }); + expect(config.update).to.be.a("function"); + }); + + it("does nothing with empty changesets", function() { + config.update({}); + expect(callback).to.not.be.called; + }); + }); + + describe("#subscribe", function() { + var callback = spy(); + + afterEach(function() { + delete config.test; + config.unsubscribe(callback); + }); + + it("subscribes a callback to change updates", function() { + config.subscribe(callback); + config.update({ test: true }); + expect(callback).to.be.calledWith({ test: true }); + }); + }); + + describe("#unsubscribe", function() { + var callback; + + beforeEach(function() { + callback = spy(); + config.subscribe(callback); + }); + + afterEach(function() { + delete config.test; + }); + + it("unsubscribes a callback from change updates", function() { + config.update({ test: true }); + expect(callback).to.be.called; + + config.unsubscribe(callback); + + config.update({ test: false }); + expect(callback).to.be.calledOnce; + }); + }); +});