What Does cfargument Really Do?

It seems obvious that cfargument defines the arguments for your udfs/methods. It is easy, however, to have assumptions about what it does that aren't correct.

Primarily, cfargument defines that arguments that you expect for your function and the order in which you expect them. This helps document your code.

It also acts like cfparam in that it defines a data-type for an argument (throwing an error if the argument isn't of that type) and either making the argument required or providing a default (technically you can do both or neither in cfargument, but that doesn't really make sense. 

It also assigns names to arguments that are passed in ordinally (that is, arguments passed in by order but without a specified name). This allows you to pass in arguments either by name or by order.

What cfargument doesn't do, however, is just as important. It doesn't restrict the arguments that come into your function to those specified by cfargument tags.

So, any arguments that are passed in by name will still exist in the arguments structure of the function. This has both drawbacks and advantages. The drawbacks primarily have to do with security. If you use argumentCollection to pass in a Form structure then any form field will be sent in as an argument.

Among the advantages are the ability to pass in argument names that are not known before hand. It also means that you can have named arguments without any cfargument tags at all (though you lose out on important documentation if you do).

Ideally, I would like to be able to check what arguments have been defined by cfargument from within the tag (so that I could programmatically delete other arguments if I wanted without changing that code every time I added an argument). I suspect it can be done, I just don't know how.

Anyone? 

Podcasts in the ColdFusion Universe

I started listening to the ColdFusion Weekly this year. I got started a bit late, but I have been catching up slowly. I finally got caught up recently. Of course, as soon as I got caught up, they took a week off (just my luck).

The good news is that this gave me a chance to listen to some of the interviews on cfFrameworks.com. I haven't listened to them all yet, but so far they are really good.

Peter Bell's interview in particular really impressed me. He was able to explain concepts in an enlightening and informative way. He explained concepts that I didn't understand so that I felt like I had a good idea about them and explained subjects that I thought I understood in a way that gave me a new perspective.

I liked his definition of a framework: "A set of code and conventions that embody a set of opinions about how to solve a class of problems.". He goes on to say that without code you just have a methodology and without conventions you just have a library and without an opinion its a waste of time. I disagree with the last part, but I still think that it isn't a framework unless it embodies an opinion. Anyway, that is just a sampler from the first two minutes of the interview - good stuff.

The one drawback to the cfFrameworks interviews is that the sound quality is a little uneven and sometimes that sound seems not to be cut together right. Despite that small drawback the interviews are well worth a listen.

As to ColdFusion Weekly, that has been consistently good. The topics are good as is the production quality. I had listened to a few last year and the production quality has improved noticeably since then.

I enjoy the news coverage, but I do worry that it leaves them constrained in the amount of time available for their focus topic. Even so, that is consistently well done.

If you haven't listened to ColdFusion Weekly and the interviews on cfFrameworks.com, you should give them a try.

Another podcast is potentially in the works is one by Sean Corfield. That would certainly be worth checking out as well.

I am really excited to keep listing to these podcasts - I think I am finally starting to get the hype on podcasts. 

DataMgr Rethinks the Data Access Layer

Prolific blogger, Peter Bell, just posted an entry about "Rethinking the Data Access Layer" wherein he mentions DataMgr and ORM solutions and lists his requirements for a database abstraction tool.

I couldn't resist responding with how DataMgr meets those requirements.

Speaking in Tongues

DataMgr provides one API that works regardless of your database. This currently includes MS SQL, Access, MySQL, and PostGreSQL. Others should be easy to add.

Get Associations

Peter mentions the desire to have CategoryService.getAssociatedProducts(CategoryID). While DataMgr won't do this directly (it is a database astraction layer, not an ORM), it does provide capabilities that are helpful in this regard.

Assuming you use XML to define your relationship (you could use setColumn() instead):

<table name="categories">
  <field ColumnName="CategoryID" CF_DataType="CF_SQL_INTEGER" PrimaryKey="true"  />
  <field ColumnName="CategoryName" CF_DataType="CF_SQL_VARCHAR" Length="80" />
  <field ColumnName="Products">
    <relation table="products" type="list" field="ProductID" join-field="CategoryID" />
  </field>
</table>

Then getRecords("categories") would return a field called "products" with a comma-delimited list of ProductID values from the products table for the category in each row.

Cascading Deletes

Taking the example above, the relationship element has an attribute of "onDelete" with a default value of ignore. In the above example, the deletion of a category will have no effect on the products table.

You can change the element to this:

<relation table="products" type="list" field="ProductID" join-field="CategoryID" onDelete="Cascade" />

In which case all related products would be deleted.

Similarly, you could do this instead:

<relation table="products" type="list" field="ProductID" join-field="CategoryID" onDelete="Error" />

In which case DataMgr would throw an error if you tried to delete a category that had related products.

The advantage of this over database constraints is that this works with logical deletes as well.

Manage Joins

I'm not honestly sure I understood what Peter want here, but DataMgr handles several types of joins. The list type (mentioned) earlier can also be used in saving for a many to many relationship (a join-table attribute is needed).

It also has a label relation field:

<table name="categories">
  <field ColumnName="ProductID" CF_DataType="CF_SQL_INTEGER" PrimaryKey="true" />
  <field ColumnName="ProductName" CF_DataType="CF_SQL_VARCHAR" Length="80" />
  <field ColumnName="Category">
    <relation table="categories" type="label" field="CategoryName" join-field="CategoryID" />
  </field>
</table>

Using this XML, getRecords("products") would return a recordset with a "Category" field that would show the CategoryName of the category for the product in each row.

Magic Fields

In DataMgr, these are "Special" fields.

<table name="categories">
  <field ColumnName="ProductID" CF_DataType="CF_SQL_INTEGER" PrimaryKey="true" />
  <field ColumnName="ProductName" CF_DataType="CF_SQL_VARCHAR" Length="80" />
  <field ColumnName="Category">
    <relation table="categories" type="label" field="CategoryName" join-field="CategoryID" />
  </field>
   <field ColumnName="DateAdded" CF_DataType="CF_SQL_DATE" Special="CreationDate" />
</table>

They include the ability to store the date a row was added or the date it was last updated as well as the ability to automatically handle manual record sorting as well as logical deletions.

They don't have the ability to handle other types of automated data that Peter mentioned, but I would be open to adding more Special types in future versions.

Everything I Need - and Nothing More

I think this is where DataMgr shines. It doesn't enforce an OO paradigm (although it could be used in one - one small ORM has already been built on top of DataMgr). The performance overhead of DataMgr is similarly minimal. 

Aggregate Subqueries

Peter mentioned this need in the comments. DataMgr handles this as well.

The best examples are found in the aggregates page of the demonstration site. 

Plenty of other examples are available as well. Check out the DataMgr demonstration site , or download CodeCop for a working application running on DataMgr. 

Using a Page Controller to simplify AJAX

While working on a form that makes use of some simple AJAX, I discovered that using a Page Controller can make this easier - especially when combined with JSMX.

[More]

Almost Done!

One of my new quarter's resolutions was to finish DataMgr 2.0. March is more than half over, so I am running out of time.

Fortunately, I have RC3 done. I didn't add any new features, but I did fix a few small bugs. This included a bug in MS Access and a bug in seeding data upon table creation.

Assuming that I get DataMgr 2.0 finished by the end of this month (and I think I will), I will get at least half of my goals met. Certainly not as good as I had hoped. Nevertheless, I really like the format of the quarterly resolutions over annual resolutions. I feel like it has kept me more focused.

The build for DataMgr 2.0 RC3 should exactly match the build for the final version. Unless, of course, more bugs are found. As the latest bugs were pretty minor, I am optimistic that they won't be.

Overall, I am really happy with the new version. I got in all of the major features that I was hoping to. Of course, I have since thought of one or two more features that I might add into a point release later. That will probably be a little ways off though as I am ready to work on other things (some of which will take advantage of DataMgr).

Mailer.cfc Notices

I have blogged about Mailer.cfc before, but it can do more than send a static email. You can also tell it about a notice that you want to send and later send that notice with personalized information.

To add a notice, use the addNotice() method. You must give the notice a unique name by which Mailer.cfc will reference the notice. The other arguments are the same as the arguments for the send() method. You can also include markers for data by wrapping a word in brackets. For example, you can have "Dear [FirstName]," in the contents and have "[FirstName]" replaced with a first name when you send the notice.

The addNotice() method also has an optional datakeys argument as a list of required variables for which markers are in the notice. 

To send a notice, use the sendNotice() method. This method has just two arguments:

  • name: the name of the notice you want to send
  • data: a structure of data to replace the markers used in addNotice()

So, the data structure could have a "FirstName" key to replace the "[FirstName]" marker. The keys in the structure can also replace any of the arguments of the send() method. For example, it could have a "To" key to replace the "To" argument of the send() method.

That's it!

Mailer.cfc is free and open-source. Version 1.5 is currently in beta. 

Want to Learn OO in CF?

For anyone wanting to learn OO (or just OO concepts) in CF, a new resource is available. Nando has started a new section on is blog called (appropriately) OO in CF.

If you aren't familiar with Nando, he is a frequent writer on the CFCDev list (another great resource for learning OO in CF). Like many of us, he started learning OO after CFCs were introduced to ColdFusion. His explanations are always clear and helpful.

He has two posts on the topic already and they are both very well written.

Even if the topics he is currently covering seem pretty introductory, this is certainly one to watch as he is likely to cover increasingly difficult material as time progresses. 

When you get the chance, read OO in CF.

A ColdFusion Page Controller

A hate to admit it, but ever since I stopped using Fusebox a few years ago, I have been without a controller. I had my view separated from my model, but no controller to be seen. In fact, I even questioned the need for a controller on most of my projects (while seeing how it could help on larger projects). I was all ready to write a "Do I need a Controller" blog entry.

And then...

[More]

DataMgr Performance

I finally got around to running some performance tests on DataMgr and I must say that I am surprised by the results. I ran test of 1000 iterations on three actions: insert, get (one record) and list (getting a recordset of about 2000 records) all against MS SQL.

I used straight cfquery as my basis of comparison. I knew that cfquery would be faster, but I was surprised at how close the results were.

insert

  cfquery DataMgr
Avg: 1 7
Min: 0 0
Max: 51 120

get

  cfquery DataMgr
Avg: 1 5
Min: 0 0
Max: 40 100

list

  cfquery DataMgr
Avg: 379 377
Min: 190 200
Max: 931 591


Although the percentage difference between cfquery and DataMgr may look significant for insert and get, note that DataMgr added - on average - only between 4 and 6 milliseconds of processing time. For retrieving several records, DataMgr didn't incur any noticeable performance impact at all.

The upshot of this is that although DataMgr does add some processing overhead, it is very minimal.

I hope to eventually do more performance tests, hopefully adding in DataMgr 1.0 (or 1.2) to the comparison as well as perhaps adding in some ORM solutions for comparison as well.

DataMgr 2.0 RC2 is feature complete. Database testing did find a few small bugs, so I will have a RC3 release prior to the final 2.0 release.

Mailer 1.5 and Testing

For some time now, I have found testing email related functionality to be a bit of a pain. This has become especially true since I started using well-encapsulated CFCs.

For example, I will often need send email to users of the site. Sometimes, I want to test this using actual data, but I don't want to risk the messages to actually send during testing.

Historically, I have changed the code during testing to get around this. This approach has proved to be labor intensive and is a bit risky as well. 

I realized recently that since all of my code uses Mailer.cfc to send email now, I should be able to take advantage of this centralization of functionality.

Taking a page out of my DataMgr playbook, I now have a Mailer_Sim.cfc as part of Mailer 1.5 (now in beta). This allows me to have code in my CFC that sends mail using Mailer.cfc. Depending on the instantiation of Mailer.cfc, it may send the email or it may merely log the email to the database instead.

Here is an example in which request.isProduction is used to indicate if this is a production site. This example assumes that I have DataMgr stored in Application.DataMgr (DataMgr is only needed when using the logging feature).

<cfif request.isProduction>
  <cfset Application.Mailer = CreateObject("com.sebtools.Mailer").init("mail.example.com","admin@example.com")>
<cfelse>
  <cfset Application.Mailer = CreateObject("com.sebtools.Mailer_Sim").init(MailServer="mail.example.com",From="admin@example.com",DataMgr=Application.DataMgr)>
</cfif>

I then pass Mailer.cfc into my components with no concern for whether the site is production or development. On the production site, the email messages sent out via Mailer.cfc will go out normally. In development, however, they will be written to the "mailerLogs" table in your database.

I could also log email that I do send by using the startLogging() method of the main Mailer component, which takes DataMgr as an argument.

Here is some example code to send email:

<cfinvoke component="#Application.Mailer#" method="send">
    <cfinvokeargument name="To" value="sample@example.com">
    <cfinvokeargument name="Subject" value="My Subject">
    <cfinvokeargument name="Contents" value="Message Text">
</cfinvoke>

This would send an email to sample@example.com unless it was using Mailer_Sim, in which case it would write that email to the database instead.

Mailer.cfc is open source and free for any use. The component docs for 1.5 are also available. 

More Entries

BlogCFC was created by Raymond Camden. This blog is running version 5.8.001.