Custom Scopes for CFGroovy2

The middle of last week I committed an update to CFGroovy2 to allow an arbitrary number of scopes to be passed in as attributes, just like you have always been able to do with the 'variables' attribute.  If you update, the change is the addition of lines 47-51:

<cfloop list="#lCase(structKeyList(attributes))#" index="scope">
  <cfif listFind("language,lang,script,variables", scope) EQ 0>
    <cfset binding.put(scope, attributes[scope]) />
  </cfif>
</cfloop>

Basically it just loops over all the attributes, lowercases them (because Groovy is case sensitive, but CFML's structs aren't – this is required for consistent behaviour across CFML runtimes), ensures they're not one of the four well-known attributes to the <g:script> tag, and adds them to to global bindings.

This is especially useful for scriptlets within UDFs and CFC methods where.   Without this change, the typical use case was like this:

<cffunction name="sayHello">
  <cfargument name="name" />
  <cfset var local = {
    greeting = variables.greeting,
    name = arguments.name
 } />
  <g:script variables="#local#">
    variables.result = variables.greeting + ", " + variables.name
  </g:script>
  <cfreturn local.result />
</cffunction>

There are a couple issues with this:

  1. The 'local' psuedo-scope is referenced as 'variables' inside the <g:script>, which compromises readability
  2. Since 'variables' is populated with the 'local' psuedo-scope, you can't access the CFC's 'variables' scope from with <g:script> (hence copying variables.greeting into local.greeting)
  3. Because there is only a single 'variables' attribute, you can't pass in multiple psuedo-scopes

With the new addition, all three problems are addressed, and the same example will look like this (changes in blue):

<cffunction name="sayHello">
  <cfargument name="name" />
  <cfset var local = {
    greeting = variables.greeting,
    name = arguments.name
  } />
  <g:script local="#local#">
    local.result = variables.greeting + ", " + local.name
  </g:script>
  <cfreturn local.result />
</cffunction>

Notice that I no longer have to copy 'greeting' from the 'variables' scope into the 'local' psuedo-scope, and than within the <g:script> I still have the variables/local delineation, exactly as it exists in the CFML code.  I haven't shown passing multiple extra psuedo-scopes, but just tack on additional attributes.

I haven't tried this with CF9, but now that it provides direct access to the function-local scope (named 'local'), you should be able to pass that into <g:script> using this mechanism and have it behave natively.

2 responses to “Custom Scopes for CFGroovy2”

  1. Gareth Arch

    Would you also be able to reference arguments.name from within the "script" tag if needed or would you need to set it to the "local" variable first?