angular-cached-resource
Version:
An AngularJS module to interact with RESTful resources, even when browser is offline
295 lines (280 loc) • 11.3 kB
JavaScript
// Generated by CoffeeScript 1.7.1
var CachedResourceManager, DEFAULT_ACTIONS, ResourceCacheArrayEntry, ResourceCacheEntry, app, resourceManagerListener,
__slice = [].slice;
DEFAULT_ACTIONS = {
get: {
method: 'GET'
},
query: {
method: 'GET',
isArray: true
},
save: {
method: 'POST'
},
remove: {
method: 'DELETE'
},
"delete": {
method: 'DELETE'
}
};
ResourceCacheEntry = require('./resource_cache_entry');
ResourceCacheArrayEntry = require('./resource_cache_array_entry');
CachedResourceManager = require('./cached_resource_manager');
resourceManagerListener = null;
app = angular.module('ngCachedResource', ['ngResource']);
app.factory('$cachedResource', [
'$resource', '$timeout', '$q', '$log', function($resource, $timeout, $q, $log) {
var modifyObjectInPlace, processReadArgs, readArrayCache, readCache, resourceManager, writeCache;
resourceManager = new CachedResourceManager($timeout);
if (resourceManagerListener) {
document.removeEventListener('online', resourceManagerListener);
}
resourceManagerListener = function(event) {
return resourceManager.flushQueues();
};
document.addEventListener('online', resourceManagerListener);
processReadArgs = function(args) {
var deferred, error, params, success;
args = Array.prototype.slice.call(args);
params = angular.isObject(args[0]) ? args.shift() : {};
success = args[0], error = args[1];
deferred = $q.defer();
if (angular.isFunction(success)) {
deferred.promise.then(success);
}
if (angular.isFunction(error)) {
deferred.promise["catch"](error);
}
return {
params: params,
deferred: deferred
};
};
modifyObjectInPlace = function(oldObject, newObject) {
var key, _i, _j, _len, _len1, _ref, _ref1, _results;
_ref = Object.keys(oldObject);
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
key = _ref[_i];
if (key[0] !== '$') {
if (newObject[key] == null) {
delete oldObject[key];
}
}
}
_ref1 = Object.keys(newObject);
_results = [];
for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
key = _ref1[_j];
if (key[0] !== '$') {
if (angular.isObject(oldObject[key]) && angular.isObject(newObject[key])) {
_results.push(modifyObjectInPlace(oldObject[key], newObject[key]));
} else if (!angular.equals(oldObject[key], newObject[key])) {
_results.push(oldObject[key] = newObject[key]);
} else {
_results.push(void 0);
}
}
}
return _results;
};
readArrayCache = function(name, CachedResource) {
return function() {
var arrayInstance, cacheArrayEntry, cacheDeferred, cacheInstanceEntry, cacheInstanceParams, httpDeferred, params, resource, _i, _len, _ref, _ref1;
_ref = processReadArgs(arguments), params = _ref.params, cacheDeferred = _ref.deferred;
httpDeferred = $q.defer();
arrayInstance = new Array();
arrayInstance.$promise = cacheDeferred.promise;
arrayInstance.$httpPromise = httpDeferred.promise;
cacheArrayEntry = new ResourceCacheArrayEntry(CachedResource.$key, params);
resource = CachedResource.$resource[name].apply(CachedResource.$resource, arguments);
resource.$promise.then(function() {
var cachedResourceInstances;
cachedResourceInstances = resource.map(function(resourceInstance) {
return new CachedResource(resourceInstance);
});
arrayInstance.splice.apply(arrayInstance, [0, arrayInstance.length].concat(__slice.call(cachedResourceInstances)));
if (!cacheArrayEntry.value) {
cacheDeferred.resolve(arrayInstance);
}
return httpDeferred.resolve(arrayInstance);
});
resource.$promise["catch"](function(error) {
if (!cacheArrayEntry.value) {
cacheDeferred.reject(error);
}
return httpDeferred.reject(error);
});
arrayInstance.$httpPromise.then(function(response) {
var cacheArrayReferences, cacheInstanceEntry, cacheInstanceParams, instance, _i, _len;
cacheArrayReferences = [];
for (_i = 0, _len = response.length; _i < _len; _i++) {
instance = response[_i];
cacheInstanceParams = instance.$params();
if (Object.keys(cacheInstanceParams).length === 0) {
$log.error("instance " + instance + " doesn't have any boundParams. Please, make sure you specified them in your resource's initialization, f.e. `{id: \"@id\"}`, or it won't be cached.");
} else {
cacheArrayReferences.push(cacheInstanceParams);
cacheInstanceEntry = new ResourceCacheEntry(CachedResource.$key, cacheInstanceParams);
cacheInstanceEntry.set(instance, false);
}
}
return cacheArrayEntry.set(cacheArrayReferences);
});
if (cacheArrayEntry.value) {
_ref1 = cacheArrayEntry.value;
for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
cacheInstanceParams = _ref1[_i];
cacheInstanceEntry = new ResourceCacheEntry(CachedResource.$key, cacheInstanceParams);
arrayInstance.push(new CachedResource(cacheInstanceEntry.value));
}
cacheDeferred.resolve(arrayInstance);
}
return arrayInstance;
};
};
readCache = function(name, CachedResource) {
return function() {
var cacheDeferred, cacheEntry, httpDeferred, instance, params, readHttp, _ref;
_ref = processReadArgs(arguments), params = _ref.params, cacheDeferred = _ref.deferred;
httpDeferred = $q.defer();
instance = new CachedResource({
$promise: cacheDeferred.promise,
$httpPromise: httpDeferred.promise
});
cacheEntry = new ResourceCacheEntry(CachedResource.$key, params);
readHttp = function() {
var resource;
resource = CachedResource.$resource[name].call(CachedResource.$resource, params);
resource.$promise.then(function(response) {
angular.extend(instance, response);
if (!cacheEntry.value) {
cacheDeferred.resolve(instance);
}
httpDeferred.resolve(instance);
return cacheEntry.set(response, false);
});
return resource.$promise["catch"](function(error) {
if (!cacheEntry.value) {
cacheDeferred.reject(error);
}
return httpDeferred.reject(error);
});
};
if (cacheEntry.dirty) {
resourceManager.getQueue(CachedResource).processResource(params, readHttp);
} else {
readHttp();
}
if (cacheEntry.value) {
angular.extend(instance, cacheEntry.value);
cacheDeferred.resolve(instance);
}
return instance;
};
};
writeCache = function(action, CachedResource) {
return function() {
var args, cacheEntry, deferred, error, instanceMethod, param, params, queue, queueDeferred, resource, success, value, _ref;
instanceMethod = this instanceof CachedResource;
args = Array.prototype.slice.call(arguments);
params = !instanceMethod && angular.isObject(args[1]) ? args.shift() : instanceMethod && angular.isObject(args[0]) ? args.shift() : {};
resource = instanceMethod ? this : new CachedResource(args.shift());
success = args[0], error = args[1];
resource.$resolved = false;
_ref = resource.$params();
for (param in _ref) {
value = _ref[param];
params[param] = value;
}
deferred = $q.defer();
resource.$promise = deferred.promise;
if (angular.isFunction(success)) {
deferred.promise.then(success);
}
if (angular.isFunction(error)) {
deferred.promise["catch"](error);
}
cacheEntry = new ResourceCacheEntry(CachedResource.$key, params);
if (!angular.equals(cacheEntry.data, resource)) {
cacheEntry.set(resource, true);
}
queueDeferred = $q.defer();
queueDeferred.promise.then(function(httpResource) {
modifyObjectInPlace(resource, httpResource);
resource.$resolved = true;
return deferred.resolve(resource);
});
queueDeferred.promise["catch"](deferred.reject);
queue = resourceManager.getQueue(CachedResource);
queue.enqueue(params, action, queueDeferred);
queue.flush();
return resource;
};
};
return function() {
var $key, CachedResource, Resource, actions, arg, args, boundParams, handler, isPermissibleBoundValue, name, param, paramDefault, paramDefaults, params, url, _ref;
args = Array.prototype.slice.call(arguments);
$key = args.shift();
url = args.shift();
while (args.length) {
arg = args.pop();
if (angular.isObject(arg[Object.keys(arg)[0]])) {
actions = arg;
} else {
paramDefaults = arg;
}
}
actions = angular.extend({}, DEFAULT_ACTIONS, actions);
if (paramDefaults == null) {
paramDefaults = {};
}
boundParams = {};
for (param in paramDefaults) {
paramDefault = paramDefaults[param];
if (paramDefault[0] === '@') {
boundParams[paramDefault.substr(1)] = param;
}
}
Resource = $resource.call(null, url, paramDefaults, actions);
isPermissibleBoundValue = function(value) {
return angular.isDate(value) || angular.isNumber(value) || angular.isString(value);
};
CachedResource = (function() {
CachedResource.prototype.$cache = true;
function CachedResource(attrs) {
angular.extend(this, attrs);
}
CachedResource.prototype.$params = function() {
var attribute, params;
params = {};
for (attribute in boundParams) {
param = boundParams[attribute];
if (isPermissibleBoundValue(this[attribute])) {
params[param] = this[attribute];
}
}
return params;
};
CachedResource.$resource = Resource;
CachedResource.$key = $key;
return CachedResource;
})();
for (name in actions) {
params = actions[name];
handler = params.method === 'GET' && params.isArray ? readArrayCache(name, CachedResource) : params.method === 'GET' ? readCache(name, CachedResource) : (_ref = params.method) === 'POST' || _ref === 'PUT' || _ref === 'DELETE' ? writeCache(name, CachedResource) : void 0;
CachedResource[name] = handler;
if (params.method !== 'GET') {
CachedResource.prototype["$" + name] = handler;
}
}
resourceManager.add(CachedResource);
resourceManager.flushQueues();
return CachedResource;
};
}
]);
if (typeof module !== "undefined" && module !== null) {
module.exports = app;
}