FlashRAM
|
ColdBox Flash RAM
Overview
The purpose of the Flash RAM is to allow variables to be persisted seamlessly from one request and be picked up in a subsequent request(s) by the same user. This allows you to hide implementation variables and create web flows or conversations in your ColdBox applications. So why not just use session or client variables? Well, most developers forget to clean them up and sometimes they just end up overtaking huge amounts of RAM and no clean cut definition is found for them. With Flash RAM, you have the facility already provided to you in an abstract and encapsulated format. This way, if you need to change your flows storage from session to client, the change is seamless and painless.
ColdBox has Flash RAM capabilities since version 2.0 (long long time ago). However, since version 3.0.0 every handler, plugin, interceptor, layout and view has a flash object in their variables scope already configured for usage. The entire flash RAM capabilities are encapsulated in a flash object that you can use in your entire ColdBox code. Not only that, but the ColdBox Flash object is based on an interface and you can build your own Flash RAM implementations very easily. What this means is that you can build a Flash RAM implementation that can store the Flashed variables wherever you want.
Our Flash Scope also can help you maintain flows or conversations between requests by using the discard() and keep() methods. This will continue to expand in our 3.X releases as we build our own conversations DSL to define programmatically wizard like or complex web conversations. Also, the ColdBox Flash RAM has the capability to not only maintain this persistence scope but it also allows you to seamlessly re-inflate these variables into the request collection or private request collection or both.
Flash Storage
There are times where you need to store user related variables in some kind of permanent storage then relocate the user into another section of your application, be able to retrieve the data, use it and then clean it. All of these tedious operations are definitely doable by why reinvent the wheel if we can have the platform give us a tool for maintaing conversation variables across requests. The key point for Flash RAM is where will the data be stored so that it is unique per user. ColdFusion gives us several persistent scopes that we can use and we have also created several storages for this purpose. Since the ColdBox flash scope is based on an interface, the flash scope storage can be virtually anywhere. The included implementations for ColdBox are:
| Name | Class | Description |
|---|---|---|
| Session | coldbox.system.web.flash.SessionFlash | Persists variables in session scope |
| Cluster | coldbox.system.web.flash.ClusterFlash | Persists variables in cluster scope via Railo only |
| Client | coldbox.system.web.flash.ClientFlash | Persists variables in client scope |
| Mock | coldbox.system.web.flash.MockFlash | Mocks the storage of Flashed variables. Great for unit/integration testing. |
| ColdboxCache | coldbox.system.web.flash.ColdboxCacheFlash | Persists variables in the ColdBox Cache |
You will find all of these implementations in the following package: coldbox.system.web.flash. In order to choose what implementation to use in your application you need to tell the configuration by using the coldbox directive: FlashURLPersistScope:
coldbox = {
// Flash scope
FlashURLPersistScope = "Client"
};
//XML
<Setting name="FlashURLPersistScope" value="Client" />
Basically the value of the setting can be from our covered implementations or it can also be a full CFC path. If you use a CFC path that means that you will be using your own Flash Scope implementation.
coldbox = {
// Flash scope
FlashURLPersistScope = "myapp.model.storage.EHCacheFlash"
};
Extra Configuration Settings
Each RAM implementation can also use custom application settings in order to alter its behavior. Below you will find tables for each of the application settings these flash implementations utilize:
SessionFlash Settings:
- none
ClusterFlash Settings:
- none
ClientFlash Settings:
- none
MockFlash Settings:
- none
ColdboxCacheFlash Settings:
| Setting | Type | Required | Default | Description |
|---|---|---|---|---|
| flashRAM_cacheName | string | false | default | The cache provider name declared in CacheBox to be used to store the user's flash RAM content. |
Using Flash RAM
There are several ways to interact with the ColdBox Flash RAM:
- Using the flash scope object (Best Practice)
- Using the persistVariables() method from the super type and coldbox controller (coldbox.system.web.Controller)
- Using the persistence arguments in the setNextEvent() method from the super type and coldbox controller (coldbox.system.web.Controller)
All of these methods interact with the Flash RAM object but the last two methods not only place variables in the temporary storage bin but actualy serialize the data into the Flash RAM storage immediately. The first approach queues up the variables for serialization, thus saving precious serialization time. In the next section we will learn what all of this means.
Flash Scope Object
The flash scope object is our best practice approach as it clearly demarcates the code that the developer is using the flash scope for persistence. Any flash scope must inherit from our AbstractFlashScope and has access to several key methods that we will cover in this section. However, let's start with how the flash scope stores data.
- The flash persistence methods are called for saving data, the data is stored in an internal temporary request bin and awaiting serialization and persistence
- If the flash methods are called with immediate save, then the data is immediately serialized and stored in the implementation's persistent storage
- If the flash's saveFlash() method is called then serialize the temporary request bin and persist in the implementation's persistent storage
- If the application relocates via setNextEvent() then serialize the temporary request bin and persist in the implementation's persistent storage
As you can see, every time you put something into the flash scope, you have a choice of storing it in the persistent storage immediately or waiting for a relocation. By default, the flash RAM waits for a relocation (via setNextEvent(),setNextRoute(),relocate()) in order to queue up all the storage and serialize only once. However, if you will not be doing a relocation and you just need to save and persist, then use the saveNow arguments or directly call the saveFlash() method.
Important : By default the Flash RAM queues up serializations for better performance.
Note : If you use the persistVariables() method or any of the persistence arguments on the setNextEvent() method, those variables will be saved and persisted immediately.
To review the Flash Scope methods, please go to the API and look for the correct implementation or the AbstractFlashScope. Below are the main methods that you can use to interact with the Flash RAM object:
clear()
Clears the temporary storage bin
clearFlash()
Clears the persistence flash storage implementation
discard()
Method : discard([string keys=])
Discards all or some keys from flash storage. You can use this method to implement flows.
Example
// discard all flash variables flash.discard(); // dicard some keys flash.discard('userID,userKey,cardID');
exists()
Method : exists(name)
Checks if a key exists in the flash storage
Example
if( flash.exists('notice') ){ // do something }
get()
Method : get(name,[default])
Get's a value from flash storage and you can even pass a default value if it does not exist.
Example
// Get a flash key that you know exists cardID = flash.get("cardID"); // Render some flash content <div class="notice">#flash.get("notice","")#</div>
getKeys()
Get a list of all the objects in the temp flash scope.
getFlash()
Get a reference to the permanent storage implementation of the flash scope.
getScope()
Get the flash temp request storage used throughout a request until flashed at the end of a request.
isEmpty()
Check if the flash scope is empty or not
keep()
Method : keep([string keys=])
Keep all or a single flash temp variable alive for another relocation. Usually called from interceptors or event handlers to create conversations and flows of data from event to event.
Example
function step2(event){ // keep variables for step 2 wizard flash.keep('userID,fname,lname'); // keep al variables flash.keep(); }
persistRC()
Method : persistRC([include=], [exclude=], [saveNow='false'])
Persist keys from the coldbox request collection in flash scope. If using exclude, then it will try to persist the entire rc but excluding certain keys. Including will only include the keys passed.
Example
// persist some variables that can be reinflated into the RC upon relocation flash.persistRC(include="name,email,address"); setNextEvent('wizard.step2'); // persist all RC variables using exclusions that can be reinflated into the RC upon relocation flash.persistRC(exclude="ouser"); setNextEvent('wizard.step2'); // persist some variables that can be reinflated into the RC upon relocation and serialize immediately flash.persistRC(include="email,addressData",savenow=true);
put()
Method : put(name, value, [saveNow='false'], [keep='true'], [inflateToRC='true'], [inflateToPRC='false'], [boolean autoPurge='true'])
Put an object in temp flash scope and choose whether to reinflate them immediately to the RC or Private RC collection. Please note that some of arguments affect the persistence of the variable placed in flash RAM. By default all variables placed in flash RAM are automatically purged in the next request once they are inflated UNLESS you use the keep() methods in order to persist them longer or create flows. However, you can also use the autoPurge argument and set it to false so you can control when the variables will be removed from flash RAM. Basically a glorified ColdFusion scope that you can use.
Example
// persist some variables that can be reinflated into the RC upon relocation flash.put(name="userData",value=userData); setNextEvent('wizard.step2'); // put and serialize immediately. flash.put(name="userData",value=userData,saveNow=true); // put and mark them to be reinflated into the PRC only flash.put(name="userData",value=userData,inflateToRC=false,inflateToPRC=true); // put and disable autoPurge, I will remove it when I want to! flash.put(name="userData",value=userWizard,autoPurge=false);
putAll()
Method : putAll(map, [saveNow='false'], [keep='true'], [inflateToRC='true'], [inflateToPRC='false'],[boolean autoPurge='true'])
Put a map of name-value pairs into the flash scope using the passed in arguments
Example
var map = { addressData = rc.address, userID = securityService.getUserID(), loggedIn = now() }; // put all the variables in flash scope flash.putAll(map);
remove()
Method : remove(name, [saveNow='false'])
Remove an object from the temporary flash scope so when the flash scope is serialized it will not be serialized. If you would like to remove a key from the flash scope and make sure your changes are reflected in the persistence storage immediately, use the saveNow argument.
Example
// mark object for removal flash.remove('notice'); // mark object and remove immediately from flash storage flash.remove('notice',true);
saveFlash()
Save the flash storage immediately. This process looks at the temporary request flash scope and serializes if it needs to and persists to the correct flash storage
size()
Get the size of the items in flash scope
Some Examples
// handler code: function saveForm(event){ // save post flash.put("notice","Saved the form baby!"); // relocate to another event setNextEvent('user.show'); } function show(event){ // Nothing to do with flash, inflating by flash object automatically event.setView('user.show'); } // User/show.cfm template using if statements <cfif flash.exists("notice")> <div class="notice">#flash.get("notice")#</div> </cfif> // User/show.cfm using defaults <div class="notice">#flash.get(name="notice",default="")</div>
Creating Your Own Flash Scope
The ColdBox Flash capabilities are very flexible and you can easily create your own Flash Implementations by doing two things:
- Create a CFC that inherits from coldbox.system.web.flash.AbstractFlashScope
- Implement the following functions: clearFlash(), saveFlash(), flashExists(), and getFlash()
Implementable Methods
| Method | ReturnType | Description |
|---|---|---|
| clearFlash() | void | Will destroy or clear the entire flash storage structure. |
| saveFlash() | void | Will be called before relocations or on demand in order to flash the storage. This method usually talks to the getScope() method to retrieve the temporary flash variables and then serialize and persist. |
| flashExists() | boolean | Checks if the flash storage is available and has data in it. |
| getFlash() | struct | This method needs to return a structure of flash data to reinflate and use during a request. |
All of the methods must be implemented and they have their unique purposes as you read in the description. Let's see a real life example, below you can see the flash implementation for the session scope:
<cfcomponent output="false" extends="coldbox.system.web.flash.AbstractFlashScope" hint="A ColdBox session flash scope"> <-------------------------------------------- CONSTRUCTOR ------------------------------------------> <cfscript> instance = structnew(); </cfscript> <--- init ---> <cffunction name="init" output="false" access="public" returntype="SessionFlash" hint="Constructor"> <cfargument name="controller" type="coldbox.system.web.Controller" required="true" hint="The ColdBox Controller"/> <cfscript> super.init(arguments.controller); instance.flashKey = "cbox_flash_scope"; return this; </cfscript> </cffunction> <-------------------------------------------- IMPLEMENTED METHODS ------------------------------------------> <--- getFlashKey ---> <cffunction name="getFlashKey" output="false" access="public" returntype="string" hint="Get the flash key storage used in session scope."> <cfreturn instance.flashKey> </cffunction> <--- clearFlash ---> <cffunction name="clearFlash" output="false" access="public" returntype="void" hint="Clear the flash storage"> <cfif flashExists()> <cfset structClear(session[getFlashKey()])> </cfif> </cffunction> <--- saveFlash ---> <cffunction name="saveFlash" output="false" access="public" returntype="void" hint="Save the flash storage in preparing to go to the next request"> <--- Init The Storage if not Created ---> <cfif NOT flashExists()> <cflock scope="session" throwontimeout="true" timeout="20"> <cfif NOT flashExists()> <cfset session[getFlashKey()] = structNew()> </cfif> </cflock> </cfif> <--- Now Save the Storage ---> <cfset session[getFlashKey()] = getScope()> </cffunction> <--- flashExists ---> <cffunction name="flashExists" output="false" access="public" returntype="boolean" hint="Checks if the flash storage exists and IT HAS DATA to inflate."> <cfscript> // Check if session is defined first if( NOT isDefined("session") ) { return false; } // Check if storage is set and not empty return ( structKeyExists(session, getFlashKey()) AND NOT structIsEmpty(session[getFlashKey()]) ); </cfscript> </cffunction> <--- getFlash ---> <cffunction name="getFlash" output="false" access="public" returntype="struct" hint="Get the flash storage structure to inflate it."> <--- Check if Exists, else return empty struct ---> <cfif flashExists()> <cfreturn session[getFlashKey()]> </cfif> <cfreturn structnew()> </cffunction> </cfcomponent>
As you can see from the implementation, it is very straightforward to create a useful session flash RAM object. You can also get more funky and use some ColdBox internal serializers for any kind of object:
<cfcomponent output="false" extends="coldbox.system.web.flash.AbstractFlashScope" hint="A ColdBox client flash scope"> <-------------------------------------------- CONSTRUCTOR ------------------------------------------> <cfscript> instance = structnew(); </cfscript> <--- init ---> <cffunction name="init" output="false" access="public" returntype="ClientFlash" hint="Constructor"> <cfargument name="controller" type="coldbox.system.web.Controller" required="true" hint="The ColdBox Controller"/> <cfscript> super.init(arguments.controller); // Marshaller instance.converter = createObject("component","coldbox.system.core.conversion.ObjectMarshaller").init(); instance.flashKey = "cbox_flash"; return this; </cfscript> </cffunction> <-------------------------------------------- PUBLIC ------------------------------------------> <--- getFlashKey ---> <cffunction name="getFlashKey" output="false" access="public" returntype="string" hint="Get the flash key storage used in cluster scope."> <cfreturn instance.flashKey> </cffunction> <--- clearFlash ---> <cffunction name="clearFlash" output="false" access="public" returntype="void" hint="Clear the flash storage"> <cfif flashExists()> <cfset structDelete(client,getFlashKey())> </cfif> </cffunction> <--- saveFlash ---> <cffunction name="saveFlash" output="false" access="public" returntype="void" hint="Save the flash storage in preparing to go to the next request"> <cfset client[getFlashKey()] = instance.converter.serializeObject( getScope() )> </cffunction> <--- flashExists ---> <cffunction name="flashExists" output="false" access="public" returntype="boolean" hint="Checks if the flash storage exists and IT HAS DATA to inflate."> <cfscript> // Check if session is defined first if( NOT isDefined("client") ) { return false; } // Check if storage is set return ( structKeyExists(client, getFlashKey()) ); </cfscript> </cffunction> <--- getFlash ---> <cffunction name="getFlash" output="false" access="public" returntype="struct" hint="Get the flash storage structure to inflate it."> <--- Check if Exists, else return empty struct ---> <cfif flashExists()> <cfreturn instance.converter.deserializeObject(client[getFlashKey()])> </cfif> <cfreturn structnew()> </cffunction> </cfcomponent>
Summary
The ColdBox Flash RAM is an excellent utility to help you create basic or even complex conversations between events and be able to persist data across requests. I really encourage you to start using the Flash RAM capabilities and go funky with them. Expect great things to come as we develop our webflow and conversation DSL language in versions to come.
Brett said
at 04:15:17 PM 14-Jan-2011
Richard Davies said
at 01:11:55 PM 24-Mar-2011
Luis Majano said
at 01:45:04 PM 24-Mar-2011
Joseph Lamoree said
at 10:33:47 PM 26-Jan-2012

SideBar
User Login 




Comments (