Browse files

- Adding support for a callback function called 'expireOnFailure' tha…

…t allows the promise owner to forcefully expire the promise cache on failure scenarios (like non 200 responses from a server)

- Updated example and tests to accommodate new functionality

- Added support for istanbul code coverage
  • Loading branch information...
1 parent ca3bda6 commit 74ff560efebd1573ca435416a12123415f37161e Chris Roberson committed Nov 12, 2013
Showing with 103 additions and 35 deletions.
  1. +24 −3 angular-promise-cache.js
  2. +2 −2 angular-promise-cache.min.js
  3. +36 −24 angular-promise-cache.test.js
  4. +17 −4 example/example.html
  5. +11 −2 karma.conf.js
  6. +13 −0 package.json
View
27 angular-promise-cache.js
@@ -51,9 +51,13 @@ angular.module('angular-promise-cache', [])
var promise = opts.promise,
ttl = parseInt(opts.ttl),
bustCache = !!opts.bustCache,
+ // v0.0.3: Adding ability to specify a callback function to forcefully expire the cache
+ // for a promise that returns a failure
+ expireOnFailure = opts.expireOnFailure,
args = opts.args,
now = new Date().getTime(),
- strPromise = opts.key || promise.toString().replace(whitespaceRegex, '');
+ strPromise = opts.key || promise.toString().replace(whitespaceRegex, ''),
+ ret;
dateReference = dateReference || now;
@@ -66,6 +70,7 @@ angular.module('angular-promise-cache', [])
memos[strPromise].cache = (function() {
var updatedCache = {},
cache = memos[strPromise].cache,
+ forceExpiration = !!memos[strPromise].forceExpiration,
key,
parts,
timestamp,
@@ -74,7 +79,7 @@ angular.module('angular-promise-cache', [])
for (key in cache) {
parts = key.split(keyDelimiter);
timestamp = parseInt(parts[1]);
- omit = bustCache || timestamp + (ttl || DEFAULT_TTL_IN_MS) < now;
+ omit = bustCache || forceExpiration || timestamp + (ttl || DEFAULT_TTL_IN_MS) < now;
if (omit) {
dateReference = now;
@@ -84,10 +89,26 @@ angular.module('angular-promise-cache', [])
}
}
+ // Always reset this after expiring the cache
+ // so it is not "stuck on"
+ memos[strPromise].forceExpiration = false;
+
return updatedCache;
}());
}
- return memos[strPromise].apply(this, args);
+ ret = memos[strPromise].apply(this, args);
+ if (angular.isFunction(expireOnFailure)) {
+ ret.then(
+ angular.noop,
+ function() {
+ if (expireOnFailure.apply(this, arguments)) {
+ memos[strPromise].forceExpiration = true;
+ }
+ return arguments;
+ }
+ );
+ }
+ return ret;
}
});
View
4 angular-promise-cache.min.js
@@ -20,5 +20,5 @@ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
-angular.module("angular-promise-cache",[]).factory("promiseCache",function(){var b={},m=/\s+/g,e,p="undefined"!==typeof _&&hasOwnProperty.call(_,"memoize")?_.memoize:function(a,b){var e=+new Date+"",f=function(){var c=f.cache,d=b?b.apply(this,arguments):e+arguments[0];return hasOwnProperty.call(c,d)?c[d]:c[d]=a.apply(this,arguments)};f.cache={};return f};return function(a){var l=a.promise,n=parseInt(a.ttl),f=!!a.bustCache,c=a.args,d=(new Date).getTime(),g=a.key||l.toString().replace(m,"");e=e||d;
-hasOwnProperty.call(b,g)?b[g].cache=function(){var a={},c=b[g].cache,k,h;for(k in c)h=k.split("$"),h=parseInt(h[1]),(h=f||h+(n||5E3)<d)?e=d:a[k]=c[k];return a}():b[g]=p(l,function(){return"$"+e+"$"+Array.prototype.slice.call(arguments)});return b[g].apply(this,c)}});
+angular.module("angular-promise-cache",[]).factory("promiseCache",function(){var b={},n=/\s+/g,f,q="undefined"!==typeof _&&hasOwnProperty.call(_,"memoize")?_.memoize:function(a,b){var f=+new Date+"",g=function(){var c=g.cache,e=b?b.apply(this,arguments):f+arguments[0];return hasOwnProperty.call(c,e)?c[e]:c[e]=a.apply(this,arguments)};g.cache={};return g};return function(a){var m=a.promise,p=parseInt(a.ttl),g=!!a.bustCache,c=a.expireOnFailure,e=a.args,l=(new Date).getTime(),d=a.key||m.toString().replace(n,
+"");f=f||l;hasOwnProperty.call(b,d)?b[d].cache=function(){var a={},c=b[d].cache,e=!!b[d].forceExpiration,k,h;for(k in c)h=k.split("$"),h=parseInt(h[1]),(h=g||e||h+(p||5E3)<l)?f=l:a[k]=c[k];b[d].forceExpiration=!1;return a}():b[d]=q(m,function(){return"$"+f+"$"+Array.prototype.slice.call(arguments)});a=b[d].apply(this,e);angular.isFunction(c)&&a.then(angular.noop,function(){c.apply(this,arguments)&&(b[d].forceExpiration=!0);return arguments});return a}});
View
60 angular-promise-cache.test.js
@@ -3,15 +3,13 @@
describe('angular-promise-cache', function() {
var apc,
q,
- scope,
- state;
+ scope;
beforeEach(module('angular-promise-cache'));
- beforeEach(inject(function($q, $rootScope, promiseCache, promiseCacheState) {
+ beforeEach(inject(function($q, $rootScope, promiseCache) {
q = $q;
scope = $rootScope;
apc = promiseCache;
- state = promiseCacheState;
}));
it('should be a function', function() {
@@ -83,7 +81,7 @@ describe('angular-promise-cache', function() {
runs(function() {
apc({ promise: getPromise, ttl: 1000 }).then(function(idx) { expect(idx).toBe(2); });
scope.$apply();
- });
+ });
});
it('should not expire too early', function() {
@@ -104,36 +102,50 @@ describe('angular-promise-cache', function() {
runs(function() {
apc({ promise: getPromise, ttl: 1000 }).then(function(idx) { expect(idx).toBe(1); });
scope.$apply();
- });
+ });
});
- it('should set the state', function() {
- var calls = 0;
- function getPromise() {
- var deferred = q.defer();
- deferred.resolve(++calls);
- return deferred.promise;
- }
+ it('should support manually expiring the cache', function() {
+ var calls = 0,
+ obj = {
+ promise: function() {
+ var deferred = q.defer();
+ if (++calls === 1) {
+ deferred.reject(calls);
+ }
+ else {
+ deferred.resolve(calls);
+ }
+ return deferred.promise;
+ },
+ ttl: 1000,
+ expireOnFailure: function(request) {
+ return true;
+ }
+ },
+ fns = {
+ one: function(idx) {
+ expect(idx).toBe(1);
+ },
+ two: function(idx) {
+ expect(idx).toBe(2);
+ }
+ };
runs(function() {
- apc({ promise: getPromise, key: 'apc', ttl: 1000 }).then(function(idx) { expect(idx).toBe(1); });
+ spyOn(fns, 'one').andCallThrough();
+ apc(obj).then(angular.noop, fns.one);
scope.$apply();
+ expect(fns.one).toHaveBeenCalled();
});
waits(500);
runs(function() {
- apc({ promise: getPromise, key: 'apc', ttl: 1000 })
- expect(state.state('apc').expired).toBe(false);
- expect(state.state('apc').ttl < 500).toBe(true);
- });
-
- waits(500);
-
- runs(function() {
- apc({ promise: getPromise, key: 'apc', ttl: 1000 });
+ spyOn(fns, 'two').andCallThrough();
+ apc(obj).then(fns.two);
scope.$apply();
- expect(state.state('apc').expired).toBe(true);
+ expect(fns.two).toHaveBeenCalled();
});
});
});
View
21 example/example.html
@@ -25,7 +25,7 @@
clearInterval(interval);
};
}
- </script>
+ </script>
</head>
<body ng-app="myAwesomeApp">
<div ng-controller="simpleXHRRequestCtrl" class="well">
@@ -56,9 +56,22 @@
// Actions
$scope.send = function() {
- promiseCache({ promise: makeXhrRequest, ttl: ttl }).then(function() {
- $scope.numberOfResolvedPromises++;
- });
+ promiseCache({
+ promise: makeXhrRequest,
+ ttl: ttl,
+ // New feature in v0.0.3 - supports forcefully expiring the promise
+ // cache on a failure if this function returns true
+ expireOnFailure: function(request) {
+ return request.status !== 200;
+ }
+ }).then(
+ function() {
+ $scope.numberOfResolvedPromises++;
+ },
+ function() {
+ $scope.numberOfResolvedPromises++;
+ }
+ );
};
$scope.reset = function() {
$scope.numberOfHttpRequests = 0;
View
13 karma.conf.js
@@ -23,13 +23,22 @@ module.exports = function(config) {
// list of files to exclude
exclude: [
-
+
],
+ preprocessors: {
+ 'angular-promise-cache.js': 'coverage'
+ },
+
// test results reporter to use
// possible values: 'dots', 'progress', 'junit', 'growl', 'coverage'
- reporters: ['progress'],
+ reporters: ['coverage', 'progress'],
+
+ coverageReporter: {
+ type : 'html',
+ dir : 'coverage/'
+ },
// web server port
View
13 package.json
@@ -22,5 +22,18 @@
"license": "MIT",
"bugs": {
"url": "https://github.com/chrisronline/angular-promise-cache/issues"
+ },
+ "devDependencies": {
+ "istanbul": "~0.1.44",
+ "karma-script-launcher": "~0.1.0",
+ "karma-firefox-launcher": "~0.1.0",
+ "karma-chrome-launcher": "~0.1.0",
+ "karma-html2js-preprocessor": "~0.1.0",
+ "karma-jasmine": "~0.1.3",
+ "karma-requirejs": "~0.1.0",
+ "karma-coffee-preprocessor": "~0.1.0",
+ "karma-phantomjs-launcher": "~0.1.0",
+ "karma": "~0.10.4",
+ "karma-coverage": "~0.1.2"
}
}

0 comments on commit 74ff560

Please sign in to comment.