11

I've been exploring different methods of structuring my ColdFusion applications and I'm looking for some opinions on the best way to provide application wide UDFs.

For each of my apps, I generally use a bunch of extra functions that don't really belong in any particular object. Data manipulation stuff mostly. I want these functions to be available throughout my application, both for use in CFM templates and in Application instantiated CFCs.

The way I see it there are various methods of achieving this, but they all have their own limitations:

  1. Instantiate a base Utils CFC in the Application scope. This is the method I've used most often. All the functions are available app wide, but if I instantiate the same CFC from multiple applications then they'll each have their own application scope - meaning that each has to instantiate their own base Utils CFC. There's nothing wrong with this but it feels like I'm not encapsulating the CFC well enough. I'm not keen on referencing the Application scope from within the CFC.

  2. Create an base Utils CFC and make every other CFC extend this. This works fine, and it means the CFC can reference the Utils functions directly from the CFC's THIS scope - However it means the Utils functions are kept in memory for every CFC. It also doesn't sit right conceptually as my other CFCs have no relationship with the Utils CFC.

  3. Inject my base Utils CFC into my other CFCs. Another method I've been playing with is instantiating my base Utils CFC in the Application scope, but then passing that as an object to an argument in my other CFCs. This works for me conceptually, and for encapsulation. In the same way that I'll set up my datasources in my init method, I can do the same with my UDFs. This has the same issue that the UDFs are included in each CFC. When I dump all my CFCs I get each UDF multiple times - however as I'm passing an instantiated object, I'm assuming that it's not taking any extra memory space. If anyone could confirm that, it'd be helpful - I'm just assuming! The only real problem I have with this method is that it seems a bit convoluted.

  4. Have my Application CFC extend my Utils CFC. This is what a lot of frameworks seem to do. I've not used this method, but I'm sure there are pros and cons.

  5. CFInclude my UDFs from separate templates, directly in Application.cfc This is functionally similar to instantiating in the Application scope.

  6. Add my UDFs to the server's Components.cfc It's a great idea in theory - I can maintain one copy of the base Utils and be sure that everything on the server can access them - However, if I want to run apps across multiple servers then they'll all need those functions. Plus any update to the server may overwrite the components. It just feels like hacking the core - which I'm sure we can all atest from bitter experience, is bad.

So - my question is this: What is the best practice for extending CF with UDFs in an elegant and reusable way? Any of the above options or something I've not thought of?

Jeromy French
  • 11,372
  • 13
  • 67
  • 119
