UNPKG

eventif

Version:

Intelligent event emitter for nodejs - only handle events when they have 'the right stuff'

349 lines (237 loc) 9.54 kB
# eventif Clean, conditional event handlers. ## Overview eventif simplifies event handling. Instead of doing this: ````javascript emitter.on('anevent',function(a,b){ if (a === 1 && b !== 1) { // do something } else if (a !== 1 && b === 2) { // do something else } else if (a > 5) { // another thing } else if (b < 20) { // and another something } else { // default case } }); emitter.emit(2,20); emitter.emit(1,0); emitter.emit(-1); ```` Do this: ````javascript emitter.on({a: 1, b: 2}, handler1); emitter.on({a: 0}, handler2); emitter.on({a: {from: 3, to: 10}, b: -1, _join: 'OR'}, handler3); ```` Clean, simple, short *readable* handlers. ## Installation Simple: ````bash npm install eventif ```` ## Usage Eventif follows the syntax and style of [Nodejs Event Emitter](https://nodejs.org/dist/latest-v4.x/docs/api/events.html). You follow three simple steps: 1. Create or enable an eventif emitter 2. Create zero, one or more handlers 3. Emit events ### Create An Event Emitter You have 2 ways to create an eventif emitter: * Create a new eventif emitter * Enable an existing event emitter to be eventif-capable #### Create a New Emitter You can create a new eventif-compliant emitter by having it inheirt from eventif. As the example in the docs shows, you can use either classical nodejs prototypal inheritance: ````javascript const Eventif = require('eventif'); const util = require('util'); function MyEmitter() { EventEmitter.call(this); } util.inherits(MyEmitter, Eventif); myEmitter = new MyEmitter(); ```` Or, if you prefer, you can use ES6-style syntax: ````javascript const EventIf = require('eventif'); class MyEmitter extends EventIf {} const myEmitter = new MyEmitter(); ```` In both of the above examples, you are creating a new emitter by inheriting from `eventif` instead of from `events`. #### Enable an Existing Emitter If you already have a standard event emitter class which, somewhere down the line, inherits from `events`, you may not be able to change it to inherit from `eventif` instead. In that case, instead of creating a new event emitter, you *enable* the existing one to be eventif capable. ````javascript const Eventif = require('eventif'); const EventEmitter = require('events'); const util = require('util'); function MyEmitter() { EventEmitter.call(this); } util.inherits(MyEmitter, Eventif); Em = Eventif.y(MyEmitter); // or an alias Em = Eventif.enable(MyEmitter); myEmitter = new Em(); ```` If es6 classes make you happier: ````javascript const EventIf = require('eventif'); const EventEmitter = require('events'); class MyEmitter extends EventEmitter {} Em = Eventif.y(MyEmitter); // or an alias Em = Eventif.enable(MyEmitter); myEmitter = new Em(); ```` **NOTE:** You only can enable an existing event emitter class if it inherits from `EventEmitter`. Now we have an eventif-capable emitter. Let's use it! ### Create zero or more handlers How would you create a normal event handler? With `on`! ````javascript myEmitter.on('anevent',function(a,b){ // do something }); ```` How would you create an eventif-capable handler? **In exactly the same way!** ````javascript myEmitter.on('anevent',function(a,b){ // do something }); ```` *Handlers do not need to be changed to support eventif.* However, you now have the *option* to make those handlers conditional: ````javascript myEmitter.on('anevent',{a:1, b:0},function(a,b){ // do something }); ```` As you can see, handlers really do not *need* to be changed. Of course, you may *want* to change them, now that all of that extraneous `if-else if-else if-else` or perhaps `switch-case-case-case-case-case-default` code can go away! The signature for emitter functions is as follows: * `emitter.on(event,conditions,handler)` * `emitter.once(event,conditions,handler)` * `emitter.emit(event,[param1,param2,...,paramN])` See below for details on the correct syntax for conditions and how it matches parameters. #### No Conditions If you call `on()` or `once()` with no conditions at all, it is handled directly by the original event emitter. ````javascript myEmitter.on('anevent',function(a,b){ // do something }); ```` The above means, "for every event of type 'anevent', *always* call the handler". #### Using once eventif fully supports `once()`. The [rules for `once()`](https://nodejs.org/dist/latest-v4.x/docs/api/events.html#events_emitter_once_event_listener) state that "This listener is invoked only the next time event is triggered, after which it is removed." eventif follows the same logic. The handler is removed after the handler actually is invoked. Thus, if the handler would have been invoked if not for eventif, but the conditions do not match, then the handler remains in place. It is removed **only** after the actual handler function has been called at least once. ````javascript myEmitter.once({a:1},'anevent',function(a,b){ // do something }); myEmitter.emit('anevent'); // handler is NOT run or removed myEmitter.emit('anevent',2); // handler is NOT run or removed myEmitter.emit('anevent',1); // handler IS run and removed myEmitter.emit('anevent',1); // handler already WAS removed, so it is NOT run ```` On the other hand, if you used a traditional event emitter, or even eventif without any conditions: ````javascript myEmitter.once('anevent',function(a,b){ // do something }); myEmitter.emit('anevent'); // handler IS run and removed myEmitter.emit('anevent',2); // handler already WAS removed, so it is NOT run ```` ### Emit Events This is easy. Just emit events like you always do! ````JavaScript myEmitter.emit('anevent'); // or if you want to pass parameters myEmitter.emit('anevent',1,0,"hello world!"); ```` ## Conditions and Syntax In order for your conditions to work correctly, you need to specify the correct conditions syntax. The syntax is the JSQL syntax of [searchjs](https://github.com/deitch/searchjs). The condition is what you pass as the (optional) second argument to `.on()` or `.once()`. The data checked against the condition is constructed of the name of the event and the parameters to your handler. Let's take that apart. Here's a simple example: ````javascript emitter.on('anevent',/* some condition */,function(a,b){ // do something }); emitter.emit('anevent',1,"hello"); ```` Your handler expects the first argument to the event to be saved as `a` and the second as `b`. Thus, the object tested against will be: ````json {a:1, b:"hello"} ```` On the other hand, if you created the handler as follows: ````javascript emitter.on('anevent',/* some condition */,function(q,f){ // do something }); emitter.emit('anevent',1,"hello"); ```` Then the data to test the condition against would be: ````json {q:1, f:"hello"} ```` In addition, the parameters are saved in order: ````json {1:1, 2: "hello"} ```` All that really happens with each event emission is: 1. Create a new object `{}` 2. Populate the object with the parameters to the event call `{1:1, 2:"hello"}` 3. If the handler has named arguments, populate the object with the named parameters `{1:1, 2:"hello", a:1, b:"hello"}` 4. Call `searchjs.matchObject(data,condition)`, e.g. `searchjs.match({1:1, 2:"hello", a:1, b:"hello"}, {1:1})`. 5. If `matchObject()` returns `true`, call the handler, else do not. ### Examples In each example, we will show if it matches, and thus the handler is called, and why or why not. ##### Example 1 ````javascript emitter.on('anevent',function(){ }); emitter.emit('anevent',1,2,'hello','jim',25,{a:1}); ```` **YES:** With no condition, the handler always is called. ##### Example 2 ````javascript emitter.on('anevent',{order:1,name:"jim"},function(order,count,message,name){ }); emitter.emit('anevent',1,2,'hello','jim',25,{a:1}); ```` **YES:** * `order` is 1st argument to function, which means 1st parameter to `emit()`. In condition, `order:1` means it must equal 1. It does, and it matches. * `name` is 4th argument to function, which means 4th parameter to `emit()`. In condition, `name:"jim"` means it must equal "jim". It does, and it matches. ##### Example 3 ````javascript emitter.on('anevent',{order:1,name:"jill"},function(order,count,message,name){ }); emitter.emit('anevent',1,2,'hello','jim',25,{a:1}); ```` **NO:** * `order` is 1st argument to function, which means 1st parameter to `emit()`. In condition, `order:1` means it must equal 1. It does, and it matches. * `name` is 4th argument to function, which means 4th parameter to `emit()`. In condition, `name:"jill"` means it must equal "jill", but it equals "jim", so it fails. ##### Example 4 ````javascript emitter.on('anevent',{3:"jim"},function(){ }); emitter.emit('anevent',1,2,'hello','jim',25,{a:1}); ```` **YES:** * Condition says that the 4th argument (3 from index 0) must be "jim". The 4th parameter (after the event), or 3rd from index 0, is indeed "jim". ##### Example 5 ````javascript emitter.on('anevent',{order:1,name:"jill",_join:"OR"},function(order,count,message,name){ }); emitter.emit('anevent',1,2,'hello','jim',25,{a:1}); ```` **YES:** Condition says that "order" must equal 1 **OR** "name" must equal "jill". Since one of them is correct ("order" === 1), it matches. ##### Example 6 ````javascript emitter.on('anevent',{count:{from: 1, to: 50} },function(order,count,message,name){ }); emitter.emit('anevent',1,32,'hello','jim',25,{a:1}); ```` **YES:** Condition says that "order" must be from 1 to 50. Since "order" is equal to 32, it is in the range and matches.