Coding conventions for writing cfc's in CF9? - coldfusion

With the new ways of writing CFC in CF9, what are some of the coding convention new to CF9?
Here are some I can think of...
always use LOCAL scope
always include init() method that returns itself, since New will call init() if found.
do not put required arguments in init() if it is an ORM entity, otherwise expect Exceptions...
always use THIS.setXXX in where XXX is property name inside init(), so that it will call the implicit setters or custom setter if available.
abandon the pre-CF8 INSTANCE scope convention, see: http://henrylearnstorock.blogspot.com/2009/08/should-we-abandon-instance-scope-in-cf9.html
no output=false for component and functions in script style CFC, see: http://www.coldfusionjedi.com/index.cfm/2009/8/26/Ask-a-Jedi-Impact-of-whitespace-and-script-based-CFCs
use the cleaner and more efficient isNull(arguments.optionalArg) instead of isDefined()

do we still need to set attribute output=false for component and functions in script style CFC?
I wouldn't think so. <cfscript> by its nature suppresses any whitespace and needs writeOutput() in order to have any output at all.

Your init() method doesn't have to return the "this" scope if you are calling it using the "new my.cfc()" syntax. True story.
If you are inside a cfc and want to set a property, dont use this.setFoo(), just go setFoo().
Same goes for getFoo().
The this.xxx() is like going out the front door only to come back in. Also, your access=private custom getters and setters wont work as the functions wont be in the this scope.
"var foo" vs "local.foo" - personally, I prefer var'd variables as there is a) less code to type, and b) less code to read.
// there isnt a huge difference here
var today = now();
var tomorrow = dateAdd( 'd', 1, today );
local.today = now();
local.tomorrow = dateAdd( 'd', 1, local.today );
// but when you start getting more complex examples, it quickly blows out
var out = method( var1, var2, var3, var4, var5 );
local.out = method( local.var1, local.var2, local.var3, local.var4, local.var5 );
Use javadocs style comments. Documentation is your friend.
/**
* #hint This is a hint for the whole function
* #arg1 This is an argument hint
* #arg2 This is another argument hint
**/
public void function myFunction( string arg1 = 'default', boolean arg2 ) {
return true;
}

all functions that alter data should return some value even if it is a boolean that is currently always true. Functions have a way of eventually needing to return false

Related

Java code migration error using com.jayway.jsonpath.JsonPath

