fusiongrokker

Entries Tagged as Best Practices

Finding the current DB type in a Mango plugin

I was recently burned because I wrote some SQL that was specific to MS SQL Server in one of my Mango plugins. It didn't occur to me at the time that my code might not work against other databases, because 99% of the time I have control of both my code and the DBMS that it runs against. That's not the case when writing a plugin and releasing it into the wild -- anything can happen. Now that I've figured out how to properly account for multiple database types inside a Mango plugin, I thought I would share that knowledge with you so you don't make the same mistake I did.

Mango's plugin API provides a couple of hooks into its core, which prove to be very useful here. Whether you're building your plugin by following the instructions on mangoblog.org, or by taking an existing plugin and modifying it to do what you need, you will have an Init function that takes 2 arguments: blogManager and preferencesManager. The BlogManager argument is your starting point for interacting with Mango and provides access to a series of other "managers" and "interfaces" that allow you to interact with Mango via the API instead of modifying the database directly -- ensuring the most possible backwards compatability when Mango changes.

Hint: You can dump an instance of blogManager to see available functions, then dump the return value of those functions to see what they return.

In your init function, you need to save a reference to the BlogManager so that you can use it later, when an event is dispatched:

Now that we have a reference to BlogManager, we need to use it when an event is dispatched to find out which database type the current user is running. To do that, we need to use the QueryInterface object, returned from blogManager.getQueryInterface(). This object has a method called getDBType() that will return a string describing the current DBMS. Mango supports MySQL and MS SQL Server 2000 and 2005.

DBMSReturns
MS SQL Server 2000 mssql
MS SQL Server 2005 (Express) mssql_2005
MySQL mysql

Now that we know what values we can expect from this function, we can safely write custom SQL per potential database type. I've started writing a fail-safe default case as well; so that even if it's not the best way to go about things, at least the plugin will work. Here's some example code showing how to use the reference we saved to BlogManager to find out the database type, and then to use that to write separate SQL statements depending on the DB type, and a default.

Note that I'm not querying the database directly, I'm using the QueryAdapter's makeQuery() function to execute the SQL. This is considered the best practice. When I use a CFOutput or CFLoop to process the results of the query, I limit the rows using from="1" and to="#variables.intPopularPostCount#" so that even in the default case the correct number of rows is used.

Any questions?

Posted in Best Practices | Mango | 1 Response December 09 2008

Best Practice: Separate App config from Framework Config

Best Practices is something that I don't think anyone, anywhere, ever gets 100% right. There's no "right" way to do everything. But we're striving to do things better all of the time, right?

For example, in the ColdFusion world we have Unit Testing frameworks like MxUnit, CFUnit, CFCUnit, and Selenium. I know Selenium isn't just for CF, but it does integrate nicely! Test-driven Development is all the rage these days, and while that's a good thing it's not for everyone and certainly something very difficult to "get right."

Consider that in a large project -- one with 1,000 test cases -- if you refactor some code that affects 10% of your tests, you have to re-write 100 test cases. This isn't to say that TDD is a bad thing... far from it! Just that there is no silver bullet, and that you always have to be thinking ahead to try and do things right the first time, and not repeat yourself.

This brings me to a change I recently made in Grub.

Grub uses the Model-Glue framework to, as I like to say, "code less and do more." In Model-Glue's XML config, we have an <include> tag that allows us to separate our config into logical sections to keep maintainability high. But did you know you can do the same thing with your ColdSpring XML config?

Well, not with out-of-the-box 1.0... You have to download the Bleeding Edge Release, here. The feature was first mentioned in November of 2006, when Jared announced that he had written it. It was then blogged by a bunch of other ColdSpring users, but I don't think any of them mentioned that it wasn't yet included in the official release except Mark Drew, and even then I don't think I picked up on that fact until well after my second or third reading of his post. I ended up reading this bug report that clued me in, after about an hour of trying to figure out why the necessary method wasn't where it should be.