Gary Stanton
  • 1,414
  • 12
  • 27
  • I suggest none of the above. Instead, write them in .cfm files and cfinclude them in either the Application.cfc or the pages that need them. You might want to have more than one file, depending on whether or not they are on different topics. – Dan Bracuk Mar 16 '13 at 17:25
  • Isn't that option 5? The problem is that the other CFCs aren't encapsulated as well - they'd have to refer to the Application scope when calling the functions. – Gary Stanton Mar 16 '13 at 17:28
  • It's not option 5. Option 5 as you wrote it would either make the udf's available to only one application, or subject you to code repetition. – Dan Bracuk Mar 16 '13 at 17:32
  • I'm not sure I see the difference. How would including them in the Application cfc not make them available to only that application? I could include them from multiple applications but I could instantiate the same CFC from multiple applications too... I don't really understand the advantage of doing it this way. – Gary Stanton Mar 16 '13 at 17:36
  • My suggestion was to write the functions .cfm files which are available to any application on the server. They get called by cfinclude tags. Your option 5 was to write the functions directly in an application.cfc file. – Dan Bracuk Mar 16 '13 at 18:26
  • Option 1 seems ok to me but I don't understand why you then say "I'm not keen on referencing the Application scope from within the CFC." - why would you need to? Maybe it depends on what your functions are doing, but you should be able to pass any scope into your CFC to keep encapsulation – duncan Mar 16 '13 at 18:52
  • @DanBracuk Sorry if I was unclear, that's how I would have done it - not just because I could include them from elsewhere, but also to keep all that code out of the Application.cfc. I've updated the question to clarify. – Gary Stanton Mar 16 '13 at 19:02
  • @duncan I would be referencing the Application scope to call the functions from other CFCs as they're instantiated there. With regard to passing the scope to the CFC, is there an advantage over passing the instantiated object to the CFC? – Gary Stanton Mar 16 '13 at 19:07
  • Something to keep in mind is efficiency. If you cfinclude the udf files in your application.cfc, say in onrequeststart(), and don't use any functions on some pages, that would be inefficient. That's why I'm a fan of putting the cfincludes in the cfm pages that are going to use the functions. – Dan Bracuk Mar 16 '13 at 21:15
  • True enough, but the kind of functions I'd like to have as a base are very small, and generally the kind of things I think are missing from the language. I'd rather not have to manually include whenever I want to use them. Whilst looking about, I came across a blog by Ben Nadel (http://bit.ly/6ZrxQy) that experiments with putting these type of functions into the URL scope and using CF's scope searching to call them unscoped - so they appear part of the language. It's a great idea, but feels like a bit of a hack. – Gary Stanton Mar 16 '13 at 22:23
  • 1
    Since I use ColdSpring for EVERY application I write, I would create a CFc that is managed by ColdSpring and include the 'bean' on .cfm pages, or wire it in to .cfc files, that need it. Often times, when using a framework, I have beans that are included in every request...such as a 'formater' bean I use for formatting dates, etc. consistently throughout an application. – Scott Stroz Mar 16 '13 at 22:41
  • Alas, I've never used ColdSpring, in fact I've never really got on with any framework! I'm essentially creating my own here - a foolish re-invention of the wheel perhaps, but I like to tinker. ;) – Gary Stanton Mar 16 '13 at 22:55
  • Gary... your usage of the word "foolish" is apt here. Tinkering is for your spare time: for your applications do things properly. Scott has you on the right track: use ColdSpring. If you don't know how... *find out*. – Adam Cameron Mar 16 '13 at 23:13
  • Fair enough... I'm reading through the docs now. Conceptually it certainly looks like the right way to do things, but I'm concerned about the learning curve and not having much luck getting the examples to work on my Railo server... – Gary Stanton Mar 17 '13 at 00:39
  • Well, I've got ColdSpring working, but to be honest I'm struggling to see the advantage over manually injecting my dependent CFCs. Previously I was instantiating a bunch of CFCs and passing them to each other as arguments where one required use of another. Now I'm doing the same thing, but passing the CFCs to each other via the ColdSpring XML file. I still need the arguments in my .init methods only now I'm not able to set the other values in the method. The AOP stuff looks very interesting, but the basic functionality doesn't really seem to offer any benefits... I must be missing something! – Gary Stanton Mar 17 '13 at 03:05
  • use Railo and add them as global functions through their admin – Matt Busche Mar 17 '13 at 15:18
  • Much as I love Railo, and use it wherever I have the choice, I don't like to use functionality that won't be mirrored on ACF. Some of my apps have to run on that too. ;) – Gary Stanton Mar 17 '13 at 15:42

1 Answers1

1

If you are really concerned about structure and keeping things independent, don't even start with singletons or inheritance that extend functionality. Instead extend the base functionality within ColdFusion by appending your non-component library on runtime/request, see ColdFusion Developer's Guide. This doesn't magically solve all problems, but at least that's the proper way to implement general purpose functions.

Alex
  • 7,319
  • 1
  • 16
  • 34
  • I'm not sure I understand. The docs are suggesting I include those functions in the request scope. Conceptually I don't see how that differs from instantiating in the Application scope. My other CFCs would still rely on it being there and have to call them outside of their own scope. I see I wouldn't be instantiating with this method but what's the benefit? – Gary Stanton Mar 17 '13 at 14:59
  • The docs are not suggesting that you put your function in the request scope. They are showing you how to do so should you wish to. – Dan Bracuk Mar 18 '13 at 16:41
  • Ok, so the suggestion is that I add UDFs at runtime and not include them in a CFC at all. What happens then when I want to use one of them inside another CFC? If I have to include the .cfm file containing the UDFs in the pseudo constructor, isn't that bad for encapsulation? I'd have thought it better to have the location of the file as an argument to the init method - at which point isn't it easier to have the UDFs in an instantiated CFC and pass that as an argument? – Gary Stanton Mar 18 '13 at 18:17