Permalink
Browse files

feat($cookies): move logic into $cookies and deprecate $cookieStore

The new API on `$cookies` includes:

 * `get`
 * `put`
 * `getObject`
 * `putObject`
 * `getAll`
 * `remove`

The new API no longer polls the browser for changes to the cookies and no longer copy
cookie values onto the `$cookies` object.

The polling is expensive and caused issues with the `$cookies` properties not
synchronizing correctly with the actual browser cookie values.

The reason the polling was originally added was to allow communication between
different tabs, but there are better ways to do this today (for example `localStorage`).

DEPRECATION NOTICE:

`$cookieStore` is now deprecated as all the useful logic
has been moved to `$cookies`, to which `$cookieStore` now simply
delegates calls.

BREAKING CHANGE:

`$cookies` no longer exposes properties that represent the current browser cookie
values. Now you must explicitly the methods described above to access the cookie
values. This also means that you can no longer watch the `$cookies` properties for
changes to the browser's cookies.

This feature is generally only needed if a 3rd party library was programmatically
changing the cookies at runtime. If you rely on this then you must either write code that
can react to the 3rd party library making the changes to cookies or implement your own polling
mechanism.

Closes #6411
Closes #7631
  • Loading branch information...
1 parent 997fdea commit 38fbe3ee8370fc449b82d80df07b5c2ed2cd5fbe @shahata shahata committed with petebacondarwin Mar 2, 2015
Showing with 145 additions and 191 deletions.
  1. +9 −4 src/ngCookies/cookieStore.js
  2. +84 −76 src/ngCookies/cookies.js
  3. +16 −47 test/ngCookies/cookieStoreSpec.js
  4. +36 −64 test/ngCookies/cookiesSpec.js
