backbone.cycle
Version:
Navigating and selecting Backbone Models and Collections.
176 lines (144 loc) • 6.75 kB
JavaScript
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// No dependencies on other helpers
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// --- Recording event arguments for select/deselect events ---
//
// This is the Sinon/Chai version of getEventSpies in the Backbone.Select helpers (which is Jasmine-based).
/**
* Creates and returns event spies for select(ed) and deselect(ed) events, including catch-all spies based on wildcards.
*
* Creating the spies:
*
* - The method takes an observed object, or an array of objects, as the first argument. These objects can be Select.Me
* models and Select.One/Select.Many collections.
*
* - Optionally, it takes a namespace or an array of namespaces as the second argument. If unspecified, the "selected"
* namespace is used.
*
* Generated spies:
*
* - The spy collection covers all base event types, like "selected", "deselected", "select:one" etc, and their
* namespaced variants. The whole set is created for each observed object, regardless of its type.
*
* - In addition, wildcard event spies capture all namespaces for a given event type, as well as the un-namespaced base
* event. E.g., when the namespaces "selected" and "starred" have been passed in, the "select:one:*" spy captures
* "select:one", "select:one:selected" and "select:one:starred" events.
*
* - There is also a top-level "*" spy, capturing all selection-related events. It does not capture other events, like
* "add", "remove" etc. Usage example: expect( events.get( model, "*" ).toHaveBeenCalledTwice();
*
* - Note that there isn't any wildcard for event subtypes like ":one", ":some", ":all" etc. These have to be spelled
* out when querying a spy.
*
* - The base event types are hard-coded. The list must be modified when new event types are created.
*
* Retrieving a spy:
*
* - When a single observed object is passed in during creation, and it is NOT wrapped in an array, the method returns a
* hash of spies. Event names serve as keys. Example:
*
* var modelEvents = getEventSpies( model, ["selected", "starred"] );
* expect( modelEvents["selected:starred"] ).not.toHaveBeenCalled();
*
* - When an array of observed objects is passed in during creation, the method returns a hash table object. Retrieve
* the event spy for an object and an event with the .get( object, eventName ) method. Example:
*
* var events = getEventSpies( [ model, collection ], ["selected", "starred"] );
* expect( events.get( model, "selected:starred" ) ).not.toHaveBeenCalled();
*
*
* @param {Backbone.Model|Backbone.Collection|Array} observed a Select.Me, Select.One or Select.Many entity, or an
* array of them (may be mixed object types)
* @param {string|string[]} [namespaces="selected"]
* @returns {Array|Object}
*/
function getEventSpies ( observed, namespaces ) {
var eventSpies,
baseEventNames = _getBackboneSelectBaseEventNames();
function _getTargetedEventSpies ( observed, eventNames ) {
var eventSpies = _createSpyObj( eventNames );
_.each( eventNames, function ( eventName ) {
observed.listenTo( observed, eventName, eventSpies[eventName] );
} );
return eventSpies;
}
function _getWildcardEventSpies ( observed, baseEventNames, namespaces ) {
var wildcardEventNames = _.map( baseEventNames, function ( baseEventName ) {
return baseEventName + ":*";
} ),
eventSpies = _createSpyObj( wildcardEventNames );
_.each( baseEventNames, function ( baseEventName ) {
var eventNameVariations = [baseEventName].concat( _getNamespacedEventNames( baseEventName, namespaces ) );
_.each( eventNameVariations, function ( eventName ) {
observed.listenTo( observed, eventName, eventSpies[baseEventName + ":*"]);
} );
} );
return eventSpies;
}
function _getStarEventSpy ( observed, eventNames ) {
var eventSpy = sinon.spy();
_.each( eventNames, function ( eventName ) {
observed.listenTo( observed, eventName, eventSpy );
} );
return { "*": eventSpy };
}
function _getEventSpies( observed, baseEventNames, namespaces ) {
var targetedEventSpies, wildcardEventSpies, starEventSpy,
eventNames = _getAllEventNames( baseEventNames, namespaces );
targetedEventSpies = _getTargetedEventSpies( observed, eventNames );
wildcardEventSpies = _getWildcardEventSpies( observed, baseEventNames, namespaces );
starEventSpy = _getStarEventSpy( observed, eventNames );
return _.extend( targetedEventSpies, wildcardEventSpies, starEventSpy );
}
namespaces || ( namespaces = ["selected"] );
if ( _.isString( namespaces ) ) namespaces = [ namespaces ];
if ( _.isArray( observed ) ) {
eventSpies = new HashTableForBackboneEntities();
_.each( observed, function ( singleObserved ) {
eventSpies._set( singleObserved, _getEventSpies( singleObserved, baseEventNames, namespaces ) );
} );
} else {
eventSpies = _getEventSpies( observed, baseEventNames, namespaces );
}
return eventSpies;
}
// --- Helpers ---
function HashTableForBackboneEntities () {
this._hashTable = {};
}
HashTableForBackboneEntities.prototype._set = function ( entity, entry ) {
this._hashTable[_getEntityId( entity )] = entry;
};
HashTableForBackboneEntities.prototype.get = function ( entity, category ) {
return category === undefined ? this._hashTable[_getEntityId( entity )] : this._hashTable[_getEntityId( entity )][category];
};
function _getNamespacedEventNames ( baseEventName, namespaces ) {
return _.map( namespaces, function ( namespace ) {
return baseEventName + ":" + namespace;
} );
}
function _getBackboneSelectBaseEventNames () {
return [
"selected", "deselected", "reselected",
"select:one", "deselect:one", "reselect:one",
"select:none", "select:some", "select:all", "reselect:any"
];
}
function _getEntityId ( entity ) {
return entity._pickyCid || "model " + entity.cid;
}
function _getAllEventNames ( baseEventNames, namespaces ) {
var eventNames = baseEventNames;
_.each( baseEventNames, function ( baseEventName ) {
var namespacedEventNames = _getNamespacedEventNames( baseEventName, namespaces );
eventNames = eventNames.concat( namespacedEventNames );
} );
return eventNames;
}
function _createSpyObj( methodNames ) {
var obj = {};
_.each( methodNames, function ( methodName ) {
obj[methodName] = sinon.spy();
} );
return obj;
}