get return value from CFMODULE - coldfusion

I am using some vendor code that we have neither control over nor have the ability to view the source.
For the sake of explanation lets assume I have three files.
Test.cfm
VendorModule.cfm
Custom.cfm
In Test.cfm: I am calling the cfmodule as such:
<CFMODULE template="VendorModule.cfm">
I know nothing about the Vendor module; not the code it executes or the other modules it calls etc, EXCEPT that it will at some point call my file: Custom.cfm
In Custom.cfm: Does some logic and prints Yes or No. (Obviously the variable "something" gets defined in code I am omitting)
<cfif something is true>
<cfoutput>Yes</cfoutput>
<cfelse>
<cfoutput>No</cfoutput>
</cfif>
When I run Test.cfm I get the Yes or No output as expected on the page.
What I want to do is have Custom.cfm set a variable that can then be read by Test.cfm.
So Custom.cfm would look like:
<cfif something is true>
<cfset ??? = "Yes">
<cfelse>
<cfset ??? = "No">
</cfif>
And Test.cfm would look like:
<CFMODULE template="VendorModule.cfm">
<!--- Do something here based on value of ??? set in Custom.cfm --->
Can this be done give what I have available to me?
Thanks!

It's definitely not an ideal situation, but given the fact that you can't see the vendormodule.cfm code let alone change it, your choices are limited.
If vendormodule.cfm calls your custom.cfm in either a cfinclude or a cfmodule you could modify the scope you're after like this, it feels bad to even suggest it, but it should accomplish what you're after.
<cfif something is true>
<cfset caller.caller.result = "Yes">
<cfelse>
<cfset caller.caller.result = "No">
</cfif>
The above code would have to be modified depending at what level custom.cfm is called. For example, if custom.cfm was six levels deep, then it would reference the variables scope within test.cfm like this:
caller.caller.caller.caller.caller.caller.result
Your only other option would be to modify some other shared scope within custom.cfm that test.cfm can see.
Hope that helps.

Usually a cfmodule would return values by using the Caller scope, which is the caller of the cfmodule i.e. Test.cfm. So maybe before and after you called the cfmodule, dump the Variables scope and see if there are any additional vars set by the cfmodule.
Some cfmodule may return vars in other scopes as well, e.g. Request scope. So if you don't seen anything new set in Variables scope, try all other scopes.

Related

In ColdFusion, what is the difference between setting a function to a variable and calling a function in hashtags?

I wrote the following function:
<cffunction name="check_session_valid" returntype="boolean">
<cfif NOT StructKeyExists(session,"username") OR (len(session.username) EQ 0)>
<script>location.href = 'logout.cfm'</script>
<cfabort>
</cfif>
<cfset session.myApp_start = now()>
<cfreturn true>
</cffunction>
In my .cfm page, I can call that function using
<cfset session_valid = application.lib.check_session_valid()>
OR
#application.lib.check_session_valid()#
What's the difference? Best practice?
Since you asked about best practice, which is a matter of opinion, I think you can improve your function by having it returning either true or false depending on whether or not session.username exists and has a length greater than 0. Then you can use it like this:
<cfif application.lib.check_session_valid()>
code for this condition
<cfelse>
<cflocation href = "logout.cfm">
<!--- note that cfabort is not necessary --->
<cfif>
Regarding your specific question, I think the extra variable, session_valid, is a waste of typing. However, that is simply my opinion.
Not related to your question, I found it curious that you would direct users to a page called logout.cfm. Often users are directed to a page that allows them to log in.
To be honest, both are valid and both would be considered best practice depending on what you are trying to do.
My rule of thumb is if I will need to use the result of a function call more than once, I will set it to a variable
myResult = application.lib.check_session_valid();
If I will only need to use the variable once I would do what Dan mentioned
if( application.lib.check_session_valid() ){
// Do stuff
}
The difference between the examples you showed are
<cfset session_valid = application.lib.check_session_valid()>
This will set the variable named session_valid to whatever is returned from the call to check_session_valid().
#application.lib.check_session_valid()#
This will, in .cfm pages, simply render the value returned from the call to check_session_valid() assuming it is inside of a <cfoutput> tag. There are other places this would also render the value, such as inside a <cfsavecontent>.

How to include UDF_library in application.cfm?