@@ -4,6 +4,7 @@ angular.module('ngCookies').
/**
* @ngdoc service
* @name $cookieStore
+ * @deprecated
* @requires $cookies
*
* @description
@@ -13,6 +14,11 @@ angular.module('ngCookies').
*
* Requires the {@link ngCookies `ngCookies`} module to be installed.
*
+ * <div class="alert alert-error">
+ * **Note:** The $cookieStore service is deprecated.
+ * Please use the {@link ngCookies.$cookies `$cookies`} service instead.
+ * </div>
+ *
* @example
*
* ```js
@@ -41,8 +47,7 @@ angular.module('ngCookies').
* @returns {Object} Deserialized cookie value, undefined if the cookie does not exist.
*/
get: function(key) {
- var value = $cookies[key];
- return value ? angular.fromJson(value) : value;
+ return $cookies.getObject(key);
},
/**
@@ -56,7 +61,7 @@ angular.module('ngCookies').
* @param {Object} value Value to be stored.
*/
put: function(key, value) {
- $cookies[key] = angular.toJson(value);
+ $cookies.putObject(key, value);
},
/**
@@ -69,7 +74,7 @@ angular.module('ngCookies').
* @param {string} key Id of the key-value pair to delete.
*/
remove: function(key) {
- delete $cookies[key];
+ $cookies.remove(key);
}
};
@@ -25,9 +25,9 @@ angular.module('ngCookies', ['ng']).
* @description
* Provides read/write access to browser's cookies.
*
- * Only a simple Object is exposed and by adding or removing properties to/from this object, new
- * cookies are created/deleted at the end of current $eval.
- * The object's properties can only be strings.
+ * BREAKING CHANGE: `$cookies` no longer exposes properties that represent the
+ * current browser cookie values. Now you must use the get/put/remove/etc. methods
+ * as described below.
*
* Requires the {@link ngCookies `ngCookies`} module to be installed.
*
@@ -37,87 +37,95 @@ angular.module('ngCookies', ['ng']).
* angular.module('cookiesExample', ['ngCookies'])
* .controller('ExampleController', ['$cookies', function($cookies) {
* // Retrieving a cookie
- * var favoriteCookie = $cookies.myFavorite;
+ * var favoriteCookie = $cookies.get('myFavorite');
* // Setting a cookie
- * $cookies.myFavorite = 'oatmeal';
+ * $cookies.put('myFavorite', 'oatmeal');
* }]);
* ```
*/
- factory('$cookies', ['$rootScope', '$browser', '$$cookieReader', '$$cookieWriter', function($rootScope, $browser, $$cookieReader, $$cookieWriter) {
- var cookies = {},
- lastCookies = {},
- lastBrowserCookies,
- runEval = false,
- copy = angular.copy,
- isUndefined = angular.isUndefined;
+ factory('$cookies', ['$$cookieReader', '$$cookieWriter', function($$cookieReader, $$cookieWriter) {
+ return {
+ /**
+ * @ngdoc method
+ * @name $cookies#get
+ *
+ * @description
+ * Returns the value of given cookie key
+ *
+ * @param {string} key Id to use for lookup.
+ * @returns {string} Raw cookie value.
+ */
+ get: function(key) {
+ return $$cookieReader()[key];
+ },
- //creates a poller fn that copies all cookies from the $browser to service & inits the service
- $browser.addPollFn(function() {
- var currentCookies = $$cookieReader();
- if (lastBrowserCookies != currentCookies) { //relies on browser.cookies() impl
- lastBrowserCookies = currentCookies;
- copy(currentCookies, lastCookies);
- copy(currentCookies, cookies);
- if (runEval) $rootScope.$apply();
- }
- })();
-
- runEval = true;
-
- //at the end of each eval, push cookies
- //TODO: this should happen before the "delayed" watches fire, because if some cookies are not
- // strings or browser refuses to store some cookies, we update the model in the push fn.
- $rootScope.$watch(push);
-
- return cookies;
+ /**
+ * @ngdoc method
+ * @name $cookies#getObject
+ *
+ * @description
+ * Returns the deserialized value of given cookie key
+ *
+ * @param {string} key Id to use for lookup.
+ * @returns {Object} Deserialized cookie value.
+ */
+ getObject: function(key) {
+ var value = $$cookieReader()[key];
+ return value ? angular.fromJson(value) : value;
+ },
+ /**
+ * @ngdoc method
+ * @name $cookies#getAll
+ *
+ * @description
+ * Returns a key value object with all the cookies
+ *
+ * @returns {Object} All cookies
+ */
+ getAll: function() {
+ return $$cookieReader();
+ },
- /**
- * Pushes all the cookies from the service to the browser and verifies if all cookies were
- * stored.
- */
- function push() {
- var name,
- value,
- browserCookies,
- updated;
-
- //delete any cookies deleted in $cookies
- for (name in lastCookies) {
- if (isUndefined(cookies[name])) {
- $$cookieWriter(name, undefined);
- }
- }
-
- //update all cookies updated in $cookies
- for (name in cookies) {
- value = cookies[name];
- if (!angular.isString(value)) {
- value = '' + value;
- cookies[name] = value;
- }
- if (value !== lastCookies[name]) {
- $$cookieWriter(name, value);
- updated = true;
- }
- }
+ /**
+ * @ngdoc method
+ * @name $cookies#put
+ *
+ * @description
+ * Sets a value for given cookie key
+ *
+ * @param {string} key Id for the `value`.
+ * @param {string} value Raw value to be stored.
+ */
+ put: function(key, value) {
+ $$cookieWriter(key, value);
+ },
- //verify what was actually stored
- if (updated) {
- updated = false;
- browserCookies = $$cookieReader();
+ /**
+ * @ngdoc method
+ * @name $cookies#putObject
+ *
+ * @description
+ * Serializes and sets a value for given cookie key
+ *
+ * @param {string} key Id for the `value`.
+ * @param {Object} value Value to be stored.
+ */
+ putObject: function(key, value) {
+ $$cookieWriter(key, angular.toJson(value));
+ },
- for (name in cookies) {
- if (cookies[name] !== browserCookies[name]) {
- //delete or reset all cookies that the browser dropped from $cookies
- if (isUndefined(browserCookies[name])) {
- delete cookies[name];
- } else {
- cookies[name] = browserCookies[name];
- }
- updated = true;
- }
- }
+ /**
+ * @ngdoc method
+ * @name $cookies#remove
+ *
+ * @description
+ * Remove given cookie
+ *
+ * @param {string} key Id of the key-value pair to delete.
+ */
+ remove: function(key) {
+ $$cookieWriter(key, undefined);
}
- }
+ };
}]);
@@ -1,58 +1,27 @@
'use strict';
describe('$cookieStore', function() {
- var mockedCookies;
-
- beforeEach(function() {
- var lastCookies = {};
- mockedCookies = {};
- module('ngCookies', {
- $$cookieWriter: function(name, value) {
- mockedCookies[name] = value;
- },
- $$cookieReader: function() {
- if (!angular.equals(lastCookies, mockedCookies)) {
- lastCookies = angular.copy(mockedCookies);
- mockedCookies = angular.copy(mockedCookies);
- }
- return mockedCookies;
- }
- });
- });
-
- it('should serialize objects to json', inject(function($cookieStore, $$cookieReader, $rootScope) {
- $cookieStore.put('objectCookie', {id: 123, name: 'blah'});
- $rootScope.$digest();
- expect($$cookieReader()).toEqual({'objectCookie': '{"id":123,"name":"blah"}'});
+
+ beforeEach(module('ngCookies', {
+ $cookies: jasmine.createSpyObj('$cookies', ['getObject', 'putObject', 'remove'])
}));
- it('should deserialize json to object', inject(function($cookieStore, $browser, $$cookieWriter) {
- $$cookieWriter('objectCookie', '{"id":123,"name":"blah"}');
- $browser.poll();
- expect($cookieStore.get('objectCookie')).toEqual({id: 123, name: 'blah'});
+ it('should get cookie', inject(function($cookieStore, $cookies) {
+ $cookies.getObject.andReturn('value');
+ expect($cookieStore.get('name')).toBe('value');
+ expect($cookies.getObject).toHaveBeenCalledWith('name');
}));
- it('should delete objects from the store when remove is called', inject(function($cookieStore, $browser, $rootScope, $$cookieReader) {
- $cookieStore.put('gonner', { "I'll":"Be Back"});
- $rootScope.$digest(); //force eval in test
- $browser.poll();
- expect($$cookieReader()).toEqual({'gonner': '{"I\'ll":"Be Back"}'});
-
- $cookieStore.remove('gonner');
- $rootScope.$digest();
- expect($$cookieReader()).toEqual({});
+ it('should put cookie', inject(function($cookieStore, $cookies) {
+ $cookieStore.put('name', 'value');
+ expect($cookies.putObject).toHaveBeenCalledWith('name', 'value');
}));
- it('should handle empty string value cookies', inject(function($cookieStore, $browser, $rootScope, $$cookieReader) {
- $cookieStore.put("emptyCookie",'');
- $rootScope.$digest();
- expect($$cookieReader()).
- toEqual({ 'emptyCookie': '""' });
- expect($cookieStore.get("emptyCookie")).toEqual('');
-
- mockedCookies['blankCookie'] = '';
- $browser.poll();
- expect($cookieStore.get("blankCookie")).toEqual('');
+
+
+ it('should remove cookie', inject(function($cookieStore, $cookies) {
+ $cookieStore.remove('name');
+ expect($cookies.remove).toHaveBeenCalledWith('name');
}));
-});
+ });
Oops, something went wrong.

0 comments on commit 38fbe3e

Please sign in to comment.