HiveBrain v1.2.0
Get Started
← Back to all entries
patternjavascriptMinor

Lightweight Cookiemanager

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
lightweightcookiemanagerstackoverflow

Problem

I've been working on a lightweight cookiemanager and version 1 has been completed and works perfectly as intended. I would love to get a review to see where we can improve it.

Where should I throw logical errors? For now I only log.

Its usage is as follows:

Getting cookies can be done by calling Cookiemanger.get(). You can supply a name argument to get a specific cookie, if no name is supplied it will return all cookies currently in the document. An additional flag can be set as a second argument whether to use strict matching on the cookies name.

Cookiemanager.get(); // returns all cookies currently defined in the document as a name/value object.
Cookiemanager.get('your_cookie_name'); // returns cookie(s) as a name/value object, using loose matching. so 'your_cookie_name_123' will get returned also.
Cookiemanager.get('your_cookie_name', true); // Same as above only now it will do strict name matching.


Setting cookies can be done by calling Cookiemanger.set(). It requires a settings object with at least a name key/value supplied.

// Simple setting
Cookiemanger.set({
    name : 'your_cookie_name',
    value : 'your value'
});

// Extended setting
Cookiemanger.set({
    name : 'your_cookie_name',
    value : 'your value',
    expires : 'a expiry date in as a UTC string'
    path : '/',
    domain : document.location.host,
    secure : false
});


Removing cookies can be done by calling Cookiemager.remove(). It accepts 3 arguments: name, domain and path as a string. domain and path are optional. If none are supplied, it will remove all cookies from the domain including all sub paths.

// Removes the cookie at the current domain, including all sub paths.
Cookiemanger.remove({
     name : 'your_cookie_name',
     domain : document.location.host
});

// Removes the cookie at the supplied path, excluding sub paths.
Cookiemanger.remove({
     name : 'your_cookie_name',
     path: '/'
});


Testing a cookie against a certain value

Solution

For a "lightweight" cookie manager, there seems to be rather a lot going on in my opinion. And I'd call the object CookieManager since it's two words, but that's not structurally/syntactically important.

I am however a bit suspicious of the get() usage. If I give it a string, and nothing else, I'd expect that to do strict name-matching. I.e. given a name, I expect zero or 1 cookie in return, not a list. Maybe break out the "fuzzy matching" into a find() method, or have get() accept a regular expression instead.

Speaking of regular expressions, that's probably the easiest way to parse the document.cookie string:

function all() {
  var cookies = {};
  // using replace, as it accepts a function
  document.cookie.replace(/([^=]+)=([^;]*)[;\s]*/g, function(match, name, value) {
    cookies[name] = value;
  });
  return cookies;
}


That'll get you an object (or hash or map if you prefer) of all the cookies. You might also expose that as CookieManager.all() instead of overloading the get() method with yet another usage.

Internally, get() can use all() to get the individual cookies, and basically become a very simply shortcut

function get(name) {
  return all()[name];
}


I wouldn't bother logging errors within the cookie manager, as you'll still have to check the return value elsewhere. In this case, get() will simply return a string value or undefined. GIGO: Garbage in, garbage out.

Now, for fuzzy name-matching, I'd add a find() function to have something distinct from the stricter get() function. Something like

function find(pattern) {
  var name,
      found = {},
      cookies = all();
  if( pattern instanceof RegExp ) {
    for( name in cookies ) {
      if( pattern.test(name) ) {
        found[name] = cookies[name];
      }
    }
  } else {
    for( name in cookies ) {
      if( name.indexOf(pattern) !== -1 ) {
        found[name] = cookies[name];
      }
    }
  }
  return found;
}


Once again, GIGO. Not necessarily your responsibility to check the arguments.

Lastly, I'd probably have set() accept specific arguments, rather than an object simply because it makes its usage more obvious. If I just have to pass an object, I still have to know what that object should contain.

Another reason would be to keep it consistent with the remove() function, which takes specific arguments already.

set() and remove() are the two functions that should probably do some input-checking and maybe throw an exception. This is mostly as they can't really follow the GIGO approach, because they don't return something. So it's more like "garbage in, throw exception"

Again, not much reason to attempt to log, just to risk throwing an exception there; probably easier to just throw if the input is spurious.

Code Snippets

function all() {
  var cookies = {};
  // using replace, as it accepts a function
  document.cookie.replace(/([^=]+)=([^;]*)[;\s]*/g, function(match, name, value) {
    cookies[name] = value;
  });
  return cookies;
}
function get(name) {
  return all()[name];
}
function find(pattern) {
  var name,
      found = {},
      cookies = all();
  if( pattern instanceof RegExp ) {
    for( name in cookies ) {
      if( pattern.test(name) ) {
        found[name] = cookies[name];
      }
    }
  } else {
    for( name in cookies ) {
      if( name.indexOf(pattern) !== -1 ) {
        found[name] = cookies[name];
      }
    }
  }
  return found;
}

Context

StackExchange Code Review Q#24513, answer score: 4

Revisions (0)

No revisions yet.