By Peter Bell

LightWire Code Available

LightWire is a lightweight ColdSpring alternative for a small set of use cases. It is currently very early proof of concept, but I've posted the code as a zip file. Let me know if anything doesn't make sense.

I'll put together a more robust sample application including this, CF Template and some of the other stuff I'm working on tomorrow.

Biggest issues: config file doesn't use XML and this requires a base class with an init() method and an addObject() method. If anyone liked the concept but wanted XML config, I might do that just to learn how to do it, but I don't need it for my use case. If anyone doesn't like base classes (and there are plenty of good reasons not to), this isn't the project for you.

No AOP yet (hopefully in the next week or two if I get a moment), no remote bean generation planned until I get the time to play with Flex, and this uses setter rather than constructor injection (ColdSpring supports both - this never will) so it loses the clearly documented "requirements contract" in its objects init methods (although they just get moved to the LightWire config file so I don't see that as an insurmountable problem).

I'm also thinking this isn't thread safe, so I'll have to test that and fix it over the next couple of days if it is an issue.

This is not a proven, tested framework like ColdSpring. It does show how easily you can write your own naive DI/IoC engine using setter injection and object based mixins (LightWire is 159 lines including spacing, commenting and brackets on their own lines. The two required base methods add 15 additional lines, so we're talking under 200 lines of code fully loaded excluding the config file).

Let me know if you like it. Let me know what sucks (and whether it could be fixed or if it is a fundamental limitation of the approach). And finally let me know if anyone sees anyvalue in playing with this at all. I've scratched my itch, so I'm back to application generation, but if anyone sees a niche for this, let me know and I'll tidy things up, add some features, provide documentation and a sample application and will release it as a mini-project.

[update] In case it isn't obvious, while I've initialized LightWire in index.cfm in the request scope, in a real application, you'd initialize it in the application scope as you would want your singletons to persist between requests.

Alright! Some good code to review.

I'll be quite pleased to review the sample application. This stuff is starting to click in my head....

# Posted By Dan Wilson | 10/9/06 9:56 AM
Hi Dan,

Any feedback much appreciated! This stuff is also starting to click for me. Honestly, it took me writing something that replicated ColdSprings core solution to start to really get what CS did! I'm slow that way :-<

Next up is CF Template and then after that a VERY naive set of ORM base classes so I start to understand what Doug has been working on all of these months with Reactor!
# Posted By Peter Bell | 10/9/06 10:03 AM

I haven't been able to run the code do the path mapping, but from what I have read, it looks really nice. I like the ease of the configuration file and the very small amount of code. I like the loading a lot, especially the lazy loading. Cool stuff.

You might want to consider adding some sort of public-var-scoping argument to the AddObject() methods. Right now, you store the passed object into the VARIABLES and the THIS scope making it both private AND public. If you had an argument that flagged for public scoping, you might allow the dependencies a bit more fine-tuning:

<cffunction name="AddObject" returntype="void" access="public" output="false"
   hint="Adds an object to the private and optinoal public scope of an object.">
   <!--- Define arguments. --->
   <cfargument name="ObjectName" type="string" required="yes" hint="I am the name of the object to add." />
   <cfargument name="Object" type="any" required="yes" hint="I am the object to add." />
   <cfargument name="AllowPublic" type="boolean" required="false" default="true" />
   <!--- Store the object in the private scope. --->
   <cfset VARIABLES[ ARGUMENTS.ObjectName ] = ARGUMENTS.Object />
      Check to see if this object should also have public scoping.
      By default we will do this, but this gives the user the option
      to create private-only dependent objects.
   <cfif ARGUMENTS.AllowPublic>
      <!--- Store a reference to the object in the public scope. --->
      <cfset THIS[ ARGUMENTS.ObjectName ] = ARGUMENTS.Object />
   <!--- Return out. --->
   <cfreturn />

Also, I would suggest always scoping your arguments ( I find it just makes things more consistent and will cut down on naming conflicts. I just recently ran into a problem where I had an argument and function named the same thing and when I kep trying to call the function pointer, it kept trying to evaluate the argument as a method (which was actually a boolean value) since the ARGUMENT scope was lower in the scope chain. Anyway, I find it a good habbit to just scope everything now a days.

I really like the use of the SUPER initialization. In fact, I like the use of StructAppend() and the whole manipulation of the VARIABLES and THIS scope in general. Cool stuff.

I would not include the config file directly into the LightWire.cfc... or rather, I would not hardcode the path. Why not send the include path in the INIT() method. This way you could swap in and out config files if you like. Although, I suppose if you are always including the config file in from the same place, it's not even an issue. However, if required, you could have multiple config file in the config folder, ex. LightWireConfig_dev.cfm, LightWireConfig_live.cfm or something like that and then let the user decide which one to use.

Minor comments:

I would use StructKeyExists() over IsDefined()... but that is just personal.

I would add more white space. Looking at my code sample above, you can probably see that I LOVE me some white space. I personally think it ups the readability like 100% percent, but then again, I am also convinced that I have reading problems, maybe some dislexia or something.

Len() can be considered a boolean. This is just personal of course, but I find just Len() more readable thatn (Len() GT 0)... but that is very personal, not having anything to do with best practices. Actually, speaking of best practices, i think the use of Len() as a boolean used to *be* best practices accroding to Macromedia... but then someone re-wrote the best practices documentation (was it Sean? I can't remember), I think they want back to using the GT 0 syntax. Personally, I think the less I have to read, the less I have to understand :)

I am not sure the use of enableCFOutputOnly="Yes" is requied so much. You seem to use it inside of Components that don't allow output (ex. Application.cfc... LightWire.cfc ... should LightWire.cfc have output="false"???). This would seemingly make it not requied. Then, at the bottom of the light wire config file, you include <cfsetting enableCFoutputOnly = "No">. This can be very misleading. You have turned enablecfoutputonly one TWICE at this point (once in Application.cfc and once at the top of the config file. This would require turning it offer TWICE in order to dispable cfoutputonly. By turning it off once at the bottom all you do it counteract the one at the top of the file. Someone who doesn't know more about enabling output (which may very well include me) might think that this is turning it off, but in fact, the one from Application.cfc is still dominating. Also, there seems little need for it on the config file as it is included by the INIT() method, which itself, does not allow output.
# Posted By Ben Nadel | 10/9/06 10:45 AM
Hi Ben,

Great comments, thanks!!!

I actually only put the objects into the THIS scope for testing – I was planning on making it private only in production as objects really shouldn’t be sharing via the public scope, so for now I’ll just remove “THIS” when I leave test mode. If people really wanted to be able to share in public I’d add the option as you suggested.

StructKeyExists is the way I've done this in the past. Not sure why I didn't use it this time. Will change.

You’re right about arguments scoping. I usually scope properly but wanted to try that out as I’d seen others doing it. I’m going back to fully scoping everything and will upgrade LightWire as part of this.

I also like the idea of passing in the path to the config file as part of the init of LightWire – great enhancement.

By default I just put the cfenableoutput at top and bottom of included files, but you are right and I’ll remove it from the ones included within the components. Had a feeling it was the wrong thing to do, but I’d rather put it in and have someone tell me it isn’t necessary than forget it!

Many thanks for all of the great feedback. Will clean up code and repost tonight based on this and any other comments.

Best Wishes,
# Posted By Peter Bell | 10/9/06 10:47 AM
BlogCFC was created by Raymond Camden. This blog is running version 5.005.