I have been using ColdFusion 8 / 9 / 10 regularly. The code below works just great in CF9 and CF10. (I developed it in 9). It does NOT work in CF8 though.
If you run the code below (at the bottom) in CF9 and CF10, you should get the HTML results immediately below:
<select>
<option value="1">Option 1</option>
<option value="2">Option 2</option>
<option selected="" value="3">Option 3</option>
</select>
If you run the code below in CF8, you'll get this error:
The SELECTED parameter to the WrapOption function is required but was not passed in.
In CF8, how would I modify this code to make the "selected" parameter (or any other parameter) optional in CF8?
<cfscript>
Options = WrapOption("Option 1", 1);
Options = Options & WrapOption("Option 2", 2);
Options = Options & WrapOption("Option 3", 3, "Selected");
SelectBox = WrapSelect(Options);
writeOutput(SelectBox);
// WRAP OPTION
function WrapOption(Content, Value, Selected) {
LOCAL.Content = ARGUMENTS.Content;
LOCAL.Properties = " value='#ARGUMENTS.Value#'";
// SELECTED
if (structKeyExists(ARGUMENTS, "Selected")) {
LOCAL.Properties = LOCAL.Properties & " selected";
}
LOCAL.Item = "<option #LOCAL.Properties#>#LOCAL.Content#</option>";
return LOCAL.Item;
}
// WRAP SELECT
function WrapSelect(Options, Class, ID) {
LOCAL.Options = ARGUMENTS.Options;
LOCAL.Properties = "";
// CLASS
if (structKeyExists(ARGUMENTS, "Class")) {
LOCAL.Properties = LOCAL.Properties & " class='#ARGUMENTS.Class#'";
}
// ID
if (structKeyExists(ARGUMENTS, "ID")) {
LOCAL.Properties = LOCAL.Properties & " id='#ARGUMENTS.ID#'";
}
LOCAL.Item = "<select #LOCAL.Properties#>#LOCAL.Options#</select>";
return LOCAL.Item;
}
</cfscript>
In CFSCRIPT, named arguments are required unless provided with a default (which can't be done until CF9).
To do optional arguments in CFSCRIPT in ColdFusion 8 and below, you need to remove the argument from the function definition and check for its existence in the body of the function. You can do this by taking advantage of ColdFusion's handling of ordinal (ordered instead of named) arguments.
function WrapOption(Content, Value) {
if ( ArrayLen(Arguments) GTE 3 ) {
ARGUMENTS.Selected = ARGUMENTS[3];
}
LOCAL.Content = ARGUMENTS.Content;
LOCAL.Properties = " value='#ARGUMENTS.Value#'";
// SELECTED
if (structKeyExists(ARGUMENTS, "Selected")) {
LOCAL.Properties = LOCAL.Properties & " selected";
}
LOCAL.Item = "<option #LOCAL.Properties#>#LOCAL.Content#</option>";
return LOCAL.Item;
}
Sean is correct:
Names of the arguments required by the function. The number of
arguments passed into the function must equal or exceed the number of
arguments in the parentheses at the start of the function definition.
If the calling page omits any of the required arguments, ColdFusion
generates a mismatched argument count error.
Quoted from: http://livedocs.adobe.com/coldfusion/8/htmldocs/UDFs_03.html
I guess you can rewrite it in CFML then it'll work for sure.
// WRAP OPTION
<cffunction name="WrapOption" output="false">
<cfargument name="Content" required="true">
<cfargument name="Value" required="true">
<cfargument name="Selected">
<cfscript>
LOCAL.Content = ARGUMENTS.Content;
LOCAL.Properties = " value='#ARGUMENTS.Value#'";
// SELECTED
if (structKeyExists(ARGUMENTS, "Selected")) {
LOCAL.Properties = LOCAL.Properties & " selected";
}
LOCAL.Item = "<option #LOCAL.Properties#>#LOCAL.Content#</option>";
return LOCAL.Item;
<cfscript>
</cffunction>
Or alternatively as a workaround for CF8, do not define the selected value in the function declarator. Just check if arguments[3] is defined. Make sure you document what is expected for arguments[3] in the comment.
p.s. don't forget, you need to Make your own LOCAL scope in CF8... i.e. var LOCAL = {}
Related
I have a function utilizing an include, of a JSP file, to retrieve thread information which is then converted into a query object. The function returns an empty query Lucee, but it executes properly in ColdFusion.
CFML:
<cffunction name="mainThreads" output="false" returntype="query" access="public">
<cfargument name="filterPages" type="boolean" required="true">
<cfscript>
var threadStackDump = "";
var thread = 0;
var stackTrace = "";
request.threads = arraynew(1);
GetPageContext().include("putParentThreadInRequestScope.jsp");
ThreadQuery = QueryNew("id, name, group, stacktrace, alive", "Integer, VarChar, VarChar, VarChar, Bit");
QueryAddRow(ThreadQuery, arrayLen(request.threads));
for ( thread = 1; thread lte arrayLen(request.threads); thread = thread + 1 )
{
QuerySetCell(ThreadQuery, "id", request.threads[thread].getId(), thread);
QuerySetCell(ThreadQuery, "name", request.threads[thread].getName(), thread);
QuerySetCell(ThreadQuery, "group", request.threads[thread].getThreadGroup().getName(), thread);
QuerySetCell(ThreadQuery, "alive", request.threads[thread].isAlive(), thread);
threadStackDump = "";
stackTrace = request.threads[thread].getStackTrace();
for ( element = 1; element lte arrayLen(stackTrace); element = element + 1 )
if ( arguments.filterPages )
{
if ( findNoCase('runPage',stackTrace[element]) neq 0 or findNoCase('runFunction',stackTrace[element]) neq 0 )
threadStackDump = threadStackDump & stackTrace[element] & "#chr(13)#";
}
else
threadStackDump = threadStackDump & stackTrace[element] & "#chr(13)#";
QuerySetCell(ThreadQuery, "stacktrace", threadStackDump, thread);
}
return ThreadQuery;
</cfscript>
</cffunction>
JSP
<%
ThreadGroup threadGroup = Thread.currentThread().getThreadGroup();
Thread threadList[]=new Thread[Thread.activeCount()];
threadGroup.enumerate(threadList);
request.setAttribute("threads", threadList);
%>
The code is not working in Lucee, but I'm not sure why. Does it have something to do with the java versions?
I'd guess you forgot to enable handling of .jsp in your web.xml, but ... you're not aware of it because the problem code executes inside a cffunction that suppresses all output!
Take a leaf out of Troubleshooting 101 and test the problem code in small chunks. Start by executing the JSP include separately. If it displays the JSP code on screen, instead of executing it, then you know JSP handling isn't enabled, and that's your problem.
<cfscript>
GetPageContext().include("putParentThreadInRequestScope.jsp");
writeDump( request );
</cfscript>
I have a coldfusion Struct containing mix keys numeric and alpha, alphanumerics
I need to access only the numeric keys.
My code looks like
<cfset ids = structkeyList(st ) />
<cfset numericIDs = "" />
<cfloop list="#ids#" index="i">
<cfif IsNumeric(i)>
<cfset numericIDs = ListAppend( numericIDs , i ) />
</cfif>
</cfloop>
Is there a better method to solve such problems?
Is there a better method to solve such problems?
I would use something like this:
<cfset numericIDs = arrayToList(reMatch('\b\d+(?=,|$)\b', structKeyList(st)))>
Is there a better method to solve such problems?
I'd generally recommend working with arrays instead of lists.
In CF9 a loop similar to yours is as good as it gets. You can make a utility function out of it if you need it more than once. This one avoids StructKeyList() to be able to deal with all kinds of keys, independent of a separator character:
<cfscript>
function GetNumericKeys(struct) {
var keys = struct.keys();
var result = ArrayNew(1);
var key = "";
while (keys.hasNext()) {
key = keys.next();
if (IsNumeric(key)) ArrayAppend(result, key);
}
return result;
}
</cfscript>
and
<cfset nkeys = GetNumericKeys(st)>
In CF11 you can get a little more sophisticated (tested on CF11, can't say how CF10 handles this code).
<cfscript>
numericIDs = arrayFilter(structKeyArray(st), function (key) {
return IsNumeric(key);
});
</cfscript>
To ensure integer keys, use:
<cfscript>
numericIDs = arrayFilter(structKeyArray(st), function (key) {
return Int(key) eq key;
});
</cfscript>
I really don't see what's wrong with this. It should work quite well already, and it is very readable.
Sometimes working with a List is faster than an Array.
I had this:
<cfscript> function ListNumeric(principal) {
a=principal;
cleanlist = ''; for (i=1; i <= ListLen(a);i=i+1) { if(IsNumeric(ListGetAt(a,i))){ cleanlist = ListAppend(cleanlist,ListGetAt(a,i)); } } Return cleanlist; } </cfscript>
Also possible to work with regular expression:
inList2 = REReplace(inList,"[^0-9.]", "","ALL");
I have a function that should check the date of a file. If the file is greater than sixty seconds, a query should run and create a new file. The query takes sixty seconds to run.
This process works perfectly when it's not wrapped in a CFTHREAD. When CFTHREAD is used, nothing seems to happen. I get no errors. What I expect to see, is a new file being made. I never see that new file.
Where should I look for an error? What am I missing? Why is CFTHREAD not working?
<!--- GET CATEGORIES --->
<cffunction name="getCategories" access="remote">
<cfscript>
LOCAL.MaxFileAge = 60;
LOCAL.MaxFileUnits = 's';
// THE FILE
LOCAL.TheFileDaily = "#VARIABLES.JSDir#\#VARIABLES.DayMonth#-categories.json";
// THE FILE DOES NOT EXIST
if (fileExists(LOCAL.TheFileDaily) == false) {
LOCAL.MakeNewFile = true;
// THE FILE EXISTS
} else {
// GET THE DATE OF THE FILE
LOCAL.LastModified = getFileInfo(LOCAL.TheFileDaily).LastModified;
// GET FILE AGE
LOCAL.FileAge = dateDiff(LOCAL.MaxFileUnits, LOCAL.LastModified, now());
// FILE IS OLD
if (LOCAL.FileAge > LOCAL.MaxFileAge) {
LOCAL.MakeNewFile = true;
} else {
LOCAL.MakeNewFile = false;
}
}
</cfscript>
<cfif LOCAL.MakeNewFile eq true>
<cfthread action="run" priority="HIGH">
<cfquery name="Q">
SELECT Stuff
FROM Tables
</cfquery>
<!--- MAKE THE DAILY FILE --->
<cffile action="write" file="#LOCAL.TheFileDaily#" output="#serializeJSON(Q)#">
</cfthread>
</cfif>
</cffunction>
You can't write to and share the local scope to a seperate thread, you need to share them via the request scope (request is the ideal scope for this as developers have very tight control over what data is contained within). You might try something like this:
Create a struct within the request scope and write to that.
In fairness, only variables you need to transfer need be in the request scope's struct. This is just a generic update because I don't know what the contents of your CFTHREAD really looks like. In this case, it actually looks like TheFileDaily is the only variable you're sharing, so that would be the only thing that needed to be in the request scope.
<!--- GET CATEGORIES --->
<cffunction name="getCategories" access="remote">
<cfscript>
request.lData = StructNew();
request.lData.MaxFileAge = 60;
request.lData.MaxFileUnits = 's';
// THE FILE
request.lData.TheFileDaily = "#VARIABLES.JSDir#\#VARIABLES.DayMonth#-categories.json";
// THE FILE DOES NOT EXIST
if (fileExists(request.lData.TheFileDaily) == false) {
request.lData.MakeNewFile = true;
// THE FILE EXISTS
} else {
// GET THE DATE OF THE FILE
request.lData.LastModified = getFileInfo(request.lData.TheFileDaily).LastModified;
// GET FILE AGE
request.lData.FileAge = dateDiff(request.lData.MaxFileUnits, request.lData.LastModified, now());
// FILE IS OLD
if (request.lData.FileAge > request.lData.MaxFileAge) {
request.lData.MakeNewFile = true;
} else {
request.lData.MakeNewFile = false;
}
}
</cfscript>
<cfif request.lData.MakeNewFile eq true>
<cfthread action="run" priority="HIGH">
<cfquery name="Q">
SELECT Stuff
FROM Tables
</cfquery>
<!--- MAKE THE DAILY FILE --->
<cffile action="write" file="#request.lData.TheFileDaily#" output="#serializeJSON(Q)#">
</cfthread>
</cfif>
</cffunction>
Useful sources:
Working with Threads
Adam Cameron's post on the topic
I'm new to using the application.cfc file in our application and some of these don't seem to be working and I can't figure out why. I have tried to cfdump "application". I get Application.DSN, Application.USERNAME, Application.Password, but not Application.SYSTEMPATH or Application.ACCOUNT
<cffunction name="onApplicationStart">
<cfscript>
Application.availableResources=0;
Application.DSN = "XXX";
Application.USERNAME = "XXX" ;
Application.PASSWORD = "XXX";
Application.SYSTEMPATH = "http://example.com/"; // This doesn't work
Application.ACCOUNT = XXX; // This doesn't work.
Application.counter1=1;
Application.sessions=0;
</cfscript>
</cffunction>
I think you want this:
Application.SYSTEMPATH = GetDirectoryFromPath(GetCurrentTemplatePath());
This will work too:
Application.SYSTEMPATH = expandPath( './' );
Now this...
Application.ACCOUNT = XXX; // This doesn't work. (because it is assuming XXX is a variable).
You need this:
XXX = 'something'; or XXX = 1; or Remove it altogether because it serves no purpose.
Then when you call:
Application.ACCOUNT = XXX; it won't give you errors.
Or you can just skip it and do this:
Application.ACCOUNT = 'something'; (a string)
Application.ACCOUNT = 1; (a number)
Then it won't fall apart (Because XXX is a variable not a value and you can't call a variable that doesn't exist).
So, if you have a 'variable' it has to have a 'value' (variable/value pair) or at least set a placeholder like XXX=0; or XXX=''; if you must have it.
Have I killed this variable/value dead horse to death??? Lol... :D
Jokes aside let us know if you have another question about your Application variables because some seem unnecessary (can't judge for sure though).
Creating a ColdFusion function to handle the report outputs based on the varieties of 10 groupings. The output shows data grouping and amounts correctly, but it doesn't have correct report heading. For instance, it should have 5 different building names shown for each group, but all have the same building name.
Here is the code that calling the function:
<cfoutput query="Output" group="#Group1Field#" >
<cfscript>
Group1Count = 0;
Group1Amount = 0;
</cfscript>
<h2>#eqInventoryHelper.getGroupingHeaderText(Group1, Output)#</h2></cfoutput>
Here is the code from the function:
<cffunction name="getGroupingHeaderText" output="false">
<cfargument name="selectedGrouping" required="true" type="string"/>
<cfargument name="outputQuery" required="true" type="query"/>
<cfscript>
var result = "";
switch (arguments.selectedGrouping) {
case "Building" :
result = "Building: #arguments.outputQuery.building_code# - #arguments.outputQuery.building_name#";
break;
case "PI Name" :
result = "PI: #arguments.outputQuery.pi_name#";
break;
case "Custodial Code" :
result = "Custodial Code: #arguments.outputQuery.custodial_code# - #arguments.outputQuery.custodian_desc#";
break;
case "Manufacturer" :
result = "Manufacturer: #arguments.outputQuery.manufacturer_name#";
break;
case "Room Number" :
result = "Room Number: #arguments.outputQuery.building_room_number#";
break;
case "Current UC Fund" :
result = "Current UC Fund : #arguments.outputQuery.cur_uc_fnd#";
break;
case "Original UC Fund" :
result = "Original UC Fund: #arguments.outputQuery.orig_uc_fnd#";
break;
case "EFA Fund Source" :
result = "EFA Fund Source: #arguments.outputQuery.efa_fnd_src_cd#";
break;
case "Asset Status" :
result = "Asset Status: #arguments.outputQuery.asset_status_name#";
break;
}
return result;
</cfscript>
</cffunction>
Any suggestion?
Thanks.
Assuming this is something like the code you implemented that worked for you, I'm posting this as an answer in case it's of benefit to anyone else in the future.
Simply pass in the current row value to the function so you can easily access that row in your query, something like:
<h2>#eqInventoryHelper.getGroupingHeaderText(Group1, Output, Output.currentRow)#</h2></cfoutput>
<cffunction name="getGroupingHeaderText" output="false">
<cfargument name="selectedGrouping" required="true" type="string"/>
<cfargument name="outputQuery" required="true" type="query"/>
<cfargument name="whichRow" required="true" type="numeric"/>
<cfscript>
var result = "";
switch (arguments.selectedGrouping) {
case "Building" :
result = "Building: #arguments.outputQuery.building_code[arguments.whichRow]# - #arguments.outputQuery.building_name[arguments.whichRow]#";
break;