3 Replies Latest reply on Oct 9, 2008 9:55 AM by JTMK

    re: Using ScopeCache custom tag

      Hi, I am quite new to ColdFusion and I have a calendar that is quite slow due to the cfloops it uses to build the calendar. As the page does not change very often I wanted to use Raymond Camden's ScopeCache custom tag. However, for some reason the custom tag was not getting picked up from the custom tag folder on the server. I have successfully used components so have tried to convert the custom tag to a component but get the following error message:

      Routines cannot be declared more than once.
      The routine scope_Cache has been declared twice in the same file.

      This is the component:

      Name : scopeCache
      Author : Raymond Camden (jedimaster@mindseye.com)
      Created : December 12, 2002
      Last Updated : November 6, 2003
      History : Allow for clearAll (rkc 11/6/03)
      : Added dependancies, timeout, other misc changes (rkc 1/8/04)
      Purpose : Allows you to cache content in various scopes.

      This tag allows you to cache content and data in various RAM based scopes.
      The tag takes the following attributes:

      name/cachename: The name of the data. (required)
      scope: The scope where cached data will reside. Must be either session,
      application, or server. (required)
      timeout: When the cache will timeout. By default, the year 3999 (i.e., never).
      Value must be either a date/time stamp or a number representing the
      number of seconds until the timeout is reached. (optional)
      dependancies: This allows you to mark other cache items as dependant on this item.
      When this item is cleared or timesout, any child will also be cleared.
      Also, any children of those children will also be cleared. (optional)
      clear: If passed and if true, will clear out the cached item. Note that
      this option will NOT recreate the cache. In other words, the rest of
      the tag isn't run (well, mostly, but don't worry).
      clearAll: Removes all data from this scope. Exits the tag immidiately.
      disabled: Allows for a quick exit out of the tag. How would this be used? You can
      imagine using disabled="#request.disabled#" to allow for a quick way to
      turn on/off caching for the entire site. Of course, all calls to the tag
      would have to use the same value.

      License : Use this as you will. If you enjoy it and it helps your application,
      consider sending me something from my Amazon wish list:
      <cffunction name="scope_Cache" access="public" returntype="string">
      <cfargument name="cachename" type="string" required="yes">
      <cfargument name="scope" type="string" required="yes">
      <cfargument name="timeout" type="string" required="yes">

      <cfprocessingdirective pageencoding="utf-8">

      <!--- allow for quick exit
      <cfif isDefined("arguments.disabled") and arguments.disabled>
      <cfexit method="exitTemplate">

      <!--- Allow for cachename in case we use cfmodule --->
      <cfif isDefined("arguments.cachename")>
      <cfset arguments.name = arguments.cachename>

      <!--- Attribute validation --->
      <cfif (not isDefined("arguments.name") or not isSimpleValue(arguments.name)) and not isDefined("arguments.clearall")>
      <cfthrow message="scopeCache: The name attribute must be passed as a string.">
      <cfif not isDefined("arguments.scope") or not isSimpleValue(arguments.scope) or not listFindNoCase("application,session,server",arguments.scope)>
      <cfthrow message="scopeCache: The scope attribute must be passed as one of: application, session, or server.">

      <!--- The default timeout is no timeout, so we use the year 3999. --->
      <cfparam name="arguments.timeout" default="#createDate(3999,1,1)#">
      <!--- Default dependancy list --->
      <cfparam name="arguments.dependancies" default="" type="string">

      <cfif not isDate(arguments.timeout) and (not isNumeric(arguments.timeout) or arguments.timeout lte 0)>
      <cfthrow message="scopeCache: The timeout attribute must be either a date/time or a number greather zero.">
      <cfelseif isNumeric(arguments.timeout)>
      <!--- convert seconds to a time --->
      <cfset arguments.timeout = dateAdd("s",arguments.timeout,now())>

      <!--- create pointer to scope --->
      <cfset ptr = structGet(arguments.scope)>

      <!--- init cache root --->
      <cfif not structKeyExists(ptr,"scopeCache")>
      <cfset ptr["scopeCache"] = structNew()>

      <cfif isDefined("arguments.clearAll")>
      <cfset structClear(ptr["scopeCache"])>
      <cfexit method="exitTag">

      <!--- This variable will store all the guys we need to update --->
      <cfset cleanup = "">
      <!--- This variable determines if we run the caching. This is used when we clear a cache --->
      <cfset dontRun = false>

      <cfif isDefined("arguments.clear") and arguments.clear and structKeyExists(ptr.scopeCache,arguments.name) and thisTag.executionMode is "start">
      <cfif structKeyExists(ptr.scopeCache[arguments.name],"dependancies")>
      <cfset cleanup = ptr.scopeCache[arguments.name].dependancies>
      <cfset structDelete(ptr.scopeCache,arguments.name)>
      <cfset dontRun = true>

      <cfif not dontRun>
      <cfif thisTag.executionMode is "start">
      <!--- determine if we have the info in cache already --->
      <cfif structKeyExists(ptr.scopeCache,arguments.name)>
      <cfif dateCompare(now(),ptr.scopeCache[arguments.name].timeout) is -1>
      <cfif not isDefined("arguments.r_Data")>
      <cfset caller[arguments.r_Data] = ptr.scopeCache[arguments.name].value>
      <!--- It is possible I'm here because I'm refreshing. If so, check my dependancies --->
      <cfif structKeyExists(ptr.scopeCache,arguments.name) and structKeyExists(ptr.scopeCache[arguments.name],"dependancies")>
      <cfif structKeyExists(ptr.scopeCache[arguments.name],"dependancies")>
      <cfset cleanup = listAppend(cleanup, ptr.scopeCache[arguments.name].dependancies)>
      <cfset ptr.scopeCache[arguments.name] = structNew()>
      <cfif not isDefined("arguments.data")>
      <cfset ptr.scopeCache[arguments.name].value = thistag.generatedcontent>
      <cfset ptr.scopeCache[arguments.name].value = arguments.data>
      <cfset ptr.scopeCache[arguments.name].timeout = arguments.timeout>
      <cfset ptr.scopeCache[arguments.name].dependancies = arguments.dependancies>
      <cfif isDefined("arguments.r_Data")>
      <cfset caller[arguments.r_Data] = ptr.scopeCache[arguments.name].value>

      <!--- Do I need to clean up? --->
      <cfset z = 1>
      <cfloop condition="listLen(cleanup)">
      <cfset z = z+1><cfif z gt 100><cfthrow message="ack"></cfif>
      <cfset toKill = listFirst(cleanup)>
      <cfset cleanUp = listRest(cleanup)>
      <cfif structKeyExists(ptr.scopeCache, toKill)>
      <cfloop index="item" list="#ptr.scopeCache[toKill].dependancies#">
      <cfif not listFindNoCase(cleanup, item)>
      <cfset cleanup = listAppend(cleanup, item)>
      <cfset structDelete(ptr.scopeCache,toKill)>

      <cfreturn scope_Cache>


        • 1. Re: Using ScopeCache custom tag
          Level 7
          > I wanted to use Raymond Camden's ScopeCache custom tag. However, for some
          > reason the custom tag was not getting picked up from the custom tag folder on
          > the server.

          I'd be trying to sort this out, rather than re-coding anything, to be

          > Routines cannot be declared more than once.
          > The routine scope_Cache has been declared twice in the same file.

          This error is not being caused by the code you attached. Have a hunt
          through your code for other instances of "scope_Cache", to see where you're
          defining a function of that name more thanonce in the same memory space.

          • 2. Re: Using ScopeCache custom tag
            johnab Level 1
            yeaj, ditto what adam said - probably easier to get the custom tag working rather than changing it to a component - which may be not work if you have to wrap the content you want to cache in the tag. But there are a few options if you can't get it to work from the customtags folder (which seems a little strange in itself)

            CF will look in the same path as the file you are accessing first, so if in a directory you have a file name index.cfm and then a file named tag.cfm then in index.cfm you could access tag as <cf_tag>. You can also use cfmodule to call the tag (using the correct pathing) and also you can use cfimport, so you could keep all your tags in a "tags" directory in your app and then use cfimport to make the tags available like <mytaglibrary:mytag... /> - but remember if you go this option you have to have a cfimport per file - you can't do it globally in application.cfm

            hope that helps,

            • 3. Re: Using ScopeCache custom tag
              JTMK Level 1
              Hi, thanks to both of you for your help. I reverted back the custom tag and it was picked up this time - I don't know what happened before. The functionality works as expected but I have hit another problem and I am hoping I can tap your combined ColdFusion wisdom to solve!!

              The calendar returns leave records for staff and filters the records using a querystring variable appended when the user clicks on a particular month. However, what members of staff the user sees depends on the data held in the users' session variables based on their authority. The reason I wanted to use caching is because the page takes a good ten seconds to run because of the use of cfloops for the members of staff and the days of the week to dynamically build the page. Using the custom tag would have worked if all members of staff saw the same calendar data and could only see one month. Can you see anyway I can speed up the page and do you think caching is the way forward here?