Suggested runMethod method

5 views
Skip to first unread message

Steve Bryant

unread,
Oct 24, 2009, 11:12:38 PM10/24/09
to CacheBox Framework
Hopefully the code will come across OK on the list.

I think the following suggested method(s) can allow an option that
would result in less code (especially less repetitive code) for
storing and fetching the results of a method call.

BEFORE EXAMPLE:
<cfif len(trim(cachename))>
<cfset result = variables.CacheBox.fetch
(arguments.cachename,arguments.cachedafter)>

<cfif result.status>
<!--- not found in cache -- get it and cache it --->
<cfset result = cb.store(cachename=arguments.cachename,
cachedwithin=arguments.cachedwithin,
content=super.getRecords(argumentcollection=arguments)) />
</cfif>

<cfreturn result.content>
<cfelse>
<cfreturn super.getRecords(argumentcollection=arguments)>
</cfif>

AFTER EXAMPLE:
<cfset var result = variables.CacheBox.runMethod(
component=super,
method="getRecords",
cachename=arguments.cachename,
args=arguments,
cachedafter=arguments.cachedafter,
cachedwithin=arguments.cachedwithin
)>


METHODS:
<cffunction name="runMethod" access="public" output="false"
returntype="any" hint="I return the cache of a method result if it
exists, otherwise I run the method and cache and return the results.">
<cfargument name="component" type="any" required="true" />
<cfargument name="method" type="string" required="true" />
<cfargument name="cachename" type="string" default="" />
<cfargument name="args" type="struct" required="false" />
<cfargument name="cachedafter" type="string" required="false"
default="">
<cfargument name="cachedwithin" type="numeric" required="false"
default="0">

<cfset var result = 0>

<!--- Make sure args argument exists --->
<cfif NOT structKeyExists(arguments,"args")>
<cfset arguments.args = structNew()>
</cfif>

<cfif len(trim(arguments.cachename))>
<cfset structDelete(arguments.args,"cachename")>
<cfset structDelete(arguments.args,"cachedafter")>
<cfset structDelete(arguments.args,"cachedwithin")>

<!--- Calculate cachename --->
<cfif structCount(arguments.args)>
<cfset arguments.cachename = "#arguments.cachename#:#VarToString
(arguments.args)#">
</cfif>

<!--- Cache the result if it doesn't exist --->
<cfif structKeyExists(instance.map,cachename)>
<cfset result = instance.cache.content[instance.map[cachename]]>
<cfelse>
<cfinvoke
returnvariable="result"
component="#arguments.component#"
method="#arguments.method#"
argumentcollection="#arguments.args#"
>
</cfinvoke>
<cfset store(
cachename=arguments.cachename,
content=result,
cachedwithin=arguments.cachedwithin
)>
</cfif>
<cfelse>
<cfinvoke
returnvariable="result"
component="#arguments.component#"
method="#arguments.method#"
argumentcollection="#arguments.args#"
>
</cfinvoke>
</cfif>

<cfreturn result>
</cffunction>

<cffunction name="VarToString" access="private" output="false"
returntype="string" hint="I'm useful for creating cache names for
queries based on filter data">
<cfargument name="variable" type="any" required="true" hint="A
variable to convert to a URL string">

<cfset var result = "">
<cfset var keys = "">
<cfset var ii = 0>

<cfscript>
if ( isSimplevalue(arguments.variable) ) {
result = arguments.variable;
} else if ( isArray(arguments.variable) ) {
for (ii=1; ii LTE ArrayLen(arguments.variable); ii=ii+1 ) {
result = "#result##ii#=#VarToString(arguments.variable[ii])#&";
}
result = Left(result,Len(result)-1);
result = "[#result#]";
} else if ( isStruct(arguments.variable) ) {
for ( ii in arguments.variable ) {
result = "#result##LCase(ii)#=#VarToString(arguments.variable[ii])
#&";
}
result = Left(result,Len(result)-1);
result = "{#result#}";
} else {
result = "(unknown)";
}
</cfscript>

