Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 8a7b3a7

Browse files
committed
feat(event): add support for event emitter
event emitter support added, with optionally bind generator methods
1 parent d7ad0ee commit 8a7b3a7

File tree

4 files changed

+693
-1
lines changed

4 files changed

+693
-1
lines changed

lib/util.js

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,14 @@
99
* file that was distributed with this source code.
1010
*/
1111

12-
const util = exports = module.exports = {}
1312
const _ = require('lodash')
1413

14+
const toStr = Object.prototype.toString
15+
const fnToStr = Function.prototype.toString
16+
const isFnRegex = /^\s*(?:function)?\*/
17+
18+
const util = exports = module.exports = {}
19+
1520
/**
1621
* tells whether value exists or not by checking
1722
* it type
@@ -37,3 +42,20 @@ util.existy = function (value) {
3742
util.spread = function () {
3843
return _.isArray(arguments[0]) ? arguments[0] : _.toArray(arguments)
3944
}
45+
46+
/**
47+
* tells whether a method is a genetator function or
48+
* not
49+
*
50+
* @method isGenerator
51+
*
52+
* @param {Function} method
53+
* @return {Boolean}
54+
*
55+
* @private
56+
*/
57+
util.isGenerator = function (method) {
58+
const viaToStr = toStr.call(method)
59+
const viaFnToStr = fnToStr.call(method)
60+
return (viaToStr === '[object Function]' || viaToStr === '[object GeneratorFunction]') && isFnRegex.test(viaFnToStr)
61+
}

providers/EventProvider.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
'use strict'
2+
3+
/**
4+
* adonis-framework
5+
* Copyright(c) 2015-2016 Harminder Virk
6+
* MIT Licensed
7+
*/
8+
9+
const ServiceProvider = require('adonis-fold').ServiceProvider
10+
11+
class EventProvider extends ServiceProvider {
12+
13+
* register () {
14+
this.app.singleton('Adonis/Src/Event', function (app) {
15+
const Event = require('../src/Event')
16+
const Config = app.use('Adonis/Src/Config')
17+
return new Event(Config)
18+
})
19+
}
20+
}
21+
22+
module.exports = EventProvider

src/Event/index.js

