Coldbox has the cbstorages module which can be used as an API for accessing persistent storage such as cookie, session, application, cache and more. The question is: why should I use such storage if there is cfcookie or something simple as as session struct?

Let me explain with some code. I was working on updating the cbi18n module where we can use session, client, cookie or request scope to store the currently selected locale. In code this is getting ugly soon, with code like this

switch(instance.localeStorage){
  case "session" : { storage = session; break; }
  case "client"  : { storage = client; break;  }
  case "cookie"  : { storage = cookie; break;  }
  case "request" : { storage = request; break; }
}

and in other places in the code:

<!--- Storage of the Locale in the user storage --->
<cfif instance.localeStorage eq "session">
  <cfset session.DefaultLocale = arguments.locale>
<cfelseif instance.localeStorage eq "client">
  <cfset client.DefaultLocale = arguments.locale>
<cfelseif instance.localeStorage eq "request">
  <cfset request.DefaultLocale = arguments.locale>
<cfelse>
  <cfcookie name="DefaultLocale" value="#arguments.locale#" />
</cfif>

So lots of switch and if statements. And just to remind you: most of this code is more than a decade old, so there is quite some room for improvement. Let’s say we want to store the user preferences in some caching system, of in a database: in that case we have to modify the module in many places to add the new functionality. We can do better, and that’s where cbstorages can shine:

  • cbstorages has a unified API, so getting or setting values is the same. You don’t have to remember specific syntax for your cookies or caching system, there are general methods for getting, setting or clearing values, checking for existence and more.
  • the nice thing: is is pluggable, so you can easily swap a sessionStorage for a distributed cache or cookieStorage. In the case of cbi18n or for example cbauth you can just specify a different storage mechanism in your module configuration, and you don’t have to change the module code. You could even create your own storage mechanisms, as long as it conforms to the same API.
  • configurability: let’s say you have special requirements for your cookies like encryption or ssl only. Again, you don’t have to change the module code, just make sure you make the necessary changes in your module configuration.

So what does this mean for the module code? Because of this unified API we don’t have to write switch or if statements anymore. It is simplified to:

// getting value from storage
return variables.storageService.get( 
    "currentLocale", variables.settings.defaultLocale );

// setting a value
variables.storageService.set( "currentLocale", arguments.locale );

So it doesn’t matter anymore if it is sessions or cookies, we just instantiate the selected storage type. And if you decide you want to store your locale setting somewhere else you. can use your own cbstorage type.

// specify storageType in settings
moduleSettings = {
  cbi18n : {
    localeStorage : "cookieStorage@cbstorages",
//..... etc

// module instantiates selected storage type
variables.storageService = wirebox.getInstance( variables.localeStorage );

For configurability cbstorages is also a big plus: There was an issue for people who had secure cookies as a requirement. You can imagine how much conditional code you can write if you you have to apply this to cookies only. Now you can just specify this in the cookiestorage@cbstorages settings, and again: no code changes required.

So was it al that easy? To be honest: No! When I just swapped cfcookie for cookiestorage in this module, my language settings where gone. It took me a while to discover: the cookiestorage default settings where not the same as those for cfcookie. So this simple use case was not the same:

cfcookie(name="localeStorage", value=myValue)
//was not the same as
cookieStorage.set("localeStorage", myValue)

Both cfcookie and cookieStorage have more attributes, such as expires. If you don’t specify this, a cookie will be saved as long as your browser is open. If you don’t specify the expires attribute in cookieStorage it will default to 0. I didn’t know yet, but this seems just as effective as immediately deleting the cookie, which is not very useful if you want to set a value. In the latest release (2.4.0) this has been fixed by removing the default value for the expires attribute.

Sometimes I am wondering why I am discovering these kind of issues. It is quite common to store cookies without a default value, so… I guess people should use cfml and these modules more often!