CFScript switch statement throws error when passed a string? - coldfusion

Update:
Thanks Ben, I decided to copy the URL to another structure and modify that one with StructUpdate(). Here's the code if anyone's interested (specific to my application, but you can edit the lines with comments to get a useful function).
function rebuildURL(key, value)
{
var URLstring = "";
var VarCount = 0;
var tmpURL = duplicate(URL);
var VarSeparator = "";
structUpdate(tmpURL, arguments.key, arguments.value);
for (key in tmpURL)
{
if (tmpURL[key] neq "" and tmpURL[key] neq "10000" and tmpURL[key] neq "1") `<!--- remove the tmpURL[key] neq "10000" and "1"--->`
{
if (VarCount neq 0)
{
VarSeparator = "&";
}
else
{
VarSeparator = "";
}
URLstring = URLstring & VarSeparator & "#Lcase(key)#" & "=#Lcase(tmpURL[key])#";
VarCount = VarCount + 1;
}
}
structClear(tmpURL); `<!---not sure if this is necessary, but probably can't hurt unless you are processing thousands of links --->`
return(URLstring);
}
Thanks again!
Scott
Hey guys,
I'm writing a custom function to rework the URL for links in my pages, and I'm getting the following error:
Complex object types cannot be converted to simple values.
The expression has requested a variable or an intermediate expression result as a simple value, however, the result cannot be converted to a simple value. Simple values are strings, numbers, boolean values, and date/time values. Queries, arrays, and COM objects are examples of complex values.
The most likely cause of the error is that you are trying to use a complex value as a simple one. For example, you might be trying to use a query variable in a cfif tag.
The error occurred in C:\ColdFusion8\wwwroot\pascalnew\turbos.cfm: line 8
Called from C:\ColdFusion8\wwwroot\pascalnew\turbos.cfm: line 108
Called from C:\ColdFusion8\wwwroot\pascalnew\turbos.cfm: line 93
Called from C:\ColdFusion8\wwwroot\pascalnew\turbos.cfm: line 1
Called from C:\ColdFusion8\wwwroot\pascalnew\turbos.cfm: line 1
6 : URLvar = "#URL#";
7 : switch(param)
8 : {
9 : case 'mfr':
10 : {
Here's my function code:
<cfscript>
function SetURL(param, paramval)
{
URLvar = "#URL#";
switch(param)
{
case 'mfr':
{
IF (URLvar contains "mfr")
{
REReplaceNoCase(URLvar, "mfr=^[^\&]", "mfr=#paramval#", "All");
}
break;
}
}
return(URLvar);
}
</cfscript>
Here's what I was testing it with:
<cfset urlvar = SetUrl("mfr", "edwards")>
<cfdump var="#urlvar#">
How is "mfr" a complex variable??
Thanks,
Scott

When you use CFScript, some versions report the beginning of the block as the line with the error.
When you assign "#URL#" to URLVar, you are creating a pointer to the URL scope. then, you attempt to use the contains operator on it. Contains, however, only compares two simple values.
So, your attempt to reference a complex value as a scalar actually comes here:
IF (URLvar contains "mfr")
{
REReplaceNoCase(URLvar, "mfr=^[^\&]", "mfr=#paramval#", "All");
}
At a guess, you are trying to look at the URL itself, not the URL scope. You can assemble this from parts of the CGI scope, including SERVER_NAME, SCRIPT_NAME, AND QUERY_STRING (or you can look at the individual part you need).
Added: If you want to know if a variable is passed in the url, I think you are overthinking this. Let's say you have a param and a paramval to replace it with. You could do it like this:
function paramReplace(param, paramVal, scope)
{
if(structkeyexists(arguments.scope, arguments.param))
{
arguments.scope[arguments.param] = arguments.paramVal;
}
}
paramReplace("mfr", "fred", URL);
This simply uses structKeyExists to find out if that variable exists in the appropriate scope, then replaces the value if it does. If you need to rebuild your actual query string, you can do so later. This avoids scenarios where you get bad data if your query string contains something like "zone=mfr".
I've not tested this -- it's off the cuff -- so it may need tweaking, but it should get you started.

Related

Google app script IF condition not matching 0, empty and null

I have issues with Google app script IF condition.
Problem i am facing its not returning value TRUE rather going to next/ Else statements.
Code i am having:
const numberOfRowsToUpdate = deliveryDate.length;
// For each item making the for loop to work
for (i=0 ; i < numberOfRowsToUpdate;i++) {
debugger;
var dp = depositAmount[i];
if(dp!==""|| dp!==0 || dp !==null || dp!==isblank())
{ .... <statements>
}
}
I want to check whether particular cell of the array is empty / zero / returning null value.
thanks in advance for the help.
SUGGESTION
I have used a similar script I'm using for a spreadsheet in which I need to search through every row for some data, but obviously adpating it to your case, and since I don't have your full code (and still can't comment asking for more info due to my recent joining in SO), I had to simplify it, in hope it will work for you.
What I did was use your incrementing i index from the for loop and use it to scan every row, while adjusting it to fit your array index, because we can't have i = 0 as a row index, and it would skip the first value on the array if left as i = 1).
SCRIPT
function test(){
const n = 6;
var depositAmount = [7,2,0,2,0,8];
// For each item making the for loop to work
var ss = SpreadsheetApp.getActive();
Logger.log(ss.getName());
for (var i=1 ; i <= n ;i++) {
debugger;
ss.getRange("A"+i).setValue(1);
var dp = depositAmount[i-1];
Logger.log(dp)
if(dp != "" || dp != 0 /*|| dp != null || dp != isblank()*/)
{
ss.getRange("B"+i).setValue(dp);
}
else
{
ss.getRange("C"+i).setValue("VOID")
Logger.log(i-1+"th index of array is "+ss.getRange("C"+i).getValue());
}
}
};
RESULTS
After running it with the four original conditions you used, i didn't get the expected result, as you must have, leading to this:
.
While studying your original code, I stumbled upon this question about the differences between == and ===, as well as != and !==.
So before I used this in our favor, I tried the old trial and error method, using only one condition at a time, and then stacking them up. Not only I managed to find out the !== operator didn't work properly for this case, but also the comparison with null and the isblank() function (at least in my case, because i haven't defined it, and I'm not sure it is a built-in function) also don't work with either operator.
Therefore, using the != operator helps you better than the strict !==.
The result of the final script is that:
.
NOTES
I also tried using a null value within the array ([7,2,0,2,,8]), but it would always break away from the loop, never scanning the whole array, and I don't know how to circle that.
Here is the Execution Log for this script:
EDIT
While fooling around, I found this question and the answer by Etienne de Villers might be even faster to apply, or at least more useful for your purposes.

In POSTMAN how do i get substring of response header item?

I am using postman to get response header value like below:
var data = postman.getResponseHeader("Location") . //value is "http://aaa/bbb" for example
I can print the value via console.log(data) easily.
However, what I really want is "bbb". So I need some substring() type of function. And apparently 'data' is not a javascript string type, because data.substring(10) for example always return null.
Does anyone what i need to do in this case?
If any postman API doc existing that explains this?
You can set an environment variable in postman. try something like
var data = JSON.parse(postman.getResponseHeader("Location"));
postman.setEnvironmentVariable("dataObj", data.href.substring(10));
You have the full flexibility of JavaScript at your fingertips here, so just split the String and use the part after the last /:
var data = pm.response.headers.get("Location").split("/").pop());
See W3 school's documentation of split and pop if you need more in depth examples of JavaScript internals.
Some initial thought - I needed a specific part of the "Location" header like the OP, but I had to also get a specific value from that specific part.
My header would look something like this
https://example.com?code_challenge_method=S256&redirect_uri=https://localhost:8080&response_type=code&state=vi8qPxcvv7I&nonce=uq95j99qBCGgJvrHjGoFtJiBoo
And I need the "state" value to pass on to the next request as a variable
var location_header = pm.response.headers.get("Location");
var attributes = location_header.split('&');
console.log(attributes);
var len = attributes.length;
var state_attribute_value = ""
var j = 0;
for (var i = 0; i < len; i++) {
attribute_key = attributes[i].split('=')[0];
if (attribute_key == "state") {
state_attribute_value = attributes[i].split('=')[1];
}
j = j + 1;
}
console.log(state_attribute_value);
pm.environment.set("state", state_attribute_value);
Might you get the point here, "split" is the choice to give you some array of values.
If the text you are splitting is always giving the same array length it should be easy to catch the correct number

Prestashop WebService "limit" parameter not working as expected

I'm trying to use the limit parameter in PrestaShop's WebService.
My problem is it's only half working, the number of entries to display is working fine, but the "starting point" is not.
For example:
http://example.com/api/combinations?display=[id,reference]&limit=[0,500]&sort=id_ASC&ws_key=WEBSERVICEKEY
AND
http://example.com/api/combinations?display=[id,reference]&limit=[500,500]&sort=id_ASC&ws_key=WEBSERVICEKEY
Returns exactly the same products (first 500).
I am using PS 1.6.1.17
I've updated the classes/webservice folder with the latest (from 1.6.1.18).
The code (classes/webservice/WebserviceRequest.php) - no override:
$sql_limit = '';
if (isset($this->urlFragments['limit'])) {
$limitArgs = explode(',', $this->urlFragments['limit']);
if (count($limitArgs) > 2) {
$this->setError(400, 'The "limit" value has to be formed as this example: "5,25" or "10"', 39);
return false;
} else {
$sql_limit .= ' LIMIT '.(int)($limitArgs[0]).(isset($limitArgs[1]) ? ', '.(int)($limitArgs[1]) : '')."\n";// LIMIT X|X, Y
}
}
The code looks fine, maybe it's SQL related? I'd like to print the generated query to test it but I couldn't fin a way...
I've found the problem.
The int cast wasn't working here.
$this->urlFragments['limit'] value is [500,500].
So after explode $limitArgs[0] is equal to [500 but the code (int)($limitArgs[0]) was always giving me 0 instead of 500 as result.
I replaced it with an ugly str_replace as follow:
$sql_limit .= ' LIMIT '.str_replace("[", "", $limitArgs[0]).(isset($limitArgs[1]) ? ','.(int)($limitArgs[1]) : '')."\n";// LIMIT X|X, Y
Not the best, but could be usefull if anyone faces this situation.
edit: I posted on the forge, the correct syntax for limit is limit=X,Y not limit=[X,Y].

Regex appears to return pos >1 but length 0

The following code is returning an error on Mid, saying the third argument is -2 - so it thinks the length is 0. We're totally stumped as to how this could happen. The code looks for values between curly braces and strips them out. Can you think of a way to break this?
Str can be anything - we don't know, it's not supplied by us - so that's the var you want to break.
str = "Here's a string with {EmailAddy} and maybe some {otherVariables}";
start = 1;
pos = 0;
length = 0;
tokens = ArrayNew(1);
while(true) {
x = REFind("\{\w*\}", str, start, true);
pos = x.pos[1];
length = x.len[1];
if (pos == 0) {
break;
} else {
// get the token, trimming the curly brackets
token = mid(str, pos+1, length-2);
arrayAppend(tokens, token);
start = pos + length;
}
}
WriteDump(tokens);
You don't need lookbehind:
var Tokens = rematch( "\{\w*(?=\})" , Arguments.Str );
for ( var i = 1 ; i LTE ArrayLen(Tokens) ; i++ )
Tokens[i] = Tokens[i].substring(1);
return Tokens;
And that code should also give you a clue as to the most likely cause of the code breaking, in that you've probably got it in a function in a persisted component, but (without any scoping) everything is going in the component's variables scope, and thus it's not thread-safe and - with multiple calls under load - the variables involved are liable to get corrupted.
This is a general issue you should be looking for throughout the code - generally the first assignment for every variable inside a function should be prefixed with either the var keyword (or explicitly the local. scope) to ensure it it local to that function and not global the the component. (Except of course in the instances when a global variable is what is desired.)
Oh, and if you ever do actually want/need to use lookbehind in CF, I've made cfRegex, a library that wraps Java's more powerful regex engine, providing support for lookbehind (with limited-width), and with a (hopefully) easy to use and consistent set of functions for interacting with it.

Checking lists and running handlers

I find myself writing code that looks like this a lot:
set<int> affected_items;
while (string code = GetKeyCodeFromSomewhere())
{
if (code == "some constant" || code == "some other constant") {
affected_items.insert(some_constant_id);
} else if (code == "yet another constant" || code == "the constant I didn't mention yet") {
affected_items.insert(some_other_constant_id);
} // else if etc...
}
for (set<int>::iterator it = affected_items.begin(); it != affected_items.end(); it++)
{
switch(*it)
{
case some_constant_id:
RunSomeFunction(with, these, params);
break;
case some_other_constant_id:
RunSomeOtherFunction(with, these, other, params);
break;
// etc...
}
}
The reason I end up writing this code is that I need to only run the functions in the second loop once even if I've received multiple key codes that might cause them to run.
This just doesn't seem like the best way to do it. Is there a neater way?
One approach is to maintain a map from strings to booleans. The main logic can start with something like:
if(done[code])
continue;
done[code] = true;
Then you can perform the appropriate action as soon as you identify the code.
Another approach is to store something executable (object, function pointer, whatever) into a sort of "to do list." For example:
while (string code = GetKeyCodeFromSomewhere())
{
todo[code] = codefor[code];
}
Initialize codefor to contain the appropriate function pointer, or object subclassed from a common base class, for each code value. If the same code shows up more than once, the appropriate entry in todo will just get overwritten with the same value that it already had. At the end, iterate over todo and run all of its members.
Since you don't seem to care about the actual values in the set you could replace it with setting bits in an int. You can also replace the linear time search logic with log time search logic. Here's the final code:
// Ahead of time you build a static map from your strings to bit values.
std::map< std::string, int > codesToValues;
codesToValues[ "some constant" ] = 1;
codesToValues[ "some other constant" ] = 1;
codesToValues[ "yet another constant" ] = 2;
codesToValues[ "the constant I didn't mention yet" ] = 2;
// When you want to do your work
int affected_items = 0;
while (string code = GetKeyCodeFromSomewhere())
affected_items |= codesToValues[ code ];
if( affected_items & 1 )
RunSomeFunction(with, these, params);
if( affected_items & 2 )
RunSomeOtherFunction(with, these, other, params);
// etc...
Its certainly not neater, but you could maintain a set of flags that say whether you've called that specific function or not. That way you avoid having to save things off in a set, you just have the flags.
Since there is (presumably from the way it is written), a fixed at compile time number of different if/else blocks, you can do this pretty easily with a bitset.
Obviously, it will depend on the specific circumstances, but it might be better to have the functions that you call keep track of whether they've already been run and exit early if required.