I am using ColdFusion 8.0.1
I am working on an existing application that has thousands of pages. I am trying to include a library of new UDFs in my application.cfm file.
I addedd this line to application.cfm:
<!--- UDF library include --->
<cfinclude template="UDF/udf_library.cfm">
The UDF library includes other files that contain UDFs, like this:
<cfinclude template="udf_powerreview.cfm">
I have functions in the udf_powerreview.cfm file, such as:
// CREATE POWER REVIEWS SNIPPET
function createPRSnippet(Page_ID) {
LOCAL.Page_ID = ARGUMENTS.Page_ID;
if (isNumeric(LOCAL.Page_ID) && LOCAL.Page_ID > 0) {
LOCAL.Snippet = "<div class='pr_snippet_product'><script type='text/javascript'>var pr_snippet_min_reviews = 0; POWERREVIEWS.display.snippet(document, { pr_page_id : '#LOCAL.Page_ID#' });</script></div>";
} else {
LOCAL.Snippet = "";
}
return LOCAL.Snippet;
}
The debugging tool says that UDF/udf_library.cfm and udf_powerreview.cfm are being successfully included.
The problem is when I call the function in another page, I get an error that says that function doesn't exist. When I can copy the function and put it directly into the page that it is used in and it works just fine. And, I do not get the error "routines can not be declared twice".
In every site that I build, I create a udf_library.cfm or udf_library.cfc in the exact same manner. They always work fine.
What might prevent the functions from being available and accessed? Is there an application setting that needs to be set?
It's a page scope issue. Don't think of the Application.cfm as an include on all your pages, just know that it runs first. Somethings it initializes will carry over to your existing page scope and some things won't. Using an Application.cfc instead of an application.cfm takes care of much of the ambiguity.
To make your UDF's available to your whole application, I would suggest using a "Singleton" Design pattern. First take your UDF's and put them in a CFC format. This will make them more portable.
in your application.cfm you could put the following lines:
<cfif NOT isdefined('session.udf_powerreview') or isdefined('url.resetudf')>
<cfset session.udf_powerreview = createobject('Component','udf.udf_powerreview')/>
<!--- this 'udf.udf_powerreview' represents the physical path udf/udf_powerreview.cfc --->
</cfif>
I'm stuffing it in the session scope instead of the application scope, becuase you won't have an good way of resetting the application scope if you modify your UDF's.
Either way, once this is in your application.cfm you should be able to see your functions on any page.
<cfdump var="#session.udf_powerreview#">
Here is one strategy that I use. This basically calls the UDFs "on demand". It won't reimport the UDFs if it already exists. You do however have to have named arguments however, otherwise you'd have to strip out the UDFName out of the argument collection. I'm worried however that argument order might not be preserved, I haven't investigated that.
application.cfm
<cfapplication
name="udftest_001" />
<cffunction name="udf">
<cfargument name="udfname" type="string" required="true">
<cfif NOT isDefined(udfname)>
<cfinclude template='./udfs/#udfname#.cfm'>
</cfif>
<cfset tempfunc = variables[udfname]>
<cfreturn tempfunc(argumentCollection=arguments)>
</cffunction>
index.cfm
<cfoutput>
#udf(udfname='testUDF',firstname='John',lastname='Smith')#<br/>
#udf(udfname='testUDF',firstname='Betty',lastname='Ford')#<br/>
</cfoutput>
/udfs/testudf.cfm
<cfscript>
function testUDF() {
return 'Hello ' & arguments.firstname & ' #arguments.lastname#';
}
</cfscript>
I suspect something is up with relative paths.
Can you make "UDF" a mapping? Then you can do
<cfinclude template="/UDF/udf_library.cfm">

Get names of functions injected into CFC?

