0

Interesting ColdFusion error with extended CFCs and Mixins

ColdFusion

Many moons ago we experienced some behavior that we had a hard time pinpointing regarding CFCs with CFINCLUDES.  There is even a blog entry about it on here somewhere that is probably a year or two old.  In essence we were seeing some things that indicated that ColdFusion didn't really see the CFC as a whole object.  We found a quick workaround at the time, and it faded from our memory never to be pursued.  I came across it again today and combined with past experience and bit more patience I think I have nailed down the problem.

For background, we use the spectacular Illudium PU-36 by Brian Rinaldi to generate most of our objects.  Since things change during development time, it was sucking to have to re-add in any customization we made to our CFCs if we had to regenerate them.  Our solution was to modify the XSL so that in each package, there is a "_includes" folder.  We would then add a <cfinclude /> at the bottom of each CFC to include any custom methods so that we could regenerate the CFCs at well. For instance in a "Foo" package, the structure would look like this:

  • _includes
    • Foo-cfc.cfm
    • FooGateway-cfc.cfm
    • FooService-cfc.cfm
  • Foo.cfc
  • FooGateway.cfc
  • FooService.cfc

This was worked without flaw for us for some time now, and in fact the entire new architecture of the soon-to-be-release InstantSpot 2 is built in that fashion.

However, in that environment we were doing no extending of objects and no method overloading.  

While working on a client application today I was building something that looked like this:

  • _includes
    • ParentFoo-cfc.cfm
    • ParentFooService-cfc.cfm
  • ParentFoo.cfc
  • ParentFooService.cfc
  • FooChild1Package
    • _includes
      • FooChild1-cfc.cfm
      • FooChild1Service-cfc.cfm
    • FooChild1.cfc
    • FooChild1Service.cfc
  • FooChild2Package
    • _inlcudes
      • FooChild2-cfc.cfm
      • FooChild2Service-cfc.cfm
    • FooChild2.cfc
    • FooChild2Service.cfc

 What we have found is a situation like this...  

Say that the FooChild1 object has a method called doStuff() which is included by FooChild1.cfc and it is intended to overload the Foo object's parent doStuff() method.  When the objects are being instantiated, an exception is thrown saying that you cannot have two methods named the same in an object.  I can't tell you why, but somehow in the way that ColdFusion is compiling, it just doesn't know how to process that overloaded method in a cfinclude.

The workaround is plain and simple, in that all we had to do was kill the includes and move the methods inside the actual CFC.   To be specific, we could leave the include in the parent, but not the child.  Of course, now we will need to pay a bit of attention as we regenerate our objects, but due to the extensions they were pretty far from stock anyway.

For the record, we have seen this behavior in both CF7 and CF8.   I realize this is probably a fringe case to most, but I wanted to throw it out there in case anyone else stumbles across the same issue. 

tags:
ColdFusion
Cutter said:
 
You know, I've been bouncing around the best way to handle these utility functions, methods that are fairly generic across certain object types. I enjoy Brian's generator, and have made several mods to the XSLT files, mostly to autogenerate comments for functions, but I've thought about adding in code to make each object extend a base class, which would then contain these utility functions. For example, the Get and SetMemento and Dump functions of the Bean objects, or the queryRowToStruct() in the DAO, would be perfect methods to have contained within base objects that the generated objects would then extend.

Just thinking out loud. Thanks for allowing me to ramble.
 
posted 714 days ago
Add Comment Reply to: this comment OR this thread
 
 
Cutter, that is exactly one of the mods we make. All beans/DAOs/Gateways/Services all extend com.base.Base[Bean/DAO/Gateway/Service]. An example of a base bean method my be clone() that creates a replica of the original.

Another big - and by far the most used - change we made to the XSL was to put hooks in place for child relationships. These are just commented in the code as "Foo" children, but it makes a nice easy and clear way to be consistent in the way that we manage those relationships.

Each bean has a commented getFooQuery(), getFooArray() and resetFoo() which clears the caching of the Foo relationship if there are any changes (adding or removing a Foo).

We also add the supporting Foo needs in the service with instructions (albeit rough ones!) on what to do with them in the case of 1-many or many-many.

We then add a method in our BaseBean to help that stuff as well with a filterQuery() method. For instance, say that I only want all my Foos where color is red, I can say My.getFooQuery(Color="red") or My.getFooArray(Color="red"). The complete query without filtering is passed to the BaseBean which does the filtering.

That may be hard to picture without seeing it, but perhaps that might spark something useful that you may not have considered! :)
 
posted 714 days ago
Add Comment Reply to: this comment OR this thread
 
Peter Bell said:
 
Hi Dave,

You can run into problems when using either class (cfinclude) or object (runtime a.methodName() = b.methodName()) mixins. The most popular way to void those problems is just to use inheritance. Typically you'll actively (re)generate GeneratedUserService.cfc and passively generate UserService.cfc which extends GeneratedUserService. That way you can put your custom code into UserService and can re-generate GeneratedUserService at any time. Additional benefit is that with inheritance you can overload so if one of the generated methods isn't working for you, you can put the same method name into UserService and thatll be called instead. I know it is some more class files, but it is worth it.

Also worth considering putting all of the generated files into a completely separate source tree so there is never any temptation to modify by hand. You'll want a com.generated with a "here be dragons" warning so everyone knows any changes they make will be blown away on next regen.

Another good tip is to put your generator cycle into your CI build so you KNOW you can't edit generated files as they'll be blown away every time you commit.
 
posted 713 days ago
Add Comment Reply to: this comment OR this thread
 
 
Wow Peter... you may have just changed my entire approach with your first paragraph. I will have to chew on that a bit, but it sure has a clean "feel" to it. Very good additional points as well. Thanks for sharing.
 
posted 713 days ago
Add Comment Reply to: this comment OR this thread
 
Peter Bell said:
 
Glad to help! Just ping me if any questions!
 
posted 713 days ago
Add Comment Reply to: this comment OR this thread
 

Search