After you download the BER and update your local copy of ColdSpring, you can add lines like the following to your beans XML:

I used this code to separate out my Model Glue config -- which will be constant in every environment, but different between them (debug, reload, allow event generation in dev; no debug, no reloading, and no generation in production or staging) -- from my bean definitions, which change frequently as I work on the applicaiton. Now, with my ANT build script, I can build and deploy my project without ever thinking about config, and I know it will always be right because it will never change without me doing so manually.

I'm somewhat surprised that over a year has gone by and it's still not included in the official release. This means that I'm currently using two unreleased frameworks in Grub (the other being the Model-Glue 3 RC), and that I am going to have to include them both in the download to make it as user-friendly as possible.

Posted in Best Practices | ColdFusion | Frameworks | 5 Responses July 29 2008

Excellent article on customizing Application.cfc per-environment

About a week ago (who has time to keep up with this stuff?) Ben Nadel wrote an excellent article about Setting custom Application.cfc settings based on the server environment, which I think is great supplemental reading to my article about Setting environment-specific Application.cfm settings.

As Ben points out, the important distinction is that his internal function is only setting up the Application.cfc settings, not Application variables; while the goal of my post was to aid in determining which values to set for your Application variables.

We differ on the fact that he thinks you should avoid having configuration files outside of the web root, while I think they are just fine as long as you code appropriately. In my example, if the file is not found, or the contents don't match any of the expected contents, then we assume we are in production. This may cause some frustration while configuring new development, test, QA, or staging environments; if you forget that the file has to exist and have appropriate contents, but ensures that unless someone deliberately sets a non-production value in the config file, production will always be right.

Posted in Best Practices | ColdFusion August 17 2007

Scope Nazi

Some people are anal retentive about not using pound signs where they aren't necessary (my boss), some people are anal retentive about var-scoping local variables in components (Raymond Camden); and then there's Me. I'm anal retentive about scoping all of your variables. Consider this. When you execute this line of code:

There is only one thing that could happen: A variable in the "variables" scope is set with the value "bar". (Unless you're in a CFC, in which case you should be using var scope, like Ray advocates. ;) On the other hand, when you execute this line of code:

The value could potentially be read from one of NINE scopes, in MX7. To clarify, I'm fine with implying your variable scope, however, it irks me to no end when you infer your variable scope. (What's the difference between implying and inferring?) To wit, a colleague pointed out that she was having issues with this code:

<CFQUERY name="orders" datasource="#application.DS#"> SELECT distinct ORDERS.ordid, ORDplacedOn, ORDbillFirstName, ORDbillLastName, ORDTotal, OrdTotal + isnull(Adjusttotal,0) as netsales, ORDERS.OSTATUS_id, ordassignedTo, OSTATUSname, orders.ORDshipAmount, p.prodassembly_reqd FROM ORDERS, OrderStatus, payflowpro, (Select ordid, sum(adjusttotal) as adjusttotal from OrderAdjust where result=0 group by ordid) as OrderAdjust, OrderItem OI, ItemView I, Product P, ordershipaddr osa WHERE ord_completed = 1 AND ...

<CFOUTPUT query="orders"> <CFQUERY name="getManu" dataSource="#application.DS#"> select distinct manname, manufacturer.manid, itemname, albumid from OrderItem, itemview as item, product, manufacturer where orderitem.ordid=#orders.ord_id# and item.itemid=orderitem.item_id and item.prodid=product.prodid and product.manid = manufacturer.manid A

The issue was that sometimes the variable prod_assembly_reqd was undefined. The resolution was simply to use proper scoping. This is the code I sent back:

A&nbsp;

Using explicit scoping like this leaves zero room for confusion where the data is coming from. The last line, where I used the scope "?????", I noted that this is probably inferring the Variables scope, but that she should check on it and make sure she indicates which it is.

Posted in Best Practices | ColdFusion July 24 2007