Lines changed: 287 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,287 @@
1+
'use strict'
2+
3+
/**
4+
* adonis-framework
5+
*
6+
* (c) Harminder Virk <virk@adonisjs.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
const EventEmitter2 = require('eventemitter2').EventEmitter2
13+
const Ioc = require('adonis-fold').Ioc
14+
const _ = require('lodash')
15+
const util = require('../../lib/util')
16+
const co = require('co')
17+
18+
class Event {
19+
20+
constructor (Config) {
21+
const options = Config.get('event')
22+
this.namedListeners = {}
23+
this.listenerLimit = null
24+
this.emitter = new EventEmitter2(options)
25+
}
26+
27+
/**
28+
* here we resolve the handler from the IoC container
29+
* or the actual callback if a closure was passed.
30+
*
31+
* @param {String|Function} handler
32+
* @return {Object|Function}
33+
*
34+
* @private
35+
*/
36+
_resolveHandler (handler) {
37+
return typeof (handler) === 'string' ? Ioc.makeFunc(handler) : handler
38+
}
39+
40+
/**
41+
* here we bind the instance to the method, only
42+
* if it exists.
43+
*
44+
* @param {Object} instance
45+
* @param {Function} method
46+
* @return {Function}
47+
*
48+
* @private
49+
*/
50+
_bindInstance (instance, method) {
51+
return function () {
52+
instance.emitter = this
53+
method.apply(instance, arguments)
54+
}
55+
}
56+
57+
/**
58+
* here we wrap the generator method using co.wrap
59+
* and make sure to pass the instance to the
60+
* method.
61+
*
62+
* @param {Object} instance
63+
* @param {Function} method
64+
* @return {Function}
65+
*
66+
* @private
67+
*/
68+
_wrapGenerator (instance, method) {
69+
return co.wrap(function * () {
70+
instance.emitter = this
71+
yield method.apply(instance, arguments)
72+
})
73+
}
74+
75+
/**
76+
* here we make the handler method with correct context and
77+
* execution type. It makes it possible to use generator
78+
* methods and other methods as event handler.
79+
*
80+
* @param {Object|Function} handler
81+
* @return {Function}
82+
*
83+
* @private
84+
*/
85+
_makeHandler (handler) {
86+
let parentContext = {}
87+
/**
88+
* if handler is resolved out of IoC container, it will
89+
* be an object with the parent context and the method.
90+
*/
91+
if (typeof (handler) !== 'function' && handler.instance) {
92+
parentContext = handler.instance
93+
handler = handler.instance[handler.method]
94+
}
95+
96+
/**
97+
* if handler to the event is a generator, then we need to
98+
* wrap it inside co with correct context
99+
*/
100+
if (util.isGenerator(handler)) {
101+
return this._wrapGenerator(parentContext, handler)
102+
}
103+
104+
/**
105+
* otherwise we bind the parentContext to the method
106+
*/
107+
return this._bindInstance(parentContext, handler)
108+
}
109+
110+
/**
111+
* returns an array of listeners for a specific event
112+
*
113+
* @param {String} event
114+
* @return {Array}
115+
*
116+
* @public
117+
*/
118+
getListeners (event) {
119+
return this.emitter.listeners(event)
120+
}
121+
122+
/**
123+
* it should tell whether there are any listeners
124+
* for a given event or not.
125+
*
126+
* @param {String} event
127+
* @return {Boolean}
128+
*
129+
* @public
130+
*/
131+
hasListeners (event) {
132+
return !!this.getListeners(event).length
133+
}
134+
135+
/**
136+
* returns the status for wildcard
137+
*
138+
*
139+
* @return {Boolean}
140+
*
141+
* @public
142+
*/
143+
wildcard () {
144+
return this.emitter.wildcard
145+
}
146+
147+
/**
148+
* removes a named handler for a given event from the
149+
* emitter
150+
*
151+
* @param {String} event
152+
* @param {String} name
153+
*
154+
* @public
155+
*/
156+
removeListener (event, name) {
157+
const handler = this.namedListeners[name]
158+
if (!handler) {
159+
throw new Error(`There is no named event with ${name} name for ${event} event`)
160+
}
161+
this.emitter.removeListener(event, handler)
162+
}
163+
164+
/**
165+
* removes all listeners for a given or all events
166+
*
167+
* @param {String} [event]
168+
*
169+
* @public
170+
*/
171+
removeListeners (event) {
172+
event ? this.emitter.removeAllListeners(event) : this.emitter.removeAllListeners()
173+
}
174+
175+
/**
176+
* emits a given event and passes all the data
177+
* to the handler
178+
*
179+
* @param {...Spread} data
180+
*
181+
* @public
182+
*/
183+
emit () {
184+
const args = _.toArray(arguments)
185+
this.emitter.emit.apply(this.emitter, args)
186+
}
187+
188+
/**
189+
* @alias emit
190+
*/
191+
fire () {
192+
this.emit.apply(this, arguments)
193+
}
194+
195+
/**
196+
* binds an handler to any event
197+
*
198+
* @param {Function|Sring} handler
199+
*
200+
* @public
201+
*/
202+
any (handler) {
203+
handler = this._resolveHandler(handler)
204+
handler = this._makeHandler(handler)
205+
this.emitter.onAny(handler)
206+
}
207+
208+
/**
209+
* defines a limit for a listener to be executed
210+
*
211+
* @param {Number} limit
212+
* @return {Object}
213+
*
214+
* @public
215+
*/
216+
limit (limit) {
217+
this.listenerLimit = limit
218+
return this
219+
}
220+
221+
/**
222+
* adds a new event listen for a specific event
223+
*
224+
* @param {String} event
225+
* @param {Function|String} handler
226+
*
227+
* @public
228+
*/
229+
on (event, name, handler) {
230+
if (!handler) {
231+
handler = name
232+
name = null
233+
}
234+
handler = this._resolveHandler(handler)
235+
handler = this._makeHandler(handler)
236+
if (name) {
237+
this.namedListeners[name] = handler
238+
}
239+
240+
/**
241+
* if there is a limit define, go with the many
242+
* method on the emitter
243+
*/
244+
if (this.listenerLimit) {
245+
this.emitter.many(event, this.listenerLimit, handler)
246+
this.listenerLimit = null
247+
return
248+
}
249+
250+
/**
251+
* otherwise register normally
252+
*/
253+
this.emitter.on(event, handler)
254+
}
255+
256+
/**
257+
* @alias on
258+
*/
259+
when () {
260+
this.on.apply(this, arguments)
261+
}
262+
263+
/**
264+
* @alias on
265+
*/
266+
listen () {
267+
this.on.apply(this, arguments)
268+
}
269+
270+
/**
271+
* adds a new event listen for a specific event
272+
* to be ran only for one time.
273+
*
274+
* @param {String} event
275+
* @param {Function|String} handler
276+
*
277+
* @public
278+
*/
279+
once (event, handler) {
280+
handler = this._resolveHandler(handler)
281+
handler = this._makeHandler(handler)
282+
this.emitter.once(event, handler)
283+
}
284+
285+
}
286+
287+
module.exports = Event

0 commit comments

Comments
 (0)