I am using the technique detailed in this answer to manage a library of small utility functions. (Essentially, each function is loaded as a "mix-in" using cfinclude.)
I have need, however, to know the names of the functions that the object has (once instantiated). A cfdump on the object only shows the init function which is written directly in the CFC.
Some more detail:
I am creating the object in the application scope in OnApplicationStart().
<cfset application.udfs=createObject("component","extensions.udfs").init()>
However, to save the developers from having to constantly write application.udfs.foo(), I thought I'd grab all of the functions and drop them in to the variables scope in OnRequestStart(), so that these hypothetical developers could just write foo().
<cfset foo=application.udfs.foo>
Obviously, though, this needs to be dynamic and to happen for each of the functions in the object, no matter how many there are. If I repeat this line for every function I've lost whatever I'd gained by having a library that is dynamically generated.
I thought perhaps I could use a collection loop, but that was invalid. I am fairly certain there's a way to get the list of methods in an object, but I have not yet been able to find it.
Any clues?
By the by, my fallback is going to be to copy the application.udfs object to a local object with a nice short name (like "u") so that the developers can simply type u.foo(), so no need to suggest that if what I want to do can't be done.
This should allow you to import all your udfs into the global variables scope:
StructAppend(variables, application.udfs);
I think GetMetaData should help you.
Here another interesting option suggested by Ben Nadel:
Check out the detail in his blog entry: http://www.bennadel.com/blog/1776-Creating-Globally-Accessible-User-Defined-Functions-In-ColdFusion-Safer-Version-.htm
UDF.cfc
<cfcomponent
output="false"
hint="I define user defined functions.">
<cffunction
name="getMessage"
access="public"
returntype="string"
output="false"
hint="I return a test message.">
<cfreturn "I am defined in the UDF component" />
</cffunction>
</cfcomponent>
Application.cfc
<!--- Define the application. --->
<cfset this.name = hash( getCurrentTemplatePath() ) />
<cfset this.applicationTimeout = createTimeSpan( 0, 0, 5, 0 ) />
<!---
Add all of our "global" methods to the URL scope. Since
ColdFusion will automatically seach the URL scope for
non-scoped variables, it will find our non-scoped method
names.
--->
<cfset structAppend(
url,
createObject( "component", "UDF" )
) />

How to best create and store APPLICATION variables?

I am using ColdFusion 9.0.1
I am taking over a site and the guy before me created about 100 variables and put them into the APPLICATION scope. I believe that his 100 variables were continuously being overwritten with each page load.
Basically, he had this in Application.cfc:
APPLICTION.VariableOne = "SomeStringOne";
APPLICTION.VariableTwo = "SomeStringTwo";
APPLICTION.VariableThree = "SomeStringThree";
My plan is to keep it simple and yet very readable is to test for a specific structure in the application scope. If it's not there, create the structure and variables:
if (not isDefined("APPLICTION.AppInfo") or not isStruct(APPLICTION.AppInfo)) {
APPLICTION.AppInfo = structNew();
APPLICTION.AppInfo.VariableOne = "SomeStringOne";
APPLICTION.AppInfo.VariableTwo = "SomeStringTwo";
APPLICTION.AppInfo.VariableThree = "SomeStringThree";
}
Of course, once the site is live and we are done creating all of the application variables, I'd move this into the into the onApplicationStart() method.
The solution that I want has to be more about "readability" and less about "efficiency". Several non-CFers, but very experience coders will be using this and will need to "get it" quickly.
Does my plan have any gaping holes or is it too inefficient?
Is there a more readable way of creating and managing application variables?
Why not move the definition into onApplicationStart() right now? If you need to reset them during development, you could always pass in a URL variable to flag it for reset, like so:
<!--- in Application.cfc --->
<cffunction name="onRequestStart">
<cfif IsDefined("url.resetApp")>
<cfset ApplicationStop()>
<cfabort><!--- or, if you like, <cflocation url="index.cfm"> --->
</cfif>
</cffunction>
Actually, after re-reading the OP, and reading the suggested solutions, I'm going to have to agree with the OP on his setup, for this very important reason:
This, in onApplicationStart()
APPLICTION.AppInfo = structNew();
APPLICTION.AppInfo.VariableOne = "SomeStringOne";
APPLICTION.AppInfo.VariableTwo = "SomeStringTwo";
Can then later be turned into this, within onRequestStart()
<cflock name="tmp" type="readonly" timeout="15">
<cfset REQUEST.AppInfo = APPLICATION.AppInfo />
</cflock>
Your app can then go on to access the REQUEST vars conveniently, esp, if you decide you want to cache CFCs in the same scope--they would simply go into a separate key:
APPLICATION.Model.MyObject = CreateObject('component','myobject');
Which, of course, also gets poured into REQUEST (if you choose)
Want to go Jake Feasel's route above? No problem:
<cfif isDefined('URL.reload')>
<cfset APPLICATION.Model = StructNew() />
</cfif>
Now you're able to flexibly kill your object cache but maintain your vars (or vice versa as you choose).
This is a great setup for another reason: If you want to build in your own Development/Production "mode", in which the development mode always recompiles the CFCs, but the production mode keeps them cached. The only change you have to make on top of this, is the REQUEST set noted above:
<cfif (isProduction)>
<cflock name="tmp" type="readonly" timeout="15">
<cfset REQUEST.AppInfo = APPLICATION.AppInfo />
</cflock>
<cfelse>
<cfset REQUEST.AppInfo = StructNew() />
<cfset REQUEST.AppInfo.VariableOne = "SomeStringOne" />
...etc...
</cfif>
You can also make the setting of vars and the creation of objects into a private method within Application.cfc, for even further convenience.
I would go ahead and just use OnApplicationStart but back in the pre Application.cfc days we used to do something like Application.Build and if the Build value was different then we did all of our sets on Application variables. So quick and dirty would be something like:
<cfparam name="Application.Build" default="" />
<cfset Build = "28-Nov-2011" />
<cfif Application.Build IS NOT Variables.Build OR StructKeyExists(URL, "Rebuild")>
<cfset Application.Build = Variables.Build />
<!--- A bunch of other CFSETs --->
</cfif>
This method though was something we used back when all we had was the Application.cfm

