edifice-facade
Advanced tools
Comparing version 0.2.0 to 0.3.0
@@ -1,40 +0,37 @@ | ||
var util = require("util"), | ||
EventEmitter = require("events").EventEmitter; | ||
module.exports = Facade; | ||
Facade.Facade = Facade; | ||
util.inherits(Facade, EventEmitter); | ||
module.exports.Facade = Facade; | ||
function Facade(core, permissions) { | ||
function Facade(core, actions, listeners) { | ||
var thisFacade = this; | ||
// Pass this to all events so that this facade knows its own events. | ||
var identifier = new FacadeIdentifier(); | ||
actions = actions || []; | ||
listeners = listeners || []; | ||
permissions = permissions || {}; | ||
pipeEvents(actions, this, core); | ||
pipeEvents(listeners, core, this).forEach(function (fn) { | ||
// so that core can find events by facade | ||
fn.facade = thisFacade; | ||
this.emit = function (eventName) { | ||
if (permissions.emit && permissions.emit.indexOf(eventName) === -1) { | ||
throw new Error("Event '" + eventName + "' not in 'emit' array"); | ||
} | ||
core.emit.apply( | ||
core, Array.from(arguments).concat(identifier)); | ||
}; | ||
this.on = function (eventName, cb) { | ||
if (permissions.on && permissions.on.indexOf(eventName) === -1) { | ||
throw new Error("Event '" + eventName + "' not in 'on' array"); | ||
} | ||
core.on(eventName, function () { | ||
var idFlag; | ||
if (arguments[arguments.length - 1] === identifier) { | ||
return; | ||
} | ||
if (arguments[arguments.length - 1] instanceof FacadeIdentifier) { | ||
idFlag = -1; | ||
} | ||
cb.apply(null, Array.from(arguments).slice(0, idFlag)); | ||
}); | ||
}; | ||
} | ||
function pipeEvents(eventList, origin, destination) { | ||
return eventList.map(function (event) { | ||
origin.on(event, handler); | ||
return handler; | ||
function handler() { | ||
var backup = origin.listeners(event); | ||
// prevent infinite recursion | ||
origin.removeAllListeners(event); | ||
destination.emit.apply(destination, | ||
Array.prototype.concat.apply([event], arguments)); | ||
// restore backup | ||
backup.forEach(function (listener) { | ||
origin.on(event, listener); | ||
}); | ||
} | ||
}); | ||
} | ||
// Make all identifiers instances of the same class so that they can be | ||
// identified and stripped out of events. | ||
function FacadeIdentifier () {} |
{ | ||
"name": "edifice-facade", | ||
"version": "0.2.0", | ||
"version": "0.3.0", | ||
"description": "Create facades to separate modules in your application", | ||
@@ -32,5 +32,4 @@ "main": "lib/facade.js", | ||
"grunt-contrib-watch": "~0.6.1", | ||
"jshint": "~2.5.0", | ||
"clone": "~0.1.11" | ||
"jshint": "~2.5.0" | ||
} | ||
} |
edifice-facade | ||
============== | ||
Create facades to separate modules in your application | ||
Create facades to separate modules in your application. | ||
Facades rely on a core which can be as simple as an EventEmitter. They separate | ||
modules from each other, so that the modules communicate with each other using | ||
events. | ||
modules from each other, and the modules communicate using events. It also | ||
enhances internal application security, ensuring nothing has more in scope than | ||
it needs. | ||
```` | ||
````js | ||
var EventEmitter = require("events").EventEmitter, | ||
@@ -16,13 +17,49 @@ Facade = require("edifice-facade"); | ||
var fileInterfaceFacade = new Facade(core, | ||
['fileDataRead', 'fileWritten'], // permitted actions | ||
['newInputData'] // permitted listeners | ||
); | ||
var userInterfaceFacade = new Facade(core, | ||
['newInputData'], | ||
['fileDataRead', 'fileWritten'] | ||
); | ||
var timerFacade = new Facade(core); | ||
var userInterfaceFacade = new Facade(core); | ||
```` | ||
Now pass these facades to your `fileInterface` and `userInterface`, so they | ||
Now pass these facades to your `timer` and `userInterface`, so they | ||
don't need to have each other or the core itself in scope. | ||
````js | ||
function userInterface(facade) { | ||
timerUserEntry.onchange = function () { | ||
facade.emit('timerValueChange', this.value); | ||
}; | ||
facade.on('timerValueChange', function (timerVal) { | ||
timerUserEntry.value = timerVal; | ||
}); | ||
} | ||
```` | ||
Facades don't respond to their own events, only events from other facades, | ||
allowing the same data to be updated and read my multiple sources, while | ||
preventing loops or awkward if statements. | ||
Module permissions | ||
------------------ | ||
Facades can be restricted in what events they can send and receive. | ||
````js | ||
var fileSystemFacade = new Facade(core, { | ||
emit : ['writeSuccess', 'fileContents'], | ||
on : ['save', 'read'] | ||
}); | ||
// This line will throw an error, as fileSystemFacade doesn't have receive (on) | ||
// permissions on userInteraction. | ||
fileSystemFacade.on('userInteraction', function () { ... }); | ||
// This line will also throw, as fileSystemFacade doesn't have send (emit) | ||
// permissions on save. It should be doing the saving! | ||
fileSystemFacade.emit('save', data); | ||
```` | ||
Why the name edifice-facade? | ||
---------------------------- | ||
I originally planned on making a module called edifice as the core, then | ||
realised the core could just be a plain old EventEmitter. So ediface is no more. | ||
License | ||
------- | ||
MIT. |
var clone = require("clone"), | ||
EventEmitter = require("events").EventEmitter, | ||
expect = require("expect.js"), | ||
injectr = require("injectr"), | ||
pretendr = require("pretendr"); | ||
EventEmitter = require("events").EventEmitter, | ||
expect = require("expect.js"), | ||
injectr = require("injectr"), | ||
pretendr = require("pretendr"); | ||
describe("Facade", function () { | ||
var Facade, | ||
mockCore, | ||
core, | ||
coreListener; | ||
var Facade, | ||
core, | ||
coreListener; | ||
beforeEach(function () { | ||
core = new EventEmitter(); | ||
coreListener = pretendr(function () {}); | ||
core.on("a", coreListener.mock); | ||
}); | ||
describe("#emit", function () { | ||
beforeEach(function () { | ||
core = new EventEmitter(); | ||
coreListener = pretendr(function () {}); | ||
core.on("a", coreListener.mock); | ||
Facade = injectr("../lib/facade.js").Facade; | ||
}); | ||
it("inherits EventEmitter", function () { | ||
var requires; | ||
requires = pretendr({ | ||
util : { | ||
inherits : function () {} | ||
}, | ||
events : { | ||
EventEmitter : function () {} | ||
} | ||
}); | ||
Facade = injectr("../lib/facade.js", requires.mock).Facade; | ||
expect(requires.util.inherits.calls).to.have.length(1); | ||
expect(requires.util.inherits.calls[0].args) | ||
.to.have.property(0, Facade) | ||
.and.to.have.property(1, requires.events.EventEmitter.mock); | ||
it("passes events to the core", function () { | ||
var facade = new Facade(core); | ||
facade.emit("a"); | ||
expect(coreListener.calls).to.have.length(1); | ||
}); | ||
it("makes the array properties optional", function () { | ||
Facade = injectr("../lib/facade.js").Facade; | ||
expect(function () { | ||
new Facade(new EventEmitter()); | ||
}).to.not.throwException(); | ||
it("passes arguments to the core", function () { | ||
var eventData = [{}, {}, {}], | ||
facade = new Facade(core); | ||
facade.emit("a", eventData[0], eventData[1], eventData[2]); | ||
expect(coreListener.calls[0].args[0]).to.equal(eventData[0]); | ||
expect(coreListener.calls[0].args[1]).to.equal(eventData[1]); | ||
expect(coreListener.calls[0].args[2]).to.equal(eventData[2]); | ||
}); | ||
describe("#emit", function () { | ||
beforeEach(function () { | ||
Facade = injectr("../lib/facade.js").Facade; | ||
}); | ||
it("passes emits to the core for listed actions", function () { | ||
var eventData = {}, | ||
facade = new Facade(core, ["a"], []); | ||
facade.emit("a", eventData); | ||
expect(coreListener.calls).to.have.length(1); | ||
expect(coreListener.calls[0].args) | ||
.to.have.property(0, eventData); | ||
}); | ||
it("passes emits to the core with no data", function () { | ||
var facade = new Facade(core, ["a"], []); | ||
facade.emit("a"); | ||
expect(coreListener.calls).to.have.length(1); | ||
expect(coreListener.calls[0].args).to.have.length(0); | ||
}); | ||
it("doesn't emit multiple times", function () { | ||
var facade = new Facade(core, ["a"], ["a"]), | ||
listener = pretendr(function () {}); | ||
facade.on("a", listener.mock); | ||
facade.emit("a", {}); | ||
expect(coreListener.calls).to.have.length(1); | ||
expect(listener.calls).to.have.length(1); | ||
}); | ||
it("doesn't disrupt the listeners on itself", function () { | ||
var e, | ||
facade = new Facade(core, ["a"], ["a", "b"]); | ||
["a", "a", "b"].forEach(function (e) { | ||
facade.on(e, function () {}); | ||
}); | ||
e = clone(facade._events); | ||
facade.emit("a"); | ||
expect(facade._events).to.eql(e); | ||
}); | ||
it("doesn't pass emits of non-listed actions", function () { | ||
var facade = new Facade(core, ["c"], []); | ||
facade.emit("a", {}); | ||
expect(coreListener.calls).to.have.length(0); | ||
}); | ||
it("passes multiple arguments to the core", function () { | ||
var facade = new Facade(core, ["a"], []); | ||
facade.emit("a", "b", "c"); | ||
expect(coreListener.calls).to.have.length(1); | ||
expect(coreListener.calls[0].args) | ||
.to.have.property(0, "b") | ||
.and.to.have.property(1, "c"); | ||
}); | ||
it("throws an error when events aren't in the emit array", function () { | ||
var facade = new Facade(core, { | ||
emit : [] | ||
}); | ||
expect(function () { | ||
facade.emit('a'); | ||
}).to.throwError(/Event 'a' not in 'emit' array/); | ||
}); | ||
describe("#on", function () { | ||
beforeEach(function () { | ||
Facade = injectr("../lib/facade.js").Facade; | ||
mockCore = pretendr({ | ||
on : function () {}, | ||
emit : function () {} | ||
}); | ||
}); | ||
it("passes handlers to the core for listed listeners", function () { | ||
var fn = pretendr(function () {}), | ||
facade = new Facade(core, [], ["b"]); | ||
facade.on("b", fn.mock); | ||
expect(core.listeners("b")).to.have.length(1); | ||
core.emit("b", "foo"); | ||
expect(fn.calls).to.have.length(1); | ||
expect(fn.calls[0].args).to.have.property(0, "foo"); | ||
}); | ||
it("doesn't pass handlers for non-listed listeners", function () { | ||
var facade = new Facade(core, [], ["b"]), | ||
listener = pretendr(function () {}); | ||
facade.on("c", listener.mock); | ||
core.emit("c"); | ||
expect(listener.calls).to.have.length(0); | ||
}); | ||
it("adds the facade as a property of the handler", function () { | ||
var facade = new Facade(core, [], ["b"]); | ||
facade.on("b", function () {}); | ||
expect(core.listeners("b")[0]) | ||
.to.have.property("facade", facade); | ||
}); | ||
it("doesn't throw an error when events are in the emit array", function () { | ||
var facade = new Facade(core, { | ||
emit : ['a'] | ||
}); | ||
expect(function () { | ||
facade.emit('a'); | ||
}).not.to.throwError(); | ||
}); | ||
}); | ||
}); | ||
describe("#on", function () { | ||
var listener; | ||
beforeEach(function () { | ||
Facade = injectr("../lib/facade.js").Facade; | ||
listener = pretendr(function () {}); | ||
}); | ||
it("listens for events on the core", function () { | ||
var facade = new Facade(core); | ||
facade.on('b', listener.mock); | ||
core.emit('b'); | ||
expect(listener.calls).to.have.length(1); | ||
}); | ||
it("does not listen for events created by own emit", function () { | ||
var facade = new Facade(core); | ||
facade.on('c', listener.mock); | ||
facade.emit('c'); | ||
expect(listener.calls).to.have.length(0); | ||
}); | ||
it("has arguments passed from the core", function () { | ||
var arg = {}, | ||
facade = new Facade(core); | ||
facade.on('c', listener.mock); | ||
core.emit('c', arg); | ||
expect(listener.calls[0].args[0]).to.equal(arg); | ||
}); | ||
it("throws an error when events aren't in the 'on' array", function () { | ||
var facade = new Facade(core, { | ||
on : [] | ||
}); | ||
expect(function () { | ||
facade.on('a'); | ||
}).to.throwError(/Event 'a' not in 'on' array/); | ||
}); | ||
it("doesn't throw an error when events are in the 'on' array", function () { | ||
var facade = new Facade(core, { | ||
on : ['a'] | ||
}); | ||
expect(function () { | ||
facade.on('a'); | ||
}).not.to.throwError(); | ||
}); | ||
}); | ||
describe("pair of facades", function () { | ||
var facades, | ||
listener; | ||
beforeEach(function () { | ||
Facade = injectr("../lib/facade.js").Facade; | ||
listener = pretendr(function () {}); | ||
facades = [new Facade(core), new Facade(core)]; | ||
}); | ||
it("will pass arguments exactly between them", function () { | ||
var args = [{}, [], '3']; | ||
facades[0].on('a', listener.mock); | ||
facades[1].emit('a', args[0], args[1], args[2]); | ||
expect(listener.calls[0].args) | ||
.to.have.length(args.length) | ||
.and.to.have.property(0, args[0]) | ||
.and.to.have.property(1, args[1]) | ||
.and.to.have.property(2, args[2]); | ||
}); | ||
}); | ||
}); |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
54190
8
16
65
184
1