Wednesday, July 25, 2007

Remove auto-registered web services while you are working

One of the little annoyances I've run into while working with publishing web services in ColdFusion is that it can be tiresome to remove the auto-registered wsdl files as you test. Here is a little bit of code you can stick at the top of your test files to clear out that cache.


<cfinvoke
component="CFIDE.adminapi.administrator"
method="login"
returnVariable="administrator" >
<cfinvokeargument name="adminPassword" value="yourAdminPassword" />
<cfinvokeargument name="adminUserId" value="admin" />
</cfinvoke>
<cfinvoke
component="CFIDE.adminapi.extensions"
method="getWebServices"
returnVariable="extensions" >
<cfinvokeargument name="includeAutoRegistered" value="true" />
</cfinvoke>
<cfloop collection="#extensions#" item="webservice">
<cfinvoke
component="CFIDE.adminapi.extensions"
method="deleteWebService" >
<cfinvokeargument name="name" value="#webservice#" />
</cfinvoke>
</cfloop>

Monday, July 23, 2007

Typed arrays in cfcs

This is interesting. I was trying out the new shorthand methods of creating arrays (and structures) and I came across this strange effect.

I haven't found any docs on this yet, but it seemed like a natural extension of the array shorthand. You can now do something like this:

<cfargument name="aggregate" type="dm3.com.color[]" required="false" default="#arrayNew(1)#">

The idea is to have cf throw an error if you submit anything other than an array of dm3.com.color objects. At first, this seems to work. Great! I thought. What I later found out was that the type checking appears to only occur on the first item in the array. You can put anything you want in any position other than one and the type checking will let it go through without error.

For me, the way to prevent issues (however unlikely they actually may be) is to restrict adding and removing collaborating objects with add() and remove() methods that can enforce the object type on each element. These work pretty much the same as your simple getters and setters, but instead of dealing with a simple value, they deal with one or more objects.

--- UPDATE: Sample code to illustrate the issue
The two objects are just for illustration. You should be able to use any objects you've created that use arrays to store aggregate collections. The scribble.cfm page at the bottom is used to exercise them.


Color.cfc

*============================================================================
* Template Name: Color
* ---------------------------------------------------------------------------
* Created by: CFC Blaster
* Creation date: 07/23/2007
* Description: Color Bean
*============================================================================

<cfcomponent name="Color.cfc" output="false">
<!--- Define --->
<cfproperty name="Background" required="false" default="" type="string">

<!--- Initialize --->
<cffunction name="init" access="public" output="false" returntype="dm3.com.Color">
<cfargument name="Background" required="false" default="" type="string" />

<cfscript>
// setup
variables.instance = structNew();

// assign arguments passed in to properties
setBackground( arguments.Background );

return this;
</cfscript>
</cffunction>

<!--- Accessors --->


<cffunction name="getBackground" access="public" output="false" returntype="string">
<cfreturn variables.instance.Background />

</cffunction>
<cffunction name="setBackground" access="public" output="false" returntype="void">
<cfargument name="Background" required="true" type="string" />
<cfscript>
testBackground(arguments.Background);
doBackground(arguments.Background);
</cfscript>
</cffunction>
<cffunction name="testBackground" access="public" output="false" returntype="void">
<cfargument name="Background" required="true" type="string" />
<!--- TODO: Write business rules --->
</cffunction>
<cffunction name="doBackground" access="public" output="false" returntype="void">
<cfargument name="Background" required="true" type="string" />
<cfset variables.instance.Background = arguments.Background />
</cffunction>

<!--- Print --->
<cffunction name="print" access="public" output="true" returntype="void">
<cfdump var="#variables.instance#" />

</cffunction>

<!--- Equals --->
<cffunction name="isEqual" access="public" output="false" returntype="boolean">
<cfargument name="obj" required="true" type="dm3.com.Color" />
<cfset var private = structNew() />
<cfwddx action="cfml2wddx" input="#print()#" output="private.obj" />
<cfwddx action="cfml2wddx" input="#arguments.obj.print()#" output="private.obj2" />
<cfif private.obj eq private.obj2>
<cfreturn true />
<cfelse>
<cfreturn false />
</cfif>
</cffunction>

<!--- Run --->
<cffunction name="run" access="public" output="false" returntype="dm3.com.Color" >
<cfset var obj = createObject("component","dm3.com.Color") />
<!--- TODO: fill in reasonable values --->
<cfset obj.init(
Background = ""
) />
<cfreturn obj />
</cffunction>

</cfcomponent>


temp.cfc

<cfcomponent output="false">

<cfproperty name="TestString" type="string" default="Hi There" required="false" hint="Here is where you would write your documentation.">
<cfproperty name="TestId" type="numeric" default="99" hint="z" />
<cfproperty name="AnArray" type="array" default="0">
<cfproperty name="YsNo" type="boolean" default="false">
<cfproperty name="colorArray" type="dm3.com.color[]">

<cffunction name="init" access="public" output="false" returntype="dm3.com.temp">
<cfargument name="aggregate" type="dm3.com.color[]" required="false" default="#arrayNew(1)#">
<cfset variables.instance = structNew() />
<cfreturn this />
</cffunction>

<cffunction name="setColorArray" access="public" output="false" returntype="void">
<cfargument name="val" required="true" type="dm3.com.color[]" />
<cfset variables.instance.colorArray = arguments.val />
</cffunction>
</cfcomponent>


scribble.cfm

<cfset colorArray = arrayNew(1)>

<cfloop index="i" from="1" to="3">
<cfset arrayAppend(colorArray,createObject("component","dm3.com.color").init())>
</cfloop>
<cfset arrayAppend(colorArray,"Hi There")>

<cfset temp = createObject("component", "dm3.com.temp").init(colorArray)>
<cfdump var="#colorArray#">
<cfdump var="#temp#">


Move the "Hi There" line above the loop to see the type checking fail. Move it below the loop to see it succeed.

Thursday, July 19, 2007

cfcBlaster gets a DIAPER

I made a small update to the cfcBlaster code that has made some of the work I've been doing with unit testing a little easier.

There is a new template called bean.xslt that I put together based on the Streamlined Object Modeling DIAPER ideas. It can only handle the simple properties that you can pull out of your db. You would have to add on any/all object collaboration methods and properties by hand.

There are a few things I like about these objects. The first is that they are really well encapsulated to start with (you can certainly make them less so as you work with them). They are completely ignorant of storage. I've been pushing that functionality into the DAO/Gateway object(s). You can easily instantiate one using the scribble pad in cfeclipse and it will generally work. You don't need any supporting code to get the thing off the ground. The run method is handy because not only does it make getting a sample of the object with reasonable values really easy, it can also be used to help with the mock object issues with some of the unit testing frameworks.

Speaking of cfeclipse, while the resulting cfcs are a little large (bloated?), they do collapse up pretty nicely.

Thursday, July 12, 2007

VSS Plugin update for Eclipse 3.3

If you've been using the VSS Plugin for cfeclipse and you're looking to upgrade to eclipse 3.3, you'll find that some of the functions don't work correctly. In particular, for me, I lost the ability to inspect the file histories in vss.
VSS Plugin Patch

Kudos to Jeff Barcalow for patching the jar file and Peter Szanto for packaging up the distribution and publishing it.

Use at your own risk, though.