Can I pass a simple value by reference in ColdFusion?

By default, ColdFusion passes simple types (like numeric, string, and GUID) by value to functions. I'd like to pass a simple type by reference.
I'm currently wrapping a simple value in a struct (they get passed by reference). This solves my problem but it is very ugly:
<!--- TheFunctionName---->
<cffunction name="TheFunctionName">
<cfargument name="OutVariable" type="struct">
<cfset OutVariable.ID = 5>
</cffunction>
<cfset OutVariable=StructNew()>
<cfset TheFunctionName(OutVariable)>
<!--- I want this to output 5--->
<cfoutput>#OutVariable.ID#</cfoutput>
I'd rather something like this:
<!--- TheFunctionName---->
<cffunction name="TheFunctionName">
<cfargument name="OutVariable" passbyref="true">
<cfset OutVariable = 5>
</cffunction>
<cfset TheFunctionName(OutVariable)>
<!--- I want this to output 5--->
<cfoutput>#OutVariable#</cfoutput>
AFAIK, there's no way to pass simple values by reference in ColdFusion. The only workaround I can think of is the one you're already using.
Instead, I would suggest trying to restructure your program to work with the grain of the language. In cases where there's only one simple value to "modify", you could just make your function return the new value, and call it like:
<cfset SomeVar = TheFunctionName(SomeVar)>
In cases where you're modifying multiple values, take a step back and think about whether it's possible to bundle those multiple values up into a CFC with your mutator functions becoming methods of the CFC. This could be clearer and more maintainable solution anyway.
You can arrange for the variables used outside and inside the function to be in a scope that exists in both code areas. For example, if you put a variable in the "session" or the "request" scope you will be able to access it from within the function. The changes made will persist.
Note that when you are doing this you aren't actually "passing" the variables to the function. The function just assumes the variable exists or creates it, depending on how you code it.
<cffunction name="TheFunctionName">
<cfset Request.StrVar = "inside function<br />" />
</cffunction>
<cfscript>
Request.StrVar = "outside function<br />";
WriteOutput(Request.StrVar);
TheFunctionName();
WriteOutput(Request.StrVar);
</cfscript>
About ColdFusion Scopes
If there is any doubt about the calling page declaring the variable in advance when it is required you'll have to do some legwork with the <cfparam> tag or IsDefined() function.
If you:
declare the function inside of a CFC
invoke the function using <cfinvoke>
You would be able to specify the <cfinvoke> parameter "returnvariable", and then output that variable however you like.
<cfinvoke component="this" method="TheFunctionName" returnvariable="blah">
<cfinvokeargument name="data" value="whatever" type="string">
<cfreturn data>
</cfinvoke>
<cfdump var="#blah#">
If you are writing everything in cfscript, then I would go with what SurroundedByFish said.