I am using Java code and converting the code in ColdFusion. There are some challenges where I am stuck. This is one function I have in Java:
import com.jayway.jsonpath.JsonPath;
import net.minidev.json.JSONArray;
import net.minidev.json.JSONStyle;
private static String getDetails(String instaDetailsElement) {
String jsonResponse = instaDetailsElement.split(" = ")[1];
JSONArray mediaArray = JsonPath.read(jsonResponse, "$.entry_data.PostPage[:1].graphql.shortcode_media");
String returnJsonString = mediaArray.toJSONString(new JSONStyle(JSONStyle.FLAG_IGNORE_NULL));
System.out.println(returnJsonString);
return returnJsonString;
}
These two lines are giving me some trouble:
var mediaArray = JsonPath.read(jsonResponse, "$.entry_data.PostPage[:1].graphql.shortcode_media");
var returnJsonString = mediaArray.toJSONString(new JSONStyle(JSONStyle.FLAG_IGNORE_NULL));
Here is what I attempted so far. I loaded the jar library for JSON path and tried using it like this:
Application.cfc settings
<cfset this.javaSettings = {LoadPaths = ["cfc/jar"], loadColdFusionClassPath = true, reloadOnChange = false}>
CF Code:
public any function getDetails(String instaDetailsElement) {
var jsonResponse = instaDetailsElement.split(" = ")[1];
var JsonPath = Createobject("java","com.jayway.jsonpath.JsonPath");
writedump(application);
var mediaArray = JsonPath.read(jsonResponse, "$.entry_data.PostPage[:1].graphql.shortcode_media");
writedump(mediaArray); abort;
var returnJsonString = mediaArray.toJSONString(new JSONStyle(JSONStyle.FLAG_IGNORE_NULL));
return returnJsonString;
}
I'm able to view the class methods when I dump the JsonPath object (screen shot), but when I try to call JsonPath.read() I get this error:
No matching Method for read(string, string) found for
com.jayway.jsonpath.JsonPath
TL;DR;
No matching method for read(string, string) found for com.jayway.jsonpath.JsonPath
Technically the error message is correct: there is no read() method that accepts two strings (even though that's how it's used in the java code). The method actually expects three arguments:
Pass in an empty array for the 3rd argument:
JsonPath.read(jsonResponse, "$.entry_data.PostPage[:1].graphql.shortcode_media", []);
Explanation:
String jsonResponse = instaDetailsElement.split(" = ")[1];
JsonPath.read(jsonResponse, "$.entry_data.PostPage[:1].graphql.shortcode_media")
If there really is no read(String, String) method, you might wonder why the java code works at all, since that's exactly what it uses. It works due to a special feature of java.
The documentation shows the overloaded read(..) method actually has three parameters, but one of them is special:
read(String json,
String jsonPath,
Predicate... filters)
Notice the ... after the class name (Predicate)? It's a construct called "varargs" (or variable number of arguments):
You can use a construct called varargs to pass an arbitrary number of values to a method. You use varargs when you don't know how many of a
particular type of argument will be passed to the method. It's a
shortcut to creating an array manually ...
To use varargs, you follow the type of the last parameter by an ellipsis (three dots, ...), then a space, and the parameter name. The
method can then be called with any number of that parameter,
including none.
So in java you're allowed to omit the third argument entirely and call read(String, String) with two strings. ColdFusion doesn't support that syntax, because it creates too much ambiguity. So in lieu of omitting the argument, you can pass in an empty array instead:
JsonPath.read(jsonResponse, "$.entry_data.PostPage[:1].graphql.shortcode_media", []);
(Since this has turned into two questions in one thread, I'm separating the second answer out for clarity ...)
var returnJsonString = mediaArray.toJSONString(new JSONStyle(JSONStyle.FLAG_IGNORE_NULL));
As for translating the JSONStyle code, it helps to unpack nested code from the inside out. Then tackle each piece separately:
mediaArray.toJSONString(new JSONStyle( JSONStyle.FLAG_IGNORE_NULL ));
mediaArray.toJSONString( new JSONStyle( JSONStyle.FLAG_IGNORE_NULL ) )
mediaArray.toJSONString( new JSONStyle( JSONStyle.FLAG_IGNORE_NULL ) )
Piece #1
Uses a static field of the JSONStyle class named FLAG_IGNORE_NULL. To access the field, create a reference to that class:
JsonStyle = createObject("java", "net.minidev.json.JSONStyle");
writeDump(JSONStyle.FLAG_IGNORE_NULL);
Piece #2
Creates a brand new instance of the JSONStyle class, using the static field from above. Use createObject() to create the new instance, passing the static field into the psuedo constructor init():
newJsonStyle = createObject("java", "net.minidev.json.JSONStyle").init(JSONStyle.FLAG_IGNORE_NULL);
writeDump( newJsonStyle );
Piece #3
All that's left is calling the JSONArray.toJSONString() method with the JSONStyle object you just created:
result = mediaArray.toJSONString( newJsonStyle );
writeDump(result);

onCFCRequest seems to be eating my return value

I have a remote function in a component that's being affected by my Application.cfc - onRequestStart is requiring the user to login. I don't want to move the .cfc into another subfolder - I want to keep it as close to the .cfm as possible so that I don't have to go hunting for the cfc while editing the .cfm.
I think the solution is to use onRequest and onCFCRequest instead of onRequestStart, but in experimenting with onCFCRequest, it looks like the return value has to be boolean. So now my remote function is returning a boolean instead of the query that it used to.
Rays' example shows outputting the result, but what about returning the result?
Here's what I tried, but ColdFusion balked at the return value not being boolean:
public boolean function onCFCRequest(string cfc, string method, struct args) {
local.comp = createObject("component", arguments.cfc);
local.result = evaluate("local.comp.#arguments.method#(argumentCollection=arguments.args)");
return local.result;
}
Your problem is this assertion:
in experimenting with onCFCRequest, it looks like the return value has
to be boolean
That's not true.
It can (and should) be "any", in which case it'll return whatever data type the called method returns (serialised so it can be transmitted in the HTTP response, natch).

Coldfusion Proxy confusion

I am using a proxy/delegate pattern in a coldfusion component, and am getting unexpected results (from my point of view). Below is my proxy component - its pretty straight forward, I just init the CFC with the actual component I want to delegate to, and then map the named functions from that CFC through to a proxy function (the below is simplified for this example)
I have created a proxy component as follows:
component output="false"{
/** Constructor for proxy - requires an instance of myFusebox **/
public MyFuseboxProxy function init( Required any myFb ){
variables.myFusebox = arguments.myFb;
return this;
}
this.do = variables.proxy;
this.getApplication = variables.proxy;
this.getApplicationData = variables.proxy;
private any function proxy(){
var local.functionName = getFunctionCalledName();
var local.function = variables.myFusebox[local.functionName];
var local.returnVal = local.function( argumentCollection=arguments );
return local.returnVal;
}
}
From my application I call the following code:
variables.myFusebox = new ab.MyFuseboxProxy( variables.myFusebox );
variables.myFusebox.getApplicationData().startTime = now();
Now, in the above scenario, I would expect my proxy component to map the getApplicationData() function straight through to the original myFusebox component (via my proxy() function).
That function in the underlying component is as follows:
<cffunction name="getApplicationData" returntype="struct" access="public" output="false"
hint="I am a convenience method to return a reference to the application data cache.">
<cfreturn getApplication().getApplicationData() />
</cffunction>
That proxy all works fine, however, once I am in the above function in the original myFusebox I get the following error:
Message: Variable GETAPPLICATION is undefined.
StackTrace: coldfusion.runtime.UndefinedVariableException: Variable GETAPPLICATION is undefined.
And if I dump "this" inside that function, it actually dumps my proxy object.
Can anyone explain this or what I have done wrong? I was expecting that once the function call was inside the underlying object, it would just use its own context from there (my proxy just being a pass through really to the delegate)
I think this is the key point:
I was expecting that once the function call was inside the underlying
object
You've got this:
private any function proxy(){
var local.functionName = getFunctionCalledName();
var local.function = variables.myFusebox[local.functionName];
var local.returnVal = local.function( argumentCollection=arguments );
return local.returnVal;
}
When you do this bit:
var local.function = variables.myFusebox[local.functionName];
you are effectively pulling the function referenced by local.functionName out of variables.myFusebox, and putting it into the current function, within the context of your MyFuseboxProxy instance.
So when you do this:
var local.returnVal = local.function( argumentCollection=arguments );
You are not running variables.myFusebox[local.functionName]() (so in the context of variables.myFusebox), but you are running local.function() (so in the context of your proxy object).
I don't have the patience to try to follow your logic here, but I am still surprised you get that error. I would have expected this to happen:
local.function (a reference to getApplicationData from variables.myFusebox) runs getApplication().
getApplication() in the context of the MyFuseboxProxy instance should be a reference to variables.proxy().
variables.proxy() resolves the proxied function as getApplication(), and pulls that out of variables.myFusebox, and runs it in the context of your MyFuseboxProxy instance.
You do not include the code of the getApplication() function from variables.myFusebox, so I dunno what would happen next, but this is not what you want to be happening.
Anyway, the crux is - I think - that instead of running the functions inside variables.myFusebox, you're running them in your MyFuseboxProxy instance instead. If you want to do this sort of proxying (and ignoring for a moment you have invoke() specifically for doing this), you need to still call the function in its original context, not reference it in some new context.
I guess you're doing all this horsing around because ColdFusion doesn't like this syntax:
someObject[someMethodName]()
It baulks at the []() notation. However the solution is not this:
someOutOfContextReference = someObject[someMethodName]
result = someOutOfContextReference()
It's this:
someObject.someInContextReference = someObject[someMethodName]
result = someObject.someInContextReference()
See the subtle difference?
ColdFusion functions are not intrinsically closures, which is what you'd need them to be to work the way you want.

Inspect Ember.js: Get the type of an object (Class)?

I use console.log() a lot, especially in combination with Ember.inspect(). But there's one thing I miss:
How can I find out the type of an object (Class)?
For example: Getting something like <Sandbox.ApplicationController:ember288> when inspecting Ember.get("controller")?
If you just want the model name (for example app/models/comment.js has the model name comment), you can use thing.constructor.modelName.
For example:
var aComment = this.get('store').createRecord('comment');
aComment.get('constructor.modelName') // => 'comment'
I understand you are looking for a string for debugging purposes, but I originally came to this question wanting to know specifically how to get the type of the object, not a string describing the object.
Using the built in Javascript property constructor will yield the class used to construct the instance. For example you could do:
person = App.Person.create();
person.constructor // returns App.Person
person.constructor.toString() // return "App.Person"
If you get Class, you can usually call toString() (or as a shortcut concat an empty string + '') to get something like <Sandbox.ApplicationController:ember288>
Another useful feature (in chrome) is the dir command.
dir(App.User)
This will give you the full object information, rather than just the name.
Be aware that some of these answers suggested here only work in development. Once your code is in production most of those methods / class names will get minified.
import Model from '#ember-data/model';
export default class Animal extends Model {
// ...
}
So in development:
const model = this.store.createRecord('animal');
model.constructor.name // returns Animal
in production:
const model = this.store.createRecord('animal');
model.constructor.name // returns 'i' (or any other single letter).
To avoid this, use constructor.toString()
const model = this.store.createRecord('animal');
model.constructor.toString() // returns 'model:animal'

ColdFusion 9 Dynamic Method Call

I am trying to work out the correct <cfscript> syntax for calling a dynamic method within ColdFusion 9. I have tried a number of variations and had a good search around.
<cfinvoke> is clearly the tag I want, sadly however I cannot use this within my pure cfscript component as it was implemented in ColdFusion 10.
i.e coldfusion 9 dynamically call method
I have tried the following within my CFC:
/** Validate the method name **/
var resources = getResources();
if (structKeyExists(variables.resources, name)) {
variables.resourceActive[name] = true;
var reflectionMethod = resources[name];
var result = "#reflectionMethod.getMethodName()#"(argumentCollection = params);
}
Where the return value of reflectionMethod.getMethodName() is the method name I want to call. It is 100% returning the correct value (the name of the method) where that method is correctly defined and accessible,
My error is a syntax error on that line.
You don't want to get the method name, you want to get the actual method, eg something like:
function getMethod(string method){
return variables[method];
}
The call that, thus:
theMethod = getMethod(variableHoldingMethodName);
result = theMethod();
Unfortunately one cannot simply do this:
result = getMethod(variableFoldingMethodName)();
Or:
result = myObject[variableFoldingMethodName]();
As the CF parser doesn't like the double-up of the parentheses or brackets.
The caveat with the method I suggested is that it pulls the method out of the CFC, so it will be running in the context of the calling code, not the CFC instance. Depending on the code in the method, this might or might not matter.
Another alternative is to inject a statically-named method INTO the object, eg:
dynamicName = "foo"; // for example
myObject.staticName = myObject[dynamicName];
result = myObject.staticName(); // is actually calling foo();
Assuming the method is in your current (variables) scope, you could try:
var result = variables[reflectionMethod.getMethodName()](argumentCollection = params);