<cfreturn result>
</cffunction>

Isaac Dealey

unread,
Oct 25, 2009, 4:06:12 PM10/25/09
to cfcac...@googlegroups.com
I like what you've done with the VarToString method - it's more flexible than the StructToURL method I had used in the DataMgr sample I included in the distribution.

I think it ought to have a urlencodedformat() around the individual items in the result string though, just in case. The problem with urlencodedformat is that it uses % symbols, which I'd already defined as the wildcard character for content expiration, i.e. agent.expire("mystuff.%"). So that means the individual items shouldn't have % symbols in them because it might make it difficult to selectively drop a single item (without accidentally dropping some others). So I think we would need to replace the % symbols with some other character and I'm not sure offhand what that would be. I use the pipe character | to delimit the cachenames already, so that would be problematic as well. Maybe the tilde ~ would work? It seems like a character that's unlikely to show up in the kind of ID values that would typically be used for identifying cache items.

Something else to consider is that this is an enhancement that would go into the CacheBoxAgent.cfc, which is a couple hundred lines right now and supposed to remain small and independent to preserve the framework's portability. I'm okay with the extra 50 lines or so here going into the agent if the community is in favor of it. And then we would roll the version of the CacheBoxAgent.cfc forward from 1.4 to 1.5. The agent version is actually separate from the service version because they're implemented separately, so separate versions allows you to see what features are implemented on either side of the framework.

Another alternative would be to place the VarToString() and RunMethod() features in the CacheBoxNanny.cfc, which is external to the agent and that way they can be customized however you like. I actually already have a custom version of the CacheBoxNanny.cfc in the DataFaucet ORM distribution, which allowed me to maintain the optional feature that mimics the native ColdFusion Cached Query behavior of testing the generated SQL string for changes before checking for a cached copy.

So here are a few bullets to summarize my thoughts:

1. How do we want to escape string values in the generated cachenames from VarToString() ?
  • URLEncodedFormat is convenient, but % symbols are problematic
  • ~ may work as a replacement for % symbols
2. Do we want RunMethod() added to a new CacheBoxAgent version or in the external CacheBoxNanny ?
  • Pro Agent: A new agent version lets you know if the feature is available.
  • Pro Nanny: Smaller / more portable CacheBoxAgent.cfc

Steve Bryant

unread,
Oct 26, 2009, 11:50:27 AM10/26/09
to cfcac...@googlegroups.com
1. I like the tilde idea.

2. I certainly understand your desire to keep CacheBoxAgent.cfc small
and 100 lines would be a pretty big relative increase to the file
size.

I don't think I know enough about CacheBox yet to fully appreciate the
ramifications of each option. I will just say that for my purposes, I
would be happy so long as my example calling code would work.

Thanks,

Steve

ike

unread,
Oct 26, 2009, 1:58:41 PM10/26/09
to CacheBox Framework
I just had another look at CacheBoxAgent.cfc and it's larger than I
remembered at just under 340 lines. So we'd be talking about ~340 to
~440 and ~15kb to ~20kb in terms of file size. Although it's already
the 4th largest file in the distribution just because it has to
support content expiration with the % wildcard. I don't think any of
that is really make or break for the idea of putting it into the
agent.

After having thought about it a bit longer however, my gut says that
there may be a fair amount of difference of opinion with regard to how
the runMethod() feature works. You and I may like the tilde as a
replacement for the % symbol, others may not, or they may have some
other idea about how it ought to format the string.

So at this point I'm actually leaning toward putting it in the
CacheBoxNanny.cfc in order to limit potential version-dependency
issues that might crop up from people fiddling much with the agent.
Not that fiddling is a bad thing -- fiddling is fine, but it helps to
design systems with fiddling in mind. ;)

I want to wait though and see if anyone else weighs in on it in the
next week. If we haven't heard any other thoughts by next Monday then
I'll go ahead and implement it in the official release version.
Reply all
Reply to author
Forward
0 new messages