Welcome to the first ColdFusion Zeus POTW (Preview of the Week). This is a new blog series where I'll be highlighting one feature a week from the upcoming new version of ColdFusion code-named Zeus. As with any preview, you should remember that things may, and probably will, change a bit before the final product is released. That being said, this is your early opportunity to voice your opinion. Some POTWs will be deep dives into large topics. Some, like today, will be simple, small changes that are meant to improve your day to day coding life. What I'm about to show is really small. But it's one of those nice things that I think you will really appreciate.
Any one who has made use of cfinclude in the past has probably run into situations where they've accidentally included the same template twice. Some times this isn't a big deal. Other times it can lead to bugs or just having your site run a bit slower than expected.
ColdFusion Zeus adds a simple way to ensure your includes are never run more than once. Consider the template below:
<cfinclude template="include_target.cfm" runonce="true">
<cfinclude template="include_target.cfm" runonce="true">
We've added a new attribute, runonce, that when set to true, will ensure the target of the include is only executed once. This is request-level intelligent as well. So given this CFM:
<cfinclude template="include_target.cfm" runonce="true">
<cfinclude template="include_test.cfm">
If include_test.cfm contains the include statements you saw above, then they recognize that they were already run before in the parent. Hopefully that didn't come out as confusing as it sounds. ;)
Archived Comments
Is there an option yet to cfinclude a file without parsing it?
Interesting. Why? If you include something that has no CFML in it, it will still be _checked_, but obviously will execute instantly. I don't know about the Java byte code generated, but I'd have to assume it would be generated as a simple string to be included or somesuch.
I am going to assume this new feature is going to be similar to PHP's include once?
As I don't use PHP, I can't comment.
I'll be honest I really can't see a use case for this.
Runonce added only on for cfinclude tag or will be and on cfscript version?
@inj - it should be in both. I'll confirm.
It breaks - I'll log it internally and make sure it works.
How about we deprecate cfinclude and teach people to use custom tags instead?
Because that doesn't make sense Nic. An include is different than a custom tag. And while I use includes much less now that I've got CFCs, I certainly still use includes from time to time. There is no reason one should deprecate the other.
I'll betcha I haven't used a cfinclude since 2005. Between CFCs and custom tags, the architecture makes so much more sense than including snippets via includes. Now this is just my opinion of course, but I can't think of a single reason why I would opt for an include.
Custom tags DO NOT work in CFSCRIPT, currently in some of my applications I use include to include templates for my emails, a custom tag is way over kill for this situation and an include is just the ticket.
Personally, I only use custom tags for layout now. <cf_layout>foo</cf_layout> Their ability to be 'wrapped' like that is nice.
I think Andrew's point about 'templates' is a great one. While I try to avoid HTML/layout/etc in CFCs, email templates are a good example of when I break that rule. In my script based CFC I'll include in a template that makes use of tags.
@Ray and @Nic I use includes all the time with styling emails in all my cfc's, within a CMS that allows me to create a template for different emails. I can just include this template into my MVC framework, and if the client so desires they can change the look and feel of that template at any time.
All I do is educate them that there is a set of tokens they can use, to customize the email template.
I should also point out that some people also use include to include extra methods into Components as well. I don't like that idea, but that doesn't mean others can't do it.
Btw - I'm doing a presentation in 2 weeks on 'extending CF', and I talk about all of this. Thanks Andrew for good examples of cfinclude.
Is it just me or does this seem like it would only affect people writing poor code?
I think that's a bit much Matt. In a large system with lots of developers, something like this could come about by accident.
Let me rephrase - it sounds like this is built to prevent something from happening that should be designed out, rather than worked around. If it's a large system and it's already happening by accident shouldn't the answer be to, you know, actually fix the logic that causes the unwanted repetition?
@ Andrew .. "Custom tags DO NOT work in CFSCRIPT" .. actually you can script custom tags, in fact, that is the only way I write custom tags. I can present example if you like.
@Matt, I have to agree I don't see a use case for this either. And the only use case would be if you chose to use Application.cfm over Application.cfc, unless someone can show me a really good use case, I am not convinced this is something CF needs either.
@ Matt .. on the surface, I am in agreement. I am still trying to think of a use for this, and about all I can come up with is that it would prevent and infinite loop (ie: including the parent in a child), otherwise, I am at a loss as to the usefulness, but perhaps something will come to me.
Thanks Ray, I'm excited for this new series!
@Jeff, I think you are confusing writing custom tags in cfscript with writing custom and using them in cfscript.
@Andrew, ah, my misunderstanding, thanks!
Others have eluded to it, but I wanted to ask directly?
@Ray - from you, or others at Adobe, what is the use case(s) for this feature, other than "situations where they've accidentally included the same template twice."?
(sounds like a mistake/bug/design problem, not a real use case)
I love how people feel something is useless just because they don't use it or can't find a use for it (while at the same time pointing out that it's already in PHP). You may be too good for this minor feature enhancement, but let's not be so arrogant as to think no one else will benefit from it (even if it is just a nice way of helping people work around their own sloppy code).
"I love how people feel something is useless just because they...can't find a use for it" - wouldn't that loosely be the definition of useless?
I think there are two things here: First, if you really look back most of are saying we don't see the use case, but we're asking what it is. If a worthwhile one exists, awesome! I'd love to hear about it.
Secondly, yes, I think there may actually be some weird suspicion on this one and my guess is that it goes back a long time to the deep-seated hurt we CF developers have endured as second-class citizens of the "real" programming world. I think many of us would consider it, well, questionable, to add in features that could be considered to promote lazy programming.
I'm probably over-thinking it though. New features yay!
Nice to know my first entry on Zeus is causing such fun discussion. ;)
I agree - in general - that the 'problem' here is something that could be solved other ways. However, you can say that about almost anything. Shoot, even cfquery solves a problem that could be solved in Java. ;)
I fully expect that not everything I cover in my blog series will be useful to everyone. I think that's ok.
Dang it Ray! If at least 60% or your readers don't want the feature, please don't blog about it!
And please include a poll so we can vote for the features you are presenting, regardless of the fact that they are *already* in there.
Thank you.
As is usually the case, people don't understand a use for something until they need it. :)
Let's say you had a template that could be executed in a "standalone" mode or in the context of a larger application. In either case it needs access to some external logic (be it UDF's, email templates, whatever... doesn't matter).
This external code is already "included" in the larger application (because other parts of the app need it as well), but the template still needs to "include" it for when it's run in standalone mode. So, I'm sure you can see how the external code would end up included twice when the template is executed as part of the application.
You could, of course, setup some flags to determine wether it's already been included, but now you don't have to.
You have an application in which your framework is includeding external code into the request most of the time, but not all the time (maybe it's admin-level code based login permissions). A particular template needs that external code included no matter what. It can do it's own include to guarantee it's available using runonce to make sure it wasn't included twice.
I can think of many more uses off the top of my head. Should I add them?
Wow nice disscussion. I think many of you are professionals, but there are still beginners or other user who are not familiar in deep OO programming or Java and so on. So i like to see such features! And i think it makes really no sence to integrate a poll here. Sorry guys you are not reflecting the whole CF community. I'm sure there are many CF developers who are not blogging or commenting threats :-).
Possible use case: you have a CMS that assembles pages from content blocks. Each block may or may not need specific pieces of JavaScript code (e.g., jQuery plugins, etc.), but you'd like to not only ensure you include each only once, but you'd like to concatenate them into a single .js file to make your site blazingly fast. You could set up dozens of flags for every possible plugin, but now you don't have to - you can just let CF remember for you, and let each content block contribute the code it needs via a cfinclude with runonce=true. Contrived? Mebbe.
/ejt
Now that is a very good use case.
Thanks for sharing this enhancements.
While runonce is definitly a good idea I think it should go a bit farther:
RUNONCE="application|request|function|loop ..."
So the include is only made once per application (until onApplicationEnd) or once per request or once per function call (if inside a CFFUNCTION tag it'll be included again next time the function is called), or only once in a CFLOOP so you don't have to CFIF around
There are other arguments/attributes I'd like to see for CFINCLUDE:
- ARGUMENTCOLLECTION (or similar): the included file gets the arguments in attributes or caller scope so you don't have to use variables from the caller (same principles as for custom tags)
- timeout (ms): When including a custom template in my software I don't want my software to be bogged down by messy code in an include
- savecontent="var": to save the output in a variable which is a shorthand of a SAVECONTENT tag around the Include
- silent="yes|no": Any output made by the include is suppressed
M.
Hmm, well, I'm not sure some of your ideas here make sense.
runonce=application: If you do the include in onApplicationStart, then that works already. Ditto for onRequestStart.
argumentcollection: cfinclude does not do any black boxing. There are no attributes or caller scope.
I could see savecontent/silent being used though.
Interesting comments! Here's a use:
I've got about 5 .cfm files (written in cfscript) that contain a bunch of utility functions for my application framework. I call cfinclude when I need to include these functions in my code. For example... appUtils.cfm... contains a bunch of stuff that almost all my code needs (like getAppFSRoot ()... which I need when I'm writing files to the server)
I'm currently very careful where I include these files. But there have been times when one of those files is already included, but another piece of code tries to include it again. CF throws an error at this point, and then I have to go in and remove that cfinclude call. Pain in the ass, and I wish I didn't have to worry about it. Now I can use this attribute and not worry about it.
I see this feature very much like using conditional includes when writing in code in C so that when you include a library it won't get included again in another part of your code.
@Edward ... hey, that could be a good use. I already do that sort of thing, but I manage it will a simple stack attached to the request scope that manages all JS and CSS that is automatically combined into single compressed format blocks. At the end of each request, one of the final steps is to gather up all JS and CSS in each stack, combine them into a single JS or CSS block, and minify before sending to the browser. The application framework handles this all automatically.
I suppose this new CF feature could be used as you suggest however, achieving similar results perhaps.
@Ray
Its not that the include is in application.cfc but in some component. The first time this component or custom tag is called the include works, that could be hours or days after the application started. But, okay, request or function is the main usage.
argumentcollection is not meant for black boxing. Its just to avoid the usage of variables from the caller (if the name or value changes and all that) just provides a sort of interface that won't change. It would be incredible easier to find attributes.lang in cfm files than simply "lang" or even "variables.lang".
I think the attributes scope would do best, the mention of caller scope was completely wrong - sorry I need a sleep ;-)
You are wrong about argumentCollection. The only thing it is for is to provide another way to pass arguments to a tag or udf. Since CF does not allow for
<cf_foo x=1 <cfif something is true>y=2>>
We allow you to create a structure instead:
<cfset s = {}>
<cfset s.x = 1>
<cfif something>
<cfset s.y = 2>
</cfif>
<cf_something attributeCollection=s>
UDFs/CFC methods support the same (although it's argument collection).
You can use this right now with the cfinclude tag.
Sorry, my explanation was very unclear.
Here's an example:
<cfinclude template="test.cfm" myvar="111">
At the moment you get "Attribute validation error for tag CFINCLUDE. It does not allow the attribute(s) MYVAR. The valid attribute(s) are TEMPLATE. "
I don't want to "expose" my variables (names) to an included (custom) template (I know, the template has access to it and can dump it).
All I wanted is "myvar" in the attributes scope in test.cfm - so next time my variable name changed, the included template mustn't be changed also if it uses the attribute instead.
(Sorry if this is a bit off topic to "runonce")
Michael, you are _not_ talking about an include. You are talking about a custom tag. An include is not meant to be black boxed. An include runs in context of the file that includes it. If you want black boxing, just use a custom tag.
@Michael, you need to be thinking about Custom Tags there and not cfinclude.
bit offtopic
A function / tag to run CFML from a query would be nice.
<cfquery name="storedCode">
SELECT cfml FROM table LIMIT 1
</cfquery>
<cfset runCFML(storedCode.cfml[1])>
*hint* You can do something similar in PHP by the way ;)
You can do that a bit slowly using the VFS. Take the code - save it - cfinclude it.
This is slightly off-topic, so I apologize, but Michael's idea about savecontent or silent added to the cfinclude (combined with the discussion of runonce) made me wonder.
I know we have the bug reporting/tracking site for ColdFusion, but is there/could there be an enhancement request/discussion site?
Something where someone requests something like the savecontent and silent added to cfinclude with whatever possible use cases they think it would have. The rest of the community could comment/discuss/expand that request. If you allowed some kind of voting structure, it could give Adobe an idea of what enhancements to ColdFusion would be popular among the community. That way, when Adobe was planning what changes to make for a future release (11,12,etc), they could see "hey, everybody would really like to see X".
If there is already a good way to do that, what is it? I'm admittedly not as involved in the CF community as I would like to be, so I may just not know of it. It also may not be a big deal, but I'm curious none the less.
The public bug tracker _does_ let you post ideas. Just mark your item as an Enhancement Request.