From d1e26b4bd1e82b283b8482fecbf60d3b4694305a Mon Sep 17 00:00:00 2001 From: Jason Dobry Date: Mon, 23 Sep 2013 20:34:37 -0600 Subject: [PATCH] Added validation to $angularCacheFactoryProvider.setCacheDefaults. #55 Swapped `localStorageImpl` and `sessionStorageImpl` options for just `storageImpl` option. --- CHANGELOG.md | 1 + README.md | 1 + TRANSITION.md | 28 ++++++++ src/angular-cache.js | 111 +++++++++++++++++++++++++------- test/angularCacheFactorySpec.js | 4 +- 5 files changed, 119 insertions(+), 26 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 999bad2..784e7d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - Swapped `aggressiveDelete` option for `deleteOnExpire` option. #30, #47 - Changed `$angularCacheFactory.info()` to return an object similar to `AngularCache.info()` #45 - Namespaced angular-cache module under `jmdobry` so it is now "jmdobry.angular-cache". #42 +- Substituted `localStorageImpl` and `sessionStorageImpl` options for just `storageImpl` option. ###### Backwards compatible API changes - Added ability to set global cache defaults in $angularCacheFactoryProvider. #55 diff --git a/README.md b/README.md index ab49799..51eef74 100644 --- a/README.md +++ b/README.md @@ -844,6 +844,7 @@ See [AngularCache#info](http://jmdobry.github.io/angular-cache/docs/Cache.html#i - Swapped `aggressiveDelete` option for `deleteOnExpire` option. #30, #47 - Changed `$angularCacheFactory.info()` to return an object similar to `AngularCache.info()` #45 - Namespaced angular-cache module under `jmdobry` so it is now "jmdobry.angular-cache". #42 +- Substituted `localStorageImpl` and `sessionStorageImpl` options for just `storageImpl` option. ###### Backwards compatible API changes - Added ability to set global cache defaults in $angularCacheFactoryProvider. #55 diff --git a/TRANSITION.md b/TRANSITION.md index 05860d2..28f041a 100644 --- a/TRANSITION.md +++ b/TRANSITION.md @@ -42,4 +42,32 @@ $angularCacheFactory('myNewCache', { maxAge: 90000, // Items added to this cache expire after 15 minutes deleteOnExpire: 'none' // Items will expire but not be removed }); +``` + +- Substituted `localStorageImpl` and `sessionStorageImpl` options for just `storageImpl` option. + +###### 1.x.x +```javascript +$angularCacheFactory('myNewCache', { + storageMode: 'localStorage', + localStorageImpl: myLocalStoragePolyfill // Use custom localStorage implementation +}); + +$angularCacheFactory('myNewCache2', { + storageMode: 'sessionStorage', + sessionStorageImpl: mySessionStoragePolyfill // Use custom sessionStorage implementation +}); +``` + +###### 2.0.0 +```javascript +$angularCacheFactory('myNewCache', { + storageMode: 'localStorage', + storageImpl: myLocalStoragePolyfill // Use custom localStorage implementation +}); + +$angularCacheFactory('myNewCache2', { + storageMode: 'sessionStorage', + storageImpl: mySessionStoragePolyfill // Use custom sessionStorage implementation +}); ``` \ No newline at end of file diff --git a/src/angular-cache.js b/src/angular-cache.js index 5c6625c..8cd5bfe 100644 --- a/src/angular-cache.js +++ b/src/angular-cache.js @@ -36,17 +36,98 @@ onExpire: null, cacheFlushInterval: null, storageMode: 'none', - localStorageImpl: null, - sessionStorageImpl: null + storageImpl: null }; }; /** - * @method setCacheDefaults + * @method _validateNumberOption + * @desc Validates the given number option. + * @param {Number} option The number option to check. + * @param {Function} cb Callback function + * @private + * @ignore + */ + function _validateNumberOption(option, cb) { + if (!angular.isNumber(option)) { + cb('must be a number!'); + } else if (option < 0) { + cb('must be greater than zero!'); + } else { + cb(null); + } + } + + /** + * @method $AngularCacheFactoryProvider.setCacheDefaults * @desc Set the default configuration for all caches created by $angularCacheFactory. * @param {Object} options + * @privileged */ this.setCacheDefaults = function (options) { + options = options || {}; + + if (!angular.isObject(options)) { + throw new Error('setOptions(): options: must be an object!'); + } + + if ('capacity' in options) { + _validateNumberOption(options.capacity, function (err) { + if (err) { + throw new Error('setCacheDefaults(): capacity: ' + err); + } + }); + } + + if ('deleteOnExpire' in options) { + if (!angular.isString(options.deleteOnExpire)) { + throw new Error('setCacheDefaults(): deleteOnExpire: must be a string!'); + } else if (options.deleteOnExpire !== 'none' && options.deleteOnExpire !== 'passive' && options.deleteOnExpire !== 'aggressive') { + throw new Error('setCacheDefaults(): deleteOnExpire: accepted values are "none", "passive" or "aggressive"!'); + } + } + + if ('maxAge' in options) { + _validateNumberOption(options.maxAge, function (err) { + if (err) { + throw new Error('setCacheDefaults(): maxAge: ' + err); + } + }); + } + + if ('cacheFlushInterval' in options) { + _validateNumberOption(options.cacheFlushInterval, function (err) { + if (err) { + throw new Error('setCacheDefaults(): cacheFlushInterval: ' + err); + } + }); + } + + if ('storageMode' in options) { + if (!angular.isString(options.storageMode)) { + throw new Error('setCacheDefaults(): storageMode: must be a string!'); + } else if (options.storageMode !== 'none' && options.storageMode !== 'localStorage' && options.storageMode !== 'sessionStorage') { + throw new Error('setCacheDefaults(): storageMode: accepted values are "none", "localStorage" or "sessionStorage"'); + } + if ('storageImpl' in options) { + if (!angular.isObject(options.storageImpl)) { + throw new Error('setCacheDefaults(): [local|session]storageImpl: must be an object!'); + } else if (!('setItem' in options.storageImpl) || typeof options.storageImpl.setItem !== 'function') { + throw new Error('setCacheDefaults(): [local|session]storageImpl: must implement "setItem(key, value)"!'); + } else if (!('getItem' in options.storageImpl) || typeof options.storageImpl.getItem !== 'function') { + throw new Error('setCacheDefaults(): [local|session]storageImpl: must implement "getItem(key)"!'); + } else if (!('removeItem' in options.storageImpl) || typeof options.storageImpl.removeItem !== 'function') { + throw new Error('setCacheDefaults(): [local|session]storageImpl: must implement "removeItem(key)"!'); + } + } + } + + if ('onExpire' in options) { + if (typeof options.onExpire !== 'function') { + throw new Error('setCacheDefaults(): onExpire: Must be a function!'); + } + } + cacheDefaults = angular.extend({}, DEFAULTS(), options); }; @@ -97,7 +178,7 @@ * @class AngularCache * @desc Instantiated via $angularCacheFactory(cacheId[, options]) * @param {String} cacheId The id of the new cache. - * @param {Object} [options] {{[capacity]: Number, [maxAge]: Number, [cacheFlushInterval]: Number, [aggressiveDelete]: Boolean, [onExpire]: Function, [storageMode]: String, [localStorageImpl]: Object}} + * @param {Object} [options] {{[capacity]: Number, [maxAge]: Number, [cacheFlushInterval]: Number, [deleteOnExpire]: String, [onExpire]: Function, [storageMode]: String, [storageImpl]: Object}} */ function AngularCache(cacheId, options) { var size = 0, @@ -135,24 +216,6 @@ }, delay); } - /** - * @method _validateNumberOption - * @desc Validates the given number option. - * @param {Number} option The number option to check. - * @param {Function} cb Callback function - * @private - * @ignore - */ - function _validateNumberOption(option, cb) { - if (!angular.isNumber(option)) { - cb('must be a number!'); - } else if (option < 0) { - cb('must be greater than zero!'); - } else { - cb(null); - } - } - /** * @method _setCapacity * @desc Set the capacity for this cache. @@ -364,7 +427,7 @@ } if ('storageMode' in options) { - _setStorageMode(options.storageMode, options.localStorageImpl || options.sessionStorageImpl); + _setStorageMode(options.storageMode, options.storageImpl); } if ('onExpire' in options) { @@ -735,7 +798,7 @@ /** * @class AngularCacheFactory * @param {String} cacheId The id of the new cache. - * @param {Object} [options] {{capacity: Number, maxAge: Number, deleteOnExpire: String, onExpire: Function, cacheFlushInterval: Number, storageMode: String, localStorageImpl: Object, sessionStorageImpl: Object}} + * @param {Object} [options] {{[capacity]: Number, [maxAge]: Number, [cacheFlushInterval]: Number, [deleteOnExpire]: String, [onExpire]: Function, [storageMode]: String, [storageImpl]: Object}} * @returns {AngularCache} */ function angularCacheFactory(cacheId, options) { diff --git a/test/angularCacheFactorySpec.js b/test/angularCacheFactorySpec.js index 7753d52..eaaf4f4 100644 --- a/test/angularCacheFactorySpec.js +++ b/test/angularCacheFactorySpec.js @@ -210,7 +210,7 @@ describe('AngularCacheFactory', function () { value: 'value2', timestamp: new Date().getTime() })); - var lsCache = $angularCacheFactory('lsCache', { localStorageImpl: myLocalStorage, storageMode: 'localStorage', maxAge: 10, deleteOnExpire: 'aggressive' }); + var lsCache = $angularCacheFactory('lsCache', { storageImpl: myLocalStorage, storageMode: 'localStorage', maxAge: 10, deleteOnExpire: 'aggressive' }); expect(lsCache.get('item1')).toEqual('value1'); expect(lsCache.get('item2')).toEqual('value2'); waits(100); @@ -227,7 +227,7 @@ describe('AngularCacheFactory', function () { value: 'value2', timestamp: new Date().getTime() })); - var ssCache = $angularCacheFactory('ssCache', { sessionStorageImpl: mySessionStorage, storageMode: 'sessionStorage', maxAge: 10, deleteOnExpire: 'aggressive' }); + var ssCache = $angularCacheFactory('ssCache', { storageImpl: mySessionStorage, storageMode: 'sessionStorage', maxAge: 10, deleteOnExpire: 'aggressive' }); expect(ssCache.get('item1')).toEqual('value1'); expect(ssCache.get('item2')).toEqual('value2'); waits(100);