Exploring Mixins And ColdFusion Components
I am not sure what the exact, technical definition of a "Mixin" is; but, from what I understand, a "mixin" is generically meant to define a scenario in which an object is composed of functionality that was not originally defined within it. This can be done either at compile time through includes or at runtime through method injection or includes. Earlier today, someone asked me about some mixin behavior and I didn't have an existing blog post to point them to. As such, I figured I would take lunch to throw together a few examples.
To start off with, let's create two ColdFusion templates, each of which contains a user defined function. The first one, public_udf.cfm, will define a method with "public" access:
<cffunction
name="PublicFunction"
access="public"
returntype="string"
output="false"
hint="I am public function that was declared OUTSIDE of a component.">
<cfreturn "I am a public function." />
</cffunction>
The second one, private_udf.cfm, will define a method with "private" access:
<cffunction
name="PrivateFunction"
access="private"
returntype="string"
output="false"
hint="I am private function that was declared OUTSIDE of a component.">
<cfreturn "I am a private function." />
</cffunction>
Now that we have these methods in place, let's use them in a standard page. We're going to include them as we would any standard user defined function (UDF) and then invoke them:
<!--- Include the UDFs. --->
<cfinclude template="public_udf.cfm" />
<cfinclude template="private_udf.cfm" />
<!--- Call the methods in the page scope. --->
<cfoutput>
#VARIABLES.PublicFunction()#<br />
#VARIABLES.PrivateFunction()#<br />
</cfoutput>
When we run this, we get the expected output:
I am a public function.
I am a private function.
I am demonstrating this more to show that the concept of Public / Private methods don't really have a meaning outside of components. Technically, I believe components and templates are executed in similar methods in that they are each a page with its own context, so technically, I guess you could think of a page's VARIABLES scope as being tantamount to the private scope of a component. But, as a standard page doesn't have a public scope, the demo is rather moot.
OK, let's get onto the more exciting stuff - mixing these UDFs into a component.
The first thing I want to try is mixing them in at compile time of the component. By that, I mean we are going to execute the CFInclude in the pseudo constructor of the CFC (the area within the CFComponent tags but outside the CFFunction tags):
<cfcomponent
output="false"
hint="I am a component whose methods are created via Mixin methodology.">
<!--- Include the mixin UDFs in the pseudo-constructor. --->
<cfinclude template="public_udf.cfm" />
<cfinclude template="private_udf.cfm" />
<cffunction
name="Init"
access="public"
returntype="any"
output="false"
hint="I return an initialized object.">
<!--- Return THIS reference. --->
<cfreturn THIS />
</cffunction>
<cffunction
name="GetVariables"
access="public"
returntype="any"
output="false"
hint="I return the private scope of this component.">
<cfreturn VARIABLES />
</cffunction>
</cfcomponent>
You can see in this code (and subsequent demos) that the Mixin.cfc (our test component) has an Init() method and a GetVariables() method that can return the private scope for debugging. You can also see that I am including our two UDFs at compile time when the component is being instantiated.
Now, let's test this component to see how it behaves:
<!--- Create the Mixin component. --->
<cfset objMixin = New( "Mixin" ).Init() />
<!--- Output the public scope (THIS). --->
<cfdump
var="#objMixin#"
label="Mixin PUBLIC Scope."
/>
<br />
<!--- Output the private scope (VARIABLES). --->
<cfdump
var="#objMixin.GetVariables()#"
label="Mixin PRIVATE Scope."
/>
When we instantiate the Mixin.cfc component and output it's public and private scopes, we get the following CFDump output:
As you can see, when we include the UDF mixins at compile time, the public and private properties of the user define methods are carried through such that our public method is available in our public THIS scope and our private method is available in our private VARIABLES scope. You'll also notice that the public methods are available in the private scope as well.
Ok, now let's try to include the UDF mixins at runtime from within the component's Init() method:
<cfcomponent
output="false"
hint="I am a component whose methods are created via Mixin methodology.">
<cffunction
name="Init"
access="public"
returntype="any"
output="false"
hint="I return an initialized object.">
<!--- Include the mixin UDFs in explicit constructor. --->
<cfinclude template="public_udf.cfm" />
<cfinclude template="private_udf.cfm" />
<!--- Return THIS reference. --->
<cfreturn THIS />
</cffunction>
<cffunction
name="GetVariables"
access="public"
returntype="any"
output="false"
hint="I return the private scope of this component.">
<cfreturn VARIABLES />
</cffunction>
</cfcomponent>
As you can see, all we've done is moved the CFInclude tags from the pseudo constructor to the explicit constructor (Init()). Now, when we run the same test as above, we get the following CFDump output:
As you can see, when we include the UDF mixins at runtime from within a CFC method, the methods end up in the CFC's private scope regardless of what their compile time access properties are.
Now, if we want to use this methodology but we want proper scoping, we can manually copy the public method to the public scope. To do this, after the UDFs are included, we need to explicitly copy the public one from the private scope to the public scope:
<cfcomponent
output="false"
hint="I am a component whose methods are created via Mixin methodology.">
<cffunction
name="Init"
access="public"
returntype="any"
output="false"
hint="I return an initialized object.">
<!--- Include the mixin UDFs in explicit constructor. --->
<cfinclude template="public_udf.cfm" />
<cfinclude template="private_udf.cfm" />
<!---
The UDFs we just included are in the PRIVATE scope.
If we want any of them to be public, we need to copy
them to THIS explicitly.
--->
<cfset THIS.PublicFunction = VARIABLES.PublicFunction />
<!--- Return THIS reference. --->
<cfreturn THIS />
</cffunction>
<cffunction
name="GetVariables"
access="public"
returntype="any"
output="false"
hint="I return the private scope of this component.">
<cfreturn VARIABLES />
</cffunction>
</cfcomponent>
Now, with our copy line in place:
<cfset THIS.PublicFunction = VARIABLES.PublicFunction />
... when we run the above test, we get the following CFDump output:
As you can see, both methods ended up in the private scope as before, but now, our PublicFunction() has been copied manually to the THIS scope and is publicly accessible.
After includes, there is really only one other kind of mixin that I can think of and that is to inject a method into a component at run time. In the following demo, the Mixin.cfc is not going to do any of the including; rather, the instantiating pages is going to manually copy our functions references into the existing CFC instance. Here is our updated CFC:
<cfcomponent
output="false"
hint="I am a component whose methods are created via Mixin methodology.">
<cffunction
name="Init"
access="public"
returntype="any"
output="false"
hint="I return an initialized object.">
<!--- Return THIS reference. --->
<cfreturn THIS />
</cffunction>
<cffunction
name="GetVariables"
access="public"
returntype="any"
output="false"
hint="I return the private scope of this component.">
<cfreturn VARIABLES />
</cffunction>
</cfcomponent>
And, in this demo, we are going to copy the UDFs from the page scope to the public scope of the instantiated CFC:
<!--- Include the UDFs into the page scope. --->
<cfinclude template="public_udf.cfm" />
<cfinclude template="private_udf.cfm" />
<!--- Create the Mixin component. --->
<cfset objMixin = New( "Mixin" ).Init() />
<!--- Store our page-scope UDFs into the component. --->
<cfset objMixin.PublicFunction = VARIABLES.PublicFunction />
<cfset objMixin.PrivateFunction = VARIABLES.PrivateFunction />
<!--- Output the public scope (THIS). --->
<cfdump
var="#objMixin#"
label="Mixin PUBLIC Scope."
/>
<br />
<!--- Output the private scope (VARIABLES). --->
<cfdump
var="#objMixin.GetVariables()#"
label="Mixin PRIVATE Scope."
/>
With this technique, when we run the above code, we get the following CFDump output:
As you can see, this time, both our UDFs are available in the public scope; but, unlike the CFInclude-based mixins (or actual pre-defined CFC methods), the private scope has no reference to these methods at all.
You can also inject methods into the private scope using a method injection proxy. In that kind of scenario, you would pass the method reference to a public method that would turn around and inject the given method into the VARIABLES scope. It achieves the same thing, only with a different target scope. Heck, you can even inject the method proxy that takes care of the variables injection.
Once a method is mixed into a CFC, it has access to all the same methods and properties that any pre-defined method would have. As far as ColdFusion is concerned, a mixin is exactly the same as a standard component method. This brings us to one final topic of discussion and that is method binding. When a UDF is used outside of a component and then called, the method is bound to the context of the calling page. By that, I mean that if you define a UDF in template A and then invoke it in template B, the UDF executes as if it were defined in template B (its calling context).
Once you copy a method into a CFC, however, the rules change. A method reference copied to a CFC is bound to the that CFC. And, when that method is invoked on that component, it uses the CFC as the context rather than that of the calling page. Of course, this only works if you call the method ON the component. If you were to copy the method reference out of the CFC and back into the page, the method take on the pervious behavior (bound to calling page context).
So there you go. I hope this sheds some light on mixin behaviors for anyone who was unclear as to what they were or how they worked.
Want to use code from this post? Check out the license.
Reader Comments
Well summarized, sir.
(Now compare and contrast with prototypal inheritance in JS. ;-)
@Rick,
Oh man. To be honest, I am not even sure HOW Javascript methods work when injected into object instances. I don't think I've ever tried that before.
I guess it's the same as passing a fn pointer as a callback. Javascript methods are lexically (spelling?) bound, so I assume injecting them won't make a difference. Of course the "this" keywords is dynamically bound....
Oh man, my head hurts. Damn you Rick!
Thank you for the post, very well written and explained!
I'm shocked that you can cfinclude a page that's just a function. I thought it would throw some sort of nesting error for having a function in a function.
@Anthony,
Yeah, it's a bit odd, right? Including a function inside a function.
Great examination! Very informative. The biggest surprises to me were that you can even use a cfinclude in the pseudo constructor of a cfc (!!?!?) That seems nutty.
Now what I'd like to see are reasons you'd use any of the mixin techniques described here with ColdFusion.
Are there valid reasons for ever authoring standalone UDFs and then including them on a page?
I've always aggregated all of the utility UDFs I need into a single CFC that injects them into the application scope onApplicationStart.
Then I can call them ( or inspect them ) anywhere as application.util.function() and never have to worry about managing them.
Great post.
Good post. Sean Corfield posted about mixins a while ago and there's some interesting banter in the comments here: http://corfield.org/blog/index.cfm/do/blog.entry/entry/Mixins
I have to say I think mixins are a horrible 'pattern'. If you use them in even a medium scale project you can run into debugging and maintenance nightmares as you can get errors thrown from code which doesn't exist except at runtime. Debugging hell!
Between dependency injection via Coldspring and the cfinterface tag I really can't think of a reason to use mixins these days.
there is so much information in this article it really helped me during my study.
@David, @George,
I agree, I don't really use this technique very much. I used it a few times with dependency injection, but I have since gone other routes for such things.
I think Isaac uses something like this in the OnTap framework to lazy load his actual component methods (using OnMissingMethod() to install them only when necessary)... but not sure on that, so I don't want to speak for him.
@Rick,
Cool behavior in Javascript:
www.bennadel.com/index.cfm?dax=blog:1552.view
@ben
Seems like there should be a better way to make modules (OOP, not cfmodule) and mixins.
I mean, in a rubyesque way.
Psuedocode is easier.
comp Math
...func pi
...func sin
...func cos
comp Debug
...func print
...func dump
...func inspect
Use Math as a ruby module. Similar to ruby's require, you have to create a reference to the class. Call methods from the class (class methods). No object is instantiated.
Use Debug as a ruby mixin. Again, you have to create a class reference like ruby's require. Then, you have to copy all the methods to the local scope, similar to ruby include.
cfm App
...Math=createObject(comp, Math) # ruby require
...x=Math.pi()
...x=Math.sin(y)
...x=Math.cos(y)
...Debug=createObject(comp, Debug) # ruby require
...for (method in Debug.methods) { structInsert(variables, method) } # ruby include
...print(x)
...dump(x)
...inspect(x)
This App needn't be a cfm. Could be a cfc or other. You'll have to be careful with a cfc, though, where you do the class reference and method copy. You'll usually be calling the rubyesque module or mixin from runtime code.
@ben
Dango!
Just now saw your post about swapping CFC methods at runtime.
I have to keep up with your blog more.
Have you tried using mixins in CF9 yet? I'll give you a hint: not what you'd expect...
@Tony,
I haven't had a chance to try this yet; let me see what happens.
@Tony,
I just tried a CFInclude-based mixin and it worked fine. What should I be looking for?
@Tony,
An injected method also seems to work fine.
@Ben,
Sorry I should've followed up to my previous comment. When I first tried using mixins in CF9, it didn't appear as if they would work because none of the functions in the mixin appear if you dump the object. However, the functions work as expected.
@Tony,
No worries my man.
Late entry here ... I'm not very confident in my understanding here so FWIW ....
Is the implementation here similar to the functionality provided by creating JavaScript closures? Where declaring a function in the body of another function makes the variables of the outer function publicly available to the inner?
If this makes sense :)
@Edward,
ColdFusion has announced Closure support in future versions of ColdFusion; but, unfortunately, that is not something we have just yet. Right now, when you invoke a function that is NOT a method on an object, the function executes in the context of the CALLING code, not the defining code. The function's lexical binding is meaningless... in the future, we'll have our closure's though!!! :D