// Include the events library so we can extend the EventEmitter // class. This will allow our evented cache to emit() events // when various mutations take place. var EventEmitter = require( "events" ).EventEmitter; // ---------------------------------------------------------- // // ---------------------------------------------------------- // // I am the evented cache constructor. I store key/value pairs, and // emit events whenever they local cache is mutated. function EventedCache(){ // Call the super constructor. EventEmitter.call( this ); // I am the cache of name-value pairs being stored. this._cache = {}; // I am the collection of values added as implicit getter / // setters. We need to keep track of these so that when the // value gets removed from the cache, we'll know if we need to // remove the implicit getter / setter. this._getterSetters = {}; // Return this object reference. return( this ); } // Extend the event emitter class so that we can use on() and emit() // in conjunction with cache-based mutations. EventedCache.prototype = Object.create( EventEmitter.prototype ); // I clear the local cache. EventedCache.prototype.clear = function(){ // Keep track of the number of items being cleared. var clearCount = 0; // Loop over the object to remove each key individually. This // will allow a "remove" event to be emitted for each key in the // current cache. for (var key in this._cache){ // Make sure this is a local property (and is not a property // coming from higher up in the prototype chain). if (this._cache.hasOwnProperty( key )){ // Remove the key. this.remove( key ); // Increment our clear counter. clearCount++; } } // Emit the clear event. this.emit( "clear", clearCount ); // Return this object reference for method chaining. return( this ); }; // I get the value at the given key. If no value exists, an optional // default value can be returned. EventedCache.prototype.get = function( name, defaultValue ){ // Check to see if the name exists in the local cache. if (this._cache.hasOwnProperty( name )){ // Return the currently stored value. return( this._cache[ name ] ); } // Return the default value (if it was provided) or null. return( (arguments.length == 2) ? defaultValue : null ); }; // I get all the values in the local cache. This does not break // encapsulation - it does not return a reference to the internal // cache store. // // NOTE: Cached complex objects are still passed by reference. EventedCache.prototype.getAll = function(){ // Create a new transport object for our values. We don't want // to pass back the underlying cache value as that breaks our // layer of encapsulation. var transport = {}; // Loop over each key and transfer it to the transport object. for (var key in this._cache){ // Make sure that this key is part of the actual cache. if (this._cache.hasOwnProperty( key )){ // Copy key/value pair over. transport[ key ] = this._cache[ key ]; } } // Return the collection. return( transport ); }; // I remove any value at the given name. EventedCache.prototype.remove = function( name ){ // Check to see if the given name even exists in the local cache. if (this._cache.hasOwnProperty( name )){ // Get the current value. var value = this._cache[ name ]; // Delete the cache entry. delete( this._cache[ name ] ); // Delete any implicit getter / setter we created. this._removeGetterSetter( name ); // Emit the remove event. this.emit( "remove", name, value ); } // Return this object reference for method chaining. return( this ); }; // I try to remove the implicit getter / setter properties. EventedCache.prototype._removeGetterSetter = function( name ){ // Before we delete anything, make sure that the given property // was added as a getter / setter. if (!this._getterSetters.hasOwnProperty( name )){ // Return out - this property was not added as a getter / // setter. We don't want to run the risk of deleting a // critical value. return; } // Delete the getter / setter. delete( this[ name ] ); // Delete the tracking of this value. delete( this._getterSetters[ name ] ); // Return this object reference for method chaining. return( this ); }; // I set the value at the given name. EventedCache.prototype.set = function( name, value ){ // Store the value in the local cache. this._cache[ name ] = value; // Try to add an implicit getter / setter for this value. this._setGetterSetter( name ); // Emit the set event. this.emit( "set", name, value ); // Return this object reference for method chaining. return( this ); }; // I try to add the implicit getter / setter properties. EventedCache.prototype._setGetterSetter = function( name ){ var that = this; // If the property already exists on the object (whether as // a getter/setter or a different value), we do not want to // overwrite it. if (name in this){ // Return out - we can't add the getter / setter without // possibly corrupting the instance API. return; } // Define the implicit getter. this.__defineGetter__( name, function(){ return( that.get( name ) ); } ); // Define the implicit setter. this.__defineSetter__( name, function( value ){ return( that.set( name, value ) ); } ); // Keep track of the getter / setter. this._getterSetters[ name ] = true; // Return this object reference for method chaining. return( this ); }; // ---------------------------------------------------------- // // ---------------------------------------------------------- // // Since this class is meant to be extended, export the constructor. exports.EventedCache = EventedCache;