Execute coldfusion code stored in a string dynamically?

Tag: dynamic , coldfusion , evaluate Author: jianjian945 Date: 2011-10-24

I have an email body stored as a string in a database, something like this:

This is an email body containing lots of different variables. Dear #name#, <br/> Please contact #representativeName# for further details.

I pull this field from the database using a stored proc, and then I want to evaluate it on the coldfusion side, so that instead of "#name#", it will insert the value of the name variable.

I've tried using evaluate, but that only seems to work if there's just a variable name. It throws an error because of the other text.

(I can't just use placeholders and a find/replace like this - Resolving variables inside a Coldfusion string, because the whole point of storing this in a database is that the variables used to build the string are dynamic. For example, in one case the name field can be called "name" and in another it could be "firstName", etc.)

sorry I still don't understand why you can't use placeholder.

Best Answer

I would loop over each #variableName# reference and replace it with the evaluated version.

A regex will be able to find them all and then a loop to go over them all and just evaluate them one by one.

Other Answer1

You need to write it to a file and CFINCLUDE it. This will incur a compilation overhead, but that's unavoidable.

Can you not save the code to the file system and just store a reference to where it is in the DB? That way it'll only get recompiled when it changes, rather than every time you come to use it?

<!--- pseudo code --->
<cfquery name="q">
    SELECT fileContent // [etc]
</cfquery>
<cfset fileWrite(expandPath("/path/to/file/to/write/code.cfm"), q.fileContent)>
<cfinclude template="/path/to/file/to/write/code.cfm">
<cfset fileDelete(expandPath("/path/to/file/to/write/code.cfm"))>

That's the basic idea: get the code, write the code, include the code, delete the code. Although you'll want to make sure the file that gets created doesn't collide with any other file (use a UUID as the file name, or something, as per someone else's suggestion).

You're also gonna want to load test this. I doubt it'll perform very well. Others have suggested using the virtual file system, but I am not so sure there'll be much of a gain there: it's the compilation process that takes the time, not the actual file ops. But it's worth investigating.

comments:

There are several different templates which would require several different files... Maybe I'll just make a separate field for each variable and evaluate them individually?
I may consider the cfinclude method though... can you provide some basic example code for that?
I've updated my response to include basic pseudocode.

Other Answer2

Are you using ColdFusion 9 or Railo? If yes, writing to and including in-memory files may be quick and simple solution. Just generate file name with something like CreateUUID() to avoid collisions.

Other Answer3

So basically, after researching and reading the answers, it seems that these are my options:

  1. Have separate fields in the table for each variable and evaluate them individually. e.g. nameVariable, reprNameVariable, and then I can build the body with code like this:

    This is an email body containing lots of different variables. Dear #evaluate(nameVariable)#, <br/> Please contact #evaluate(reprNameVariable)# for further details.

  2. Have an "emailBody" field with all the text and applicable field names, and write it to a temporary file and cfinclude it. (As suggested by Adam Cameron, and I think that's what Sergii's getting at)

  3. Have an "emailBody" field and write code to loop over it, find all coldfusion variables, and replace them with their "evaluate"d version. (As suggested by Dale Fraser)

  4. Have small template files, one for each report type, with the email body, and have a field "emailBodyTemplate" that indicates which template to include. (As suggested by Adam Cameron)

Now I just have to decide which to use :) When I do, I'll accept the answer of the person who suggested that method (unless it's one that wasn't suggested, in which case I'll probably accept this, or if someone comes up with another method that makes more sense)

Other Answer4

It's been a while since you posted this - but it's EXACTLY what I do. I found your question while looking for something else.

I've simply created my own simple syntax for variables when I write my emails into the database:

Hello ~FirstName~ ~LastName~,

Then, in my sending cfm file, I pull the email text from the database, and save it to a variable:

<cfset EmailBody = mydatabasequery.HTMLBody>

Then I quickly strip my own syntax with my variables (from another query called RecipientList):

<cfset EmailBody = ReplaceNoCase(EmailBody, "~FirstName~", "#RecipientList.First#", "ALL")>
<cfset EmailBody = ReplaceNoCase(EmailBody, "~LastName~", "#RecipientList.Last#", "ALL")>

Then I simply send my email:

<cfmail ....>#EmailBody#</cfmail>

I hope you manage to see this. If you control the authoring of the email messages, which I suspect you do, this should work well.

Russell