Added Evented#unhookEvent for logic overide
parent
701a49226c
commit
914e4eed78
|
@ -259,6 +259,8 @@ module.exports = Trait( 'Evented' )
|
|||
* (by not forwarding the listener to __supet) and instead invoke
|
||||
* __super with a new listener, possibly wrapping the original.
|
||||
*
|
||||
* See also #unhookEvent.
|
||||
*
|
||||
* @O {1} constant time
|
||||
*
|
||||
* @param {string} ev defined event id
|
||||
|
@ -328,17 +330,18 @@ module.exports = Trait( 'Evented' )
|
|||
|
||||
|
||||
/**
|
||||
* Remove a previously hooked listener in constant time, preventing it
|
||||
* from being invoked on future emits of event EV
|
||||
* Remove a previously hooked listener, preventing it from being invoked
|
||||
* on future emits of event EV
|
||||
*
|
||||
* EV must be a valid event identifier. If LISTENER is not registered
|
||||
* for event id EV, no error will occur (since it fulfills the criteria
|
||||
* that it will not be subsequently invoked by event EV).
|
||||
*
|
||||
* Listeners are compared by reference---the exact listener that was
|
||||
* registered with EV must be passed for removal.
|
||||
* The default algorithm compares listeners by reference---the exact
|
||||
* listener that was registered with EV must be passed for removal;
|
||||
* note, however, that the algorithm may be overridden by subtypes.
|
||||
*
|
||||
* @O {1} constant time
|
||||
* @O {#unhookEvent} depends on unhooking algorithm
|
||||
*
|
||||
* @param {string} ev defined event id
|
||||
* @param {Function} listener listener to remove
|
||||
|
@ -353,6 +356,38 @@ module.exports = Trait( 'Evented' )
|
|||
throw Error( `Cannot unhook undefined event \`${ev}'` );
|
||||
}
|
||||
|
||||
this.unhookEvent( ev, listener );
|
||||
return this;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Remove a previously hooked listener in constant time, preventing it
|
||||
* from being invoked on future emits of event EV
|
||||
*
|
||||
* Listeners are compared by reference---the exact listener that was
|
||||
* registered with EV must be passed for removal.
|
||||
*
|
||||
* Subtypes may override this method to undo behavior introduced by a
|
||||
* #hookEvent override. Note, however, that the Eventable specification
|
||||
* prohibits removing arbitrary listeners, and so the removed
|
||||
* listener(s) must have somehow been related to LISTENER by #hookEvent.
|
||||
*
|
||||
* This method will be called even if LISTENER has never been attached
|
||||
* to event EV---this method performs that check, ensuring that any
|
||||
* manipulations performed by #hookEvent can be properly handled here.
|
||||
*
|
||||
* See also #hookEvent.
|
||||
*
|
||||
* @O {1} constant time
|
||||
*
|
||||
* @param {string} ev defined event id
|
||||
* @param {Function} listener listener to remove
|
||||
*
|
||||
* @return {Evented} self
|
||||
*/
|
||||
'virtual protected unhookEvent'( ev, listener )
|
||||
{
|
||||
let evls = this._events[ ev ],
|
||||
levdata = listener[ this._evid ] || {},
|
||||
index = levdata[ ev ];
|
||||
|
|
|
@ -617,6 +617,109 @@ describe( 'event.Evented', () =>
|
|||
} );
|
||||
|
||||
|
||||
/**
|
||||
* Intended to clean up after #hookEvent
|
||||
*/
|
||||
describe( '#unhookEvent', function()
|
||||
{
|
||||
var ev = 'foo',
|
||||
f = ()=>{};
|
||||
|
||||
it( 'can be overridden by subtypes', ( done ) =>
|
||||
{
|
||||
common.checkOverride( EvStub, 'unhookEvent',
|
||||
( given_ev, given_listener ) =>
|
||||
{
|
||||
expect( given_ev ).to.equal( ev );
|
||||
expect( given_listener ).to.equal( f );
|
||||
done();
|
||||
}
|
||||
)().evDefineEvents( [ ev ] )
|
||||
.on( ev, f )
|
||||
.removeListener( ev, f );
|
||||
} );
|
||||
|
||||
|
||||
/**
|
||||
* Since #hookEvent can do whatever it pleases with listeners
|
||||
* (including preventing them from being hooked to begin with, or
|
||||
* replacing them with another hook), we need to ensure that the
|
||||
* hook is called regardless of whether the listener is known to be
|
||||
* attached to a particular event; otherwise, certain types of
|
||||
* cleanup would not be possible.
|
||||
*/
|
||||
it( 'will be called even if listener is not attached', ( done ) =>
|
||||
{
|
||||
common.checkOverride( EvStub, 'unhookEvent', ( _, __ ) =>
|
||||
{
|
||||
done();
|
||||
} )().evDefineEvents( [ ev ] )
|
||||
.removeListener( ev, f );
|
||||
} );
|
||||
|
||||
|
||||
/**
|
||||
* This should never be done---since it violates the Eventable
|
||||
* specification---but it is useful for determining the scope of the
|
||||
* hook's ability, and is important for proving that, when we modify
|
||||
* the behavior, we know that the default behavior is suppressed
|
||||
* (unless we invoke it ourselves).
|
||||
*/
|
||||
it( 'can prevent default implementation', () =>
|
||||
{
|
||||
let norm = () => called = true,
|
||||
called = false;
|
||||
|
||||
EvStub.extend(
|
||||
{
|
||||
'override unhookEvent': ( _, __ ) => {},
|
||||
} )().evDefineEvents( [ ev ] )
|
||||
.on( ev, norm )
|
||||
.removeListener( ev, norm )
|
||||
.evEmit( ev );
|
||||
|
||||
// should have been called since we suppressed unhooking
|
||||
expect( called ).to.be.true;
|
||||
} );
|
||||
|
||||
|
||||
/**
|
||||
* Listeners must ultimately be added/removed by invoking the super
|
||||
* method (to add listeners to the internal data structures needed
|
||||
* for dispatch on emit). If we are doing any sort of meaningful
|
||||
* manipulation, then we must be able to wholly override the
|
||||
* arguments provided to us.
|
||||
*
|
||||
* As an example, if #hookEvent wrapped the given listener $L$ as
|
||||
* $L'$, which was then hooked for dispatch on some event, then we'd
|
||||
* need to similarily remove $L'$---not $L$---when invoking
|
||||
* #removeListener with $L$.
|
||||
*/
|
||||
it( 'can override provided event id and listener', () =>
|
||||
{
|
||||
let rmf = () => called = true,
|
||||
called = false,
|
||||
ev2 = 'snazzlepuss';
|
||||
|
||||
EvStub.extend(
|
||||
{
|
||||
'override unhookEvent'( _, __ )
|
||||
{
|
||||
// use these regardless of what we're given
|
||||
this.__super( ev, rmf );
|
||||
},
|
||||
} )().evDefineEvents( [ ev, ev2 ] )
|
||||
.on( ev, rmf )
|
||||
.removeListener( ev2, f )
|
||||
.evEmit( ev );
|
||||
|
||||
// even though we requested to remove F from some arbitrary
|
||||
// event, our hard-coded removal of RMF should take place
|
||||
expect( called ).to.be.false;
|
||||
} );
|
||||
} );
|
||||
|
||||
|
||||
describe( 'listener metadata', () =>
|
||||
{
|
||||
/**
|
||||
|
|
Loading…
Reference in New Issue