Category: Ruby on Rails

Jan 5 2010

A File and Its Method: Planning out and implementing Server Specific Variables in your Applications

Here is the problem...

I want errors to be sent to the screen in development. I want errors sent to me via email in testing and production. I want the "contact us" email address to be mine in development and testing, but the client in production. I want all cfcs to be cached on every request in development (not cached, essentially) but not in testing and production. I can go on if you want, but that's just more reading for you. 

Simply put, I want my application to act slightly different in different environemnts. I didn't even bring up running the application locally.One way is to edit the code directly in each environment (really bad). Another is to have a config file in each environment (not as bad). But if you are like me, working in an enterprise environment, both of those options simply may not be possible.

Due to our change management policies, I have no control over anything after development, so hard coding anything is out of the question. Deploying different files in different environments would be possible but a terrific headache.

Solution One: cf-server.xml

I have in the past deployed an xml file to the root of each server that would handle how applications would handle several basic things. Mostly, when to cache cfcs and handle errors. The problems I had were these:

  • That xml file got overwritten on various deployments  and would end up being incorrect
  • It was very limiting.  It was per server and very few settings
  • I did not give a specific application its own settings. Sometimes I wanted it to handle caching or debugging differently than another (causeing bullet point 1)

Solution Two: Context.cfm and getContext()

Ok, once again I got this idea from Rails. Looking at the database config file where each environment is listed with all the connection information specified. That, of course, would not work in our enterprise environment due to developers not being privy to database login credentials apart from development. But it is a cool idea. Plan up front!. So why not do something like that?

So, first off, and I have said this before, we have our own framework. It's part MVC but not really and part OOP but not really. Very lightweight and pretty pwerful for building apps at warp speed. I am the primary developer managing the framework. So I get to be creative with it.

So this solution is going to be in the next release.  I have a working example and I think it would be pretty easy to implement regardless of how you are building apps. It also only took me about 30-60 minutes to build it in and start using from the point I got the idea. And I am pretty excited about it.

A File and a Method

It basically requires a file and a method in the Application.cfc file.

The idea is to create what I am calling an application context ( I have been using Railo a bit so "context" just popped into my head)

The File

Start by adding a context.cfm file to your app in an appropriate place. For example:

/yourdomain/config/context.cfm

This will will layout your different environments and for each environemnt, the server name and the list of variables you want instantiated.

Here is an example:

~Local
server:localhost
refresh:everytime
wak:/
error:screen
email:hold
status:true
deploy:none

~Testing
server:test
refresh:everytime.multipliedbyinfinity.com
wak:\
error:email
email:hold
status:true
deploy:none

~Prod
server:www.multipliedbyinfinity.com
wak:\
refresh:manual
error:email
email:send
status:false
deploy:manual

 

To give you and idea of what these settings are doing...

  • Wak - windows vs unix (yes I know there are better ways of doing this
  • Refresh - when and if to cache your apps cfcs
  • error - send to the screen with a big ol cfdump or pleasant error message and email big ol cfdump
  • email - send out emails generated by the app or not
  • status - the framework creates a status.cfm file that lets you know what cfcs got created automatically and if there were any errors along with other settings and info. Locally and development, let me just access it. in PPRD and PROD make it password protected or just not accessible
  • deploy: deploy.cfm will export the entire app from svn to the server. make it accessible just in development

You get the idea, right?

The Method

Here is the method that uses this file:

 

<cffunction name="getContext" access="public" returntype="struct" output="false">

    <!--- get the file root so we can cffile the context.cfm file --->
    <cfset fileroot = replaceNoCase(getbasetemplatepath(), "index.cfm", "")>

    <!--- which environment are we in? --->
    <cfset thisServer = cgi.server_name >

    <!--- read the file --->
    <cffile action="read" file="#fileRoot#/myAppLocation/config/context.cfm" variable="config">
   
    <!--- create a sturcture to hold the data --->
    <cfset context = structNew()>

    <!--- loop thorugh with the ~ delimiter  --->
    <cfloop list="#config#" delimiters="~" index = "i">
       
      <cfset server[i][1] = listFirst(i, "
    ")>

     <!--- set the enviroment (local, test, prod..)  --->
    <cfset currentEnv = listFirst(i, "
        ")>
    <!--- set the server of the environment you are looping through --->
    <cfset currentServer = listLast(listgetAt(i,2, "
        "), ":")>

      <!--- if the current server matches the servername in the this file, this is the set of variables that you need to instatiate --->
    <cfif thisServer eq currentServer>
   
    <!--- loop through all the variables and values and set them as elements in the stucture --->  
    <cfloop from="2" to="#listLen(i, "
            ")#" index="s"

         <cfset setting = listGetAt(i, s, "
                ")>   
        <!--- this is the  context.variable = value function--->
        <cfset context[listFirst(setting, ":")] = listLast(setting, ":")>
   
    </cfloop>
        <!--- go ahead and set the file type--->
        <cfset context.fileRoot = fileRoot>

    </cfif>
  
</cfloop>

<cfreturn context>

</cffunction>
Two things, yes I forgot to var scope the variables. on todo list. And not using chr() is sloppy.
 
So now add a call in your OnApplicationStart() method:

application.context = getContext();

Whammo!

You now have a structure in the applicaiton scope called context and you can start using it to decide how your app should work

<cfif application.context.debug eq "screen">
    <cfdump = "#foo#">
<cfelse>
   <cfmail/>
</cfif>

4 comments - Posted by Jonathan at 9:00 PM - Categories: Ruby on Rails | ColdFusion | General | Software | Framwwork | Subversion | Railo

Dec 10 2009

Subscribe

Categories

Monthly Archives

Search Archives

Favorite Links

My Links

Coldfusion Links

Recent Tweets