Apr 26, 2011

Connect Multiple Browsers using JavaScript

When searching for answers to development issues, I often land on Ben Nadel's Blog.

I landed there today and subsequently stumbled upon the most glorious JavaScript post I've ever seen in my entire life.

Ben demonstrates how to communicate with multiple client browsers using JAVASCRIPT! That's right. Imagine you and your friend have the same website pulled up. Then imagine you click on text field and started typing... and watching your friend's browser update INSTANTLY. No joke.

Ben shows us more here: http://www.bennadel.com/blog/2171-Realtime-Messaging-And-Synchronization-With-NowJS-And-Node-js.htm



Thanks, Ben!

Apr 20, 2011

Dynamic Image Placeholders (or what I like to call 'Image Ipsum')


Ever struggle to find a generic image placeholder of a web design during development?

Two fantastic sites you need to checkout:

http://kittenholder.com

http://flickholdr.com

Both services allow you to paste a URI like "http://flickholdr.com/980/220/balls" into your HTML and get back a random JPEG. In the above URI, 980 is width, 220 is height and 'balls' is a search parameter.

One suggestion for kittenholder.com - rename your website to loremkitten.com ;)

Apr 14, 2011

Caching JSON Requests

ShopWarrens.com is a retail website I've been developing for a couple of months.  The latest version was designed specifically with speed in mind.  We want customers to be able to find exactly what they are looking for quickly and intuitively.

The site is being served from a shared environment.  While its very cost effective, the database performance is less than ideal.  We've done our best to keep db processing to a minimum.  But with every request for a product list comes yet another hit against the database... and they were sometimes very costly hits.

I'd eventually had it with the intermitent performance issues and said to myself - 'there's got to be a way to make these requests load faster for ALL visitors'.  I want peoples minds to be BLOWN away at how fast the web experience is.

Then it hit me... all I'm returning to the browser is a JSON string.  What if that JSON string was cached in 6hr intervals?  Then the product lists would load INSTANTLY... no waiting for db processing...  the server can respond as fast as possible with the appropriate JSON response.

My solution was build in ColdFusion (hangs head in embarrassment) but the same could be applied to any server language.

Here's how it works...

Here's my server directory structure:
   ajax/products.get.cfm
   ajax/products.list.cfm
   ajax/_cache.cfm
   ajax/_cache  (this is a dir)

In each file that processes a request and returns JSON, I include _cache.cfm at the top of that file.  At the very bottom of each file, I also change my <cfoutput> or WriteOutput() to a custom function called CacheOutput(JSON_STRING_HERE).

Here's what _cache.cfm does. (remember - its being included before anything else)

  1. Scan the cache directory for files older than 6hrs and deletes them.
  2. Creates a unique identifier for the given request by lumping the current server file that's being processed with any GET or POST variables.
  3. It then compresses the potentially large unique identifier into an encrypted hash string. This new string becomes the file name for the cache content.
  4. Does a file already exist in the cache with that unique identifier?
    - if YES, serve that content up and ABORT page execution
    - if NO, let the current page on the server finish processing the request
  5. Lastly, it defines the function that will be used on the original page processing the request to output AND cache the result.

Here's the code:

<cfset __cacheMinutes = 60*6>

<!---------------------------------->
<!--- keep the cache files clean --->
<!---------------------------------->

<!--- get all files in the cache ----->
<cfdirectory directory="#ExpandPath('.\')#/_cache" name="qDir" sort="datelastmodified ASC" type="file" />

<!--- determine the expiry date --->
<cfset cacheExpiryDateTime = DateAdd('n',-__cacheMinutes,Now())>
<cfset timestr = '#DateFormat(cacheExpiryDateTime,"yyyy-mm-dd")# #TimeFormat(cacheExpiryDateTime,"HH:mm:ss")#'>

<!--- filter the files by the expiry date --->
<cfquery name="qDir_filtered" dbtype="query">
 SELECT *
    FROM qDir
    WHERE datelastmodified < '#Evaluate("timestr")#' 
</cfquery>

<!--- delete old files --->
<cfloop query="qDir_filtered">
 <cffile action="delete" file="#ExpandPath('.\')#/_cache/#name#" />
</cfloop>


<!-------------------------------->
<!--- build a unique requestid --->
<!-------------------------------->

<!--- start the request id with the server page being executed --->
<cfset __requestID = '#CGI.PATH_INFO#'>

<!--- add url variables to the unique requestid --->
<cfset StructDelete(Url,'FieldNames')>
<cfloop collection="#Url#" item="i">
 <cfset __requestID = '#__requestID#_#i#-#Url[i]#'>
</cfloop>

<!--- add form variables to the unique requestid --->
<cfset StructDelete(Form,'FieldNames')>
<cfloop collection="#Form#" item="i">
 <cfset __requestID = '#__requestID#_#i#-#Form[i]#'>
</cfloop>


<!-------------------------------->
<!--- build a hashed filename ---->
<!-------------------------------->
<cfset __cacheID = Hash(__requestID)>
<cfset __cacheFile = '#ExpandPath('.\')#/_cache/#__cacheID#.bk'>


<!-------------------------------->
<!--- does this cache exist? ----->
<!-------------------------------->
<cfif FileExists(__cacheFile)>
 <cffile action="read" file="#__cacheFile#" variable="CachedContent" />
    <cfoutput>#CachedContent#</cfoutput>
    <cfabort>
</cfif>


<!------------------------------------------------>
<!--- build the function that caches content ----->
<!------------------------------------------------>
<cffunction name="CacheOutput" output="yes">
 <cfargument name="OutputString">
    
    <!--- output the result as fast as possible --->
    <cfoutput>#OutputString#</cfoutput>
    
    <!--- write the output to the cache --->
    <cffile action="write" file="#__cacheFile#" nameconflict="overwrite" output="#OutputString#" />
</cffunction>