I am not going to go on a rant here about how ColdFusion absolutely needs static variables. Instead I would like to present a use case from a recent experience and I would like to get your thoughts on it. I was working on a personal open source project called Hyrule. This project allows you to do validation on business objects (beans/ORM Entities/etc) by describing constraints in your objects.
Currently you can describe these constraints using annotations. I found out in a recent project though that this can get very messy. Having played around with Grails before I really enjoy being able to define my constraints like this. I thought why not create a similar way to describe constraints for Hyrule, so I set out on my mission
My first attempt went pretty well. I created a new Validator to handle constraints based settings and ended up with a model that looked like this. I was really happy with this and it seems like a much cleaner implementation than having a million annotations running around. Then a friend pointed out something that I totally overlooked. This method would not support inheritance. You can not access the this scope from a parent object. So if we had an employee object that defined its own constraints and extended User we would never be able to get the user constraints. This obviously brings up a huge issue and killed any dreams I had of implementing this.
So here is my use case for static variables in ColdFusion. In case your not quite sure what static variables lets review. A static variable can be accessed from a class without instantiating that class. So going back to our example say we have 2 components here, person and employee. Employee Person
Now when if create an instance of the employee object we can use meta data to find out if the component extends any other components (inheritance) and if it does we would have the full name of the component. From there it would simply be a matter of accessing the variable constraints. I am not sure how the implementation would be that would be the basic idea behind it. So that is my case let know your thoughts on static variables/methods in general. I would also be all ears in figuring out a different solution to my little problem here.

#1 by Dan Vega on 3/18/11 - 8:43 AM
#2 by Brian Swartzfager on 3/18/11 - 9:12 AM
Sorry if I'm way off-base with that idea; I'm not up-to-speed on the CF9/ORM stuff yet.
#3 by Sam Farmer on 3/18/11 - 9:27 AM
#4 by Dan Vega on 3/18/11 - 9:29 AM
Just while I was typing out this comment I thought about something. You could perhaps override the getConstraints() method to call the super. It just seems like a lot of work for the end user. In the end I am framework developer trying to make this as easy as possible for the developer to work with. If I went that way I have to tell a developer to take these steps.
1.) define a property called constraints that is not persistent
2.) accessors = true
3.) in the init function initialize constraints = []
4.) override getConstraints() to call super.getConstraints() and merge the 2 arrays of constraints and return the new array.
#5 by Tony Garcia on 3/18/11 - 9:29 AM
property name="firstName" constraints="{blank: false, maxLenth: 100}"
(Or use the javaDoc style notation for constraints to keep your custom annotations separate from property attributes.)
#6 by Dan Vega on 3/18/11 - 9:32 AM
#7 by Tony Garcia on 3/18/11 - 9:46 AM
How about making it so that getConstraints() automatically looks to see if the component inherits another one and recursively collects all the constraints up the inheritance tree?
I've written a method before to recursively collect all properties from inherited components (similar to the one Bob Silverberg blogged here:
http://www.silverwareconsulting.com/index.cfm/2009...
Looks like this strategy might be able to be modified to collect all your constraints. That way at least users don't have to worry about whether the component inherits another and have to override any methods or explicitly call Super.getConstraints().
#8 by Dan Vega on 3/18/11 - 9:56 AM
#9 by Tony Garcia on 3/18/11 - 11:21 AM
private array function getConstraints(required struct md=getMetaData(this),array constraints=ArrayNew(1)) {
local.prop = 1;
if (structKeyExists(arguments.md,"properties")) {
for (local.prop=1; local.prop <= ArrayLen(arguments.md.properties); local.prop++) {
if( arguments.md.properties[local.prop].name == "constraints" ) {
arrayAppend(arguments.constraints,arguments.md.properties[local.prop]);
break;
}
}
}
if (arguments.md.extends.fullname neq "WEB-INF.cftags.component") {
arguments.constraints= getConstraints(arguments.md.extends,arguments.constraints);
}
return arguments.constraints;
}
#10 by Dan Vega on 3/18/11 - 11:48 AM
#11 by Henry Ho on 3/18/11 - 12:08 PM
See: CFMeetup: I bet you didn't know you could do that with ColdFusion @ http://experts.na3.acrobat.com/p37469767/
The technique makes use of GetMetadata() as a hack. I tried it before, it works!
#12 by Dan Vega on 3/18/11 - 12:39 PM
// STATIC VARIABLES HACK
metadata = getMetaData(this);
if( !structKeyExists(metadata,"constraints") ){
lock name="StaticVariables(#metadata.name#)" timeout="10"{
if( !structKeyExists(metadata,"constraints") ){
metadata.constraints = [
{property="firstName",blank=false},
{property="lastName",blank=false}
];
}
}
}
Back to my problem though, I don't think I want the developer to have to do this but glad to know there is a way to do it!
#13 by Dan Vega on 3/18/11 - 12:42 PM
static.constraints = ? would do the same thing as above.
Thoughts?
#14 by Tony Garcia on 3/18/11 - 12:53 PM
Sure -- I'd probably put it in a base entity. I see where you're coming from, though. You want your model to be independet of Hyrule. So if you instead want to pass your entity to Hyrule and say Hyrule.getConstraints( myEntity ), you could just change the method to
public array function getConstraints(required any entity,array constraints=ArrayNew(1)) {
local.prop = 1;
local.md = isObject(arguments.entity) ? getMetadata( arguments.entity ) : getComponentMetaData( arguments.entity) ;
if (structKeyExists(local.md,"properties")) {
for (local.prop=1; local.prop <= ArrayLen(local.md.properties); local.prop++) {
if( local.md.properties[local.prop].name == "constraints" ) {
arrayAppend(arguments.constraints,local.md.properties[local.prop]);
break;
}
}
}
if (local.md.extends.fullname neq "WEB-INF.cftags.component") {
arguments.constraints= getConstraints(local.md.extends.fullname,arguments.constraints);
}
return arguments.constraints;
}
So the first time through, the function is reading from the metadata of the object itself and then in every subsequent recursive call, it's getting the properties from getComponentMetadata() on the path of the inherited class. Won't that work?
Just a thought, I guess.
Elliott's static var hack would probably work.
#15 by Dan Vega on 3/20/11 - 12:29 PM
getTarget().getParent().getConstraints()
without though somewhere down the line you have to instantiate person unless you are specifically calling super in your getConstraints method and to do that you need to define it in the model itself.