CFML is a tag based language. When tags need arguments or options passed to them, they are passed as attributes. This is an integral part of what makes CFML so easy and so productive. But sometimes, having to pass attributes can be a hindrance. Consider a simple example, a <CFQUERY> where you sometimes just need basic attributes (NAME and DATASOURCE perhaps) but other times want additional optional attributes (like USERNAME and PASSWORD). There is no way to conditionally include attributes in a tag, and so you end up having to write code like this:
<CFIF some condition>
<CFQUERY NAME="myQuery" DATASOURCE="myDSN">
...
</CFQUERY>
<CFELSE>
<CFQUERY NAME="myQuery" DATASOURCE="myDSN"
USERNAME="#SESSION.username#"
PASSWORD="#SESSION.password#">
...
</CFQUERY>
</CFIF>
It's just an example, but it's not pretty at all. And this same is true if you conditionally want to add query caching, or specify an SMTP server in <CFMAIL>, and so on.
Scorpio solves this problem in the simple and elegant fashion we've come to expect from ColdFusion. In Scorpio you can pass all tag attributes as a single structure, an ARGUMENTSCOLLECTION. Here is a simple <CFQUERY>:
<CFSET args=StructNew()>
<CFSET args.name="myQuery">
<CFSET args.datasource="myDSN">
<CFQUERY ARGUMENTSCOLLECTION="#args#">
...
</CFQUERY>
As structure members can be added conditionally, passing optional attributes becomes a simple matter of conditionally adding members, as seen here:
<CFSET args=StructNew()>
<CFSET args.name="myQuery">
<CFSET args.datasource="myDSN">
<CFIF some condition>
<CFSET args.username=SESSION.username>
<CFSET args.password=SESSION.password>
</CFIF>
<CFIF condition that determines that caching is needed>
<CFSET args.cachedwithin=CreateTimeSpan(0,0,0,1,0,0)>
</CFIF>
<CFQUERY ARGUMENTSCOLLECTION="#args#">
...
</CFQUERY>
This new syntax can be used by all sorts of tags, including <CFCONTENT>, <CFFILE>, <CFFTP>, <CFIMAGE>, <CFMAIL>, <CFXML>, and many more.
--- Ben
And amazing feature for Scorpio. Unlike CF7, CF8 will be a must-have upgrade.
Would make writing form and ORM code generators s a lot easier.
Yep, all CFFORM tags (including CFFORM, CFINPUT, CFSELECT, etc.) and CFQUERYPARAM support this syntax. In fact, it is supported by just about everything except from flow control tags (CFIF, CFCASE) and variable assignment tags (CFSET, CFPARAM) and tags like CFOUTPUT.
--- Ben
var struct = { title: 'Hello', content: 'World' };
It just gets pretty repetitive so have to do a bunch of cfsets, ArrayAppends(), etc.
I'm curious why this feature uses argumentsCollection as opposed to the existing argumentCollection shortcut that can be used to pass arguments to a cfc call?
Is there a reason they're different?
--- Ben
Maybe Jim is using FF under Linux. I have been getting set-up under Linux (PCLinuxOS). One of the things I noticed is the code blocks on my blog being quite small. If I have trouble reading anything I just use CTRL-+ and CTRL--.
Question - Shouldn't it be AttributeCollection or AttributesCollection instead of ArgumentsCollection? Or like Todd Sharp said, the existing variable we're already used to typing, ArgumentCollection. Thanks for the teaser - see you on Wednesday!
Can you mix the two types, i.e. still use the NAME parameter but pass in the others like DSN, USERNAME, PASSWORD, etc via the ARGUMENTSCOLLECTION parameter? Same question for other tags too. I would hope that an argumentscollection member would be used only if there were not a direct parameter specified.
--- Ben
I would like to stress however that this feature remain consistent with both cfmodule and cfinvoke. With both of them, you can pass attributeCollection and argumentCollection attributes respectively along with normal attributes (and cfinvokearguments as well).
Where I see this losing a lot of value is in the example you posted. I would very likely cache the username and password for cfquery calls in a structure in my application as I do now, but using this new method to pass them in. The problem comes when I want to have different names for all my query and caching when necessary. Having to modify or copy the struct in these cases completely voids the feature.
Here are the docs I found on the precedence of arguments, which make great sense to me as they are in order of how explicit the arguments are:
Using hybrid means of passing cfinvoke arguments (explicitly stated precedence)
http://livedocs.adobe.com/coldfusion/7/htmldocs/ww...
Using hybrid attribute passing with custom tags (coded in the example)
http://livedocs.adobe.com/coldfusion/7/htmldocs/ww...
Thanks for sharing and I'm excited to see your presentation tonight at the DFW CFUG!
Mike Kelp
Regarding the font size issue, any font size set below 0.8em becomes illegible in browsers such as Opera and FF on the Mac. If you change the font-size value from 0.7em to 0.9em in the .code declaration in the style.css file, the fonts will be readable cross-platform.
Thanks again for telling us about argumentscollection!
var struct = { title: 'Hello', content: 'World' };
Well, let me tell you that I saw that last night at Ben's DFW CFUG presentation. You can also do arrays that way too with:
var array = ["First", "Second", "Third"];
(I think the syntax is right there). So, yes it will be available in Scorpio if I understood it right. I remember Ben saying that you ought to be able to take existing java declarations and just copy them in - so I guess they have the syntax the same. Good idea if you ask me!
Steven
-- Ben
From a precedence standpoint, I'd grab the attributeCollection first, if it exists, then apply any additional attributes that may be present. That seems intuitive to me, and I'd rather have the possibilty of a slight bit of confusion, which can be cleared up by reading the documentation, as opposed to requiring the developer to write addtional lines of code to do structCopys and extra struct assignments just to set a query name.
Further, it's pretty well established that tags (and custom tags) have attributes, and some tags already have an attributeCollection. CFFunctions, OTOH, have arguments. Please don't confuse the issue, and use attributeCollection for the new tag feature.
With the JSON syntax for the structs/arrays/etc, is there a native tag that will go the other direction? i.e. tag a CF struct and produce json text? Maybe a cfjson tag that works just like cfwddx, but works with json syntax?
I love the simplicity of wddx, but prefer the smaller raw data size of the json syntax.
DAMN IT I'm on my 4th attempt at this captcha.
Tim and Adam took a straw poll, and we mostly voted for "override"... that way you can use an argumentcollection for "default" arguments but you can still override certain arguments if you need to or want to. Like you might have an argumentscollection stored in the application scope that you use as a default for cached queries, but maybe you want to change one attribute for a certain query, like how long a query is cached...
Damn it, I can *NEVER* get the captcha right the first time on this blog... how about a third time... nope let's go for four... five..?
--- Ben
So the cfc query code abbreviates neatly to:
<cfquery ATTRIBUTECOLLECTION = "#REQUEST.query_parameters#" name="getData">
In the Application.cfc in the onRequestStart function:
<cfscript>
REQUEST.query_parameters = StructNew();
REQUEST.query_parameters.datasource = THIS.DataSourceName;
if(THIS.database_uses_password){
REQUEST.query_parameters.username = THIS.DB_Username;
REQUEST.query_parameters.password = THIS.DB_Password;}
</cfscript>
I set all my variables on cfinclude page i.e.app_parameters.cfm using the THIS scope for users with different setups. i.e.
THIS.DataSourceName = "dsn_name";
THIS.database_uses_password = True;
THIS.DB_Username ="db_username";
THIS.DB_Password ="db_password";
Being explicit as I went off on a wild goose chase with this issue. Might save some fellow travellers some time.