Today's ColdFusion Puzzler is based on a cool Groovy feature. I was surprised to discover that Groovy supports a Dump function. While I don't find it as pretty as ColdFusion's version, it's nice to have when debugging. But Groovy takes it a bit further and adds something similar called the inspect() function. The inspect function will take any arbitrary object and return a string that could be used to create it. Here is an example:
def s = [
name:"Raymond",
age:35,
rank:"Jedi"
]
def a = [0,2,3]
def b = new Date()
s.a = a
s.bornondate = b
println s.inspect()
This returns:
["name":"Raymond", "age":35, "rank":"Jedi", "a":[0, 2, 3], "bornondate":Fri Sep 12 08:48:16 CDT 2008]
As you can see, it isn't the code I used but code that would generate the same data.
Your challege, should you choose to accept it, is to write a similar function for ColdFusion. Your output need not look the exact same of course. I've provided a simple example that only works with arrays to get your started.
<cfscript>
function inspect(arr) {
var r = "";
var i = "";
r = "[";
for(i=1; i <= arrayLen(arr); i++) {
r &= arr[i];
if(i < arrayLen(arr) ) r&=",";
}
r &= "]";
return r;
}
</cfscript>
<cfset a = [1,2,9,20]>
<cfoutput>#inspect(a)#</cfoutput>
Your code should handle arrays, structs, and simple values. For extra credit you can handle queries to by using a bunch of query set sells.
Also note that my test UDF returns a literal value like Groovy. You can also return a series of statements instead:
ob = arrayNew(1); ob[1] = 1; ob[2] = 2; etc
Note that I used "ob" to represent the top level data. Since I pass the variable, and not the variable name, I chose an arbitrary variable name to store the data.
Enjoy!
Archived Comments
This is probably cheating but hey why reinvent the wheel?
<CFSCRIPT>
a.testvar1 = 'this is part 1';
a.testvar2 = 'this is part 2';
a.testvar3 = [1,2,9,20];
writeoutput(inspect(a));
</CFSCRIPT>
<CFFUNCTION NAME="inspect" RETURNTYPE="STRING">
<CFARGUMENT NAME="item" REQUIRED="YES">
<CFSAVECONTENT VARIABLE="contRet">
<CFDUMP VAR="#arguments.item#" FORMAT="TEXT">
</CFSAVECONTENT>
<CFRETURN contRet>
</CFFUNCTION>
--Dave
I don't mind cheating (if it is good enough for Captain Kirk, it is good enough for me), but that doesn't solve the problem. The result should be a string that could be executed to create the same data.
How about:
<cffunction name="Inspect">
<cfreturn (
"DeserializeJSON(" &
SerializeJSON( ARGUMENTS[ 1 ] ) &
")"
) />
</cffunction>
@Ray
The inspect() method on objects in Groovy is actually stolen from ruby. :)
The fact that you can't create implicit queries and that implicit arrays and structs aren't allowed in expression contexts reduces the value of the CF kind of function quite a lot.
Instead you'd end up with tons of temporary variables. :/
@Ben - Nice. :)
@Elliott - Thanks for the info re: inspect. As for the usefulness - please remember - this is for fun, nothing more. ;)
Ben stole my idea.
<cffunction name="inspect">
<cfargument name="ob" required="true" type="any" />
<cfset var ret = "" />
<cfwddx action="cfml2wddx" input="#arguments.ob#" output="ret" />
<cfreturn toString(ret) />
</cffunction>
<cfset a = arrayNew(1) />
<cfset b = structNew() />
<cfset b.name = "todd" />
<cfset b.skillLevel = "superior" />
<cfset arrayAppend(a,b) />
<cfquery name="c" datasource="cfartgallery">
select *
from artists
</cfquery>
<cfset arrayAppend(a,c) />
<cfdump var="#a#">
<cfset i = inspect(a) />
<cfoutput>#i#</cfoutput>
@Todd,
:P ... I like the WDDX approach also.
@Todd - Your solution is as wrong as the first commenter. It needs to be something that can be executed or run via cfinclude.
OK so then what im i doing wrong then - I can't get Ben's solution to do what you're looking for either?
Whoa, whoa, don't pick on me :)
OK I'm sure I over complicated this (I'm medicated today) :) But this works --
<cffunction name="inspect">
<cfargument name="ob" required="true" type="any" />
<cfset var towddx = "" />
<cfset var ret = "" />
<cfwddx action="cfml2wddx" input="#arguments.ob#" output="towddx" />
<cfsavecontent variable="ret">
<cfwddx action="wddx2cfml" input="<cfoutput>#towddx#</cfoutput>" output="cfml" />
<cfdump var="#cfml#" />
</cfsavecontent>
<cfreturn replace(replace(ret, "<", "<", "all"), ">", ">", "all") />
</cffunction>
<cfset a = arrayNew(1) />
<cfset b = structNew() />
<cfset b.name = "todd" />
<cfset b.skillLevel = "superior" />
<cfset arrayAppend(a,b) />
<cfquery name="c" datasource="cfartgallery">
select *
from artists
</cfquery>
<cfset arrayAppend(a,c) />
<cfset i = inspect(a) />
<cfdump var="#i#">
<cfset tempfile = "#getDirectoryFromPath(getCurrentTemplatePath())#/inspect.cfm" />
<cffile action="write" file="#tempfile#" output="#i#" />
<cfinclude template="inspect.cfm" />
OK, I took a stab at it. Got it working for strings, arrays, and structures. Anyone wanna extend to queries? =)
<!---// Code //--->
<cfscript>
function inspect(obj) {
var result = { dataType = '', code = '' };
result.dataType = getDataType(obj);
switch (result.dataType) {
case 'array':
result.code = '<cfset theVar = {';
for (x = 1; x LTE arrayLen(obj); x++) {
result.code = result.code & obj[x];
if (x != arrayLen(obj)) {
result.code = result.code & ',';
}
}
result.code = result.code & '} />';
break;
case 'struct':
result.code = '<cfset theVar = { ';
structKeyArr = structKeyArray(obj);
for (x=1; x LTE arrayLen(structKeyArr); x++) {
result.code = result.code & structKeyArr[x] & '=' & obj[structKeyArr[x]];
if (x != arrayLen(structKeyArr)) {
result.code = result.code & ',';
}
}
result.code = result.code & '} />';
break;
case 'string':
result.code = '<cfset theVar = "' & obj & '" />';
break;
}
return result;
}
function getDataType(obj) {
var dataType = 'unknown';
if (isSimpleValue(obj)) {
dataType = 'string';
}
if (isArray(obj)) {
dataType = 'array';
}
if (isStruct(obj)) {
dataType = 'struct';
}
if (isQuery(obj)) {
dataType = 'query';
}
return dataType;
}
</cfscript>
<cfset theArray = [12,23,23,345,56] />
<cfset theStruct = { test="yes", age=45, message="hello" } />
<cfset theString = "Hello" />
<cfoutput>
String Result: <cfdump var="#inspect(theString)#"><br />
Array Result: <cfdump var="#inspect(theArray)#"><br />
Structure Result: <cfdump var="#inspect(theStruct)#"><br />
</cfoutput>
and I posted it too fast, revised code to fix missing quotes in the structure creation =)
<!---// Code //--->
<cfscript>
function inspect(obj) {
var result = { dataType = '', code = '' };
result.dataType = getDataType(obj);
switch (result.dataType) {
case 'array':
result.code = '<cfset theVar = {';
for (x = 1; x LTE arrayLen(obj); x++) {
result.code = result.code & obj[x];
if (x != arrayLen(obj)) {
result.code = result.code & ',';
}
}
result.code = result.code & '} />';
break;
case 'struct':
result.code = '<cfset theVar = { ';
structKeyArr = structKeyArray(obj);
for (x=1; x LTE arrayLen(structKeyArr); x++) {
result.code = result.code & structKeyArr[x] & '= "' & obj[structKeyArr[x]] & '"';
if (x != arrayLen(structKeyArr)) {
result.code = result.code & ',';
}
}
result.code = result.code & '} />';
break;
case 'string':
result.code = '<cfset theVar = "' & obj & '" />';
break;
}
return result;
}
function getDataType(obj) {
var dataType = 'unknown';
if (isSimpleValue(obj)) {
dataType = 'string';
}
if (isArray(obj)) {
dataType = 'array';
}
if (isStruct(obj)) {
dataType = 'struct';
}
if (isQuery(obj)) {
dataType = 'query';
}
return dataType;
}
</cfscript>
<cfset theArray = [12,23,23,345,56] />
<cfset theStruct = { test="yes", age=45, message="hello" } />
<cfset theString = "Hello" />
<cfoutput>
String Result: <cfdump var="#inspect(theString)#"><br />
Array Result: <cfdump var="#inspect(theArray)#"><br />
Structure Result: <cfdump var="#inspect(theStruct)#"><br />
</cfoutput>
Nice Justice! Now, how about making it work for nested structs of structs and arrays and nested arrays of arrays and structs? :)
sounds like someone's being used for free broadchoice development to me.......
@shag - heh, Groovy already supports this. ;)
thats groovy, but it was funny that @sean piped in. his comments sounded kinda like, "yeah, but we needed it to do this". i just thought it was funny.. shoulda taged it with a wink. ;-)