Skip to main content
2 of 2
deleted 12 characters in body; edited tags; edited title
Jamal
  • 34.9k
  • 13
  • 133
  • 237

Member-specific object cache for Angular

I'm trying to build a reusable object cache for Angular. The goal is to:

  1. Cache objects by type
  2. Prevent collisions and conflicts
  3. Find objects by like property
  4. Ensure caches are separated by the logged in user

I am using Satellizer to determine if the user is logged in. Each object's service will act as a wrapper to this service and pass in its typeName property (probably set as a constant in the calling service).

(function () {

    'use strict';

    angular
        .module('blocks.object-cache');

    objectCache.$inject = ['CacheFactory', '$auth'];

    function objectCache(CacheFactory, $auth) {

        var _options = {
            maxAge : (60 * 60 * 1000),
            deleteOnExpire : 'passive',
            storageMode : 'localStorage'
        };

        var service = {
            setOptions          : setOptions,
            getCache            : getCache,

            clear               : clear,

            getAll              : getAll,
            getItem             : getItem,
            getItems            : getItems,

            putItem             : putItem,
            putItems            : putItems,

            getItemsByKey       : getItemsByKey,
            getItemByKeyFirst   : getItemByKeyFirst,
            getItemByKeySingle  : getItemByKeySingle,


            removeItemsByKey    : removeItemsByKey,
            removeItemByKey     : removeItemByKey,

            putItemsByKey       : putItemsByKey,
            putItemByKey        : putItemByKey
        };

        return service;
        ////////////////////////////////////////////////////////////////////////////////

        function setOptions (options) {
            options = options || {};
            options.maxAge = options.maxAge = _options.maxAge;
            options.deleteOnExpire = options.deleteOnExpire = _options.deleteOnExpire;
            options.storageMode = options.storageMode = _options.storageMode;
            _options = options;
        }
        function getCache(typeName) {
            var cacheName = [getUserId(), normalizeTypeName(typeName || 'objects')].join('_');
            var cache = CacheFactory(cacheName);
            if (cache) { return cache; }
            cache = CacheFactory(cacheName, _options);
            return cache;
        }

        function clear (typeName) {
            var cache = getCache(typeName);
            cache.removeAll();
            return (!cache.keys() || (cache.keys().length < 1));
        }

        function getAll (typeName) {
            var cache = getCache(typeName);
            var result = [];
            (cache.keys() || []).forEach(function(key){
                result.push(cache(key));
            });
            return result;
        }
        function getItem(typeName, id) {
            if (typeof id == 'undefined' || !id.trim) { return null; }
            var cache = getCache(typeName);
            return cache.get(id);
        }
        function getItems(typeName, ids) {
            var cache = getCache(typeName),
                result = [],
                _ids   = [];
            (ids || []).forEach(function(id){
                if (_ids.indexOf(id) < 0) {
                    _ids.push(id);
                    var item = cache.get(id);
                    if (item) { result.push(item); }
                }
            });
            return result;
        }

        function putItem(typeName, item, id, refresh) {
            if (typeof item == 'undefined') { return false; }
            if (typeof id == 'undefined' || !id.trim) { return false; }
            var cache = getCache(typeName);
            var existing = cache.get(id);
            if (existing && !refresh) { return true; }
            if (existing) { cache.remove(id); }
            cache.put(item, id);
            return (!!cache.get(id));
        }
        function putItems(typeName, items, idField, refresh) {
            var cache = getCache(typeName);
            (items || []).forEach(function(item){
                var id = item[idField];
                if (typeof id != 'undefined') {
                    var existing = cache.get(id);
                    if (existing && !!refresh) { cache.remove(id); }
                    if (!existing || !!refresh) { cache.put(item, id); }
                    if (!cache.get(id)) { return false; }
                }
            });
            return true;
        }

        function getItemsByKey(typeName, key, value, isCaseSensitive) {
            var result = [];
            (getAll(typeName) || []).forEach(function(item){
                var itemValue = item[key];
                if (typeof itemValue != 'undefined') {
                    if ((typeof value == 'string') && (typeof itemValue == 'string') && (!isCaseSensitive || value.toLowerCase() == itemValue.toLowerCase())) {
                        result.push(item);
                    } else if (((typeof value) == (typeof itemValue)) && (value == itemValue)) {
                        result.push(item);
                    } else {
                        // Other scenarios?
                    }
                }
            });
            return result;
        }
        function getItemByKeyFirst(typeName, key, value, isCaseSensitive) {
            var items = getItemsByKey(typeName, key, value, isCaseSensitive) || [];
            return (items.length > 0) ? items[0] : null;
        }
        function getItemByKeySingle(typeName, key, value, isCaseSensitive) {
            var items = getItemsByKey(typeName, key, value, isCaseSensitive) || [];
            return (items.length === 0) ? items[0] : null;
        }
    
        function removeItemsByKey (typeName, keyField, values, isCaseSensitive) {
            var cache = getCache(typeName),
                keysToRemove = [];
            (cache.keys() || []).forEach(function(key){
                var item        = cache.get[key],
                    itemValue   = item[keyField];
                if (typeof itemValue != 'undefined') {
                    for (var v = 0; v < (values || []).length; v += 1) {
                        if ((typeof values[v] == 'string') && (typeof itemValue == 'string') && (!isCaseSensitive || values[v].toLowerCase() == itemValue.toLowerCase())) {
                            keysToRemove.push(key);
                            break;
                        } else if (((typeof values[v]) == (typeof itemValue)) && (values[v] == itemValue)) {
                            keysToRemove.push(key);
                            break;
                        } else {
                            // Other scenarios?
                        }
                    }
                }
            });
            var success = true;
            keysToRemove.forEach(function(key){
                cache.remove(key);
                if (cache.get(key)) { success = false; }
            });
            return success;
        }
        function removeItemByKey (typeName, keyField, value, isCaseSensitive) {
            return removeItemsByKey(typeName, keyField, [value], isCaseSensitive);
        }

        function putItemsByKey(typeName, items, keyField, refresh, isCaseSensitive) {
            if (!!refresh) {
                var values  = _.map((items || []), keyField);
                if (!removeItemsByKey(typeName, keyField, values, isCaseSensitive)) { return false; }
            }
            var cache = getCache(typeName);
            (items || []).forEach(function(item){
                var id = item[keyField];
                if (typeof value != 'undefined') { cache.put(item, id); }
                if (!cache.get(id)) { return false; }
            });
            return true;
        }
        function putItemByKey(typeName, item, keyField, refresh, isCaseSensitive) {
            return putItemsByKey(typeName, [item], keyField, refresh, isCaseSensitive);
        }

        function getUserId () {
            return $auth.isAuthenticated() ? ($auth.getPayload().sub || 'unknown') : 'public';
        }
        function normalizeTypeName (typeName) {
            return typeName.split('.').join('-');
        }
    }

})();
G. Deward
  • 204
  • 1
  • 5