Handling Nested Resources in CFWheels

Several frameworks these days are including a routing system to make it easy to implement user friendly urls, or as some like to call them, search engine safe urls. These routing systems can also make refactoring much simpler as well if you use them properly.
I have been playing around with CFWheels lately and was wondering how I could take advantage of nested resources in this framework. Nested resources is something I picked up while working on some Rails projects, its a way to automatically handle related objects in your forms. Let me show you a quick example.
Say we are building a bug tracker. We have 2 objects, a Projects object and a Ticket object. Here is how the associations are setup.

Project.cfc


and now our Ticket.cfc


Now normally if you were adding a ticket, you could use a URL like this:
localhost/tickets/new
And you could pass in the projectID as a hidden field or something like that to handle the association.
But, there is a much easier way to handle this. First lets take advantage of the routing system and create a few routes. I like named routes in my applications much better than the defaults. If I ever refactor a controller and rename an action, etc. I can change the route and every link in my site that points to that route is automatically updated for me. Hows that for a time saver?


These 3 routes handle the nesting for us. Notice how the project part of the url is always first? Then the nested resource since tickets belong to projects. This URL structure will ensure that our projectID is always available in the params scope. Our URl will now look like this if we are adding a ticket to a project.
localhost/project/1/tickets/new
Heres a typical link that would point to our form to create a new ticket.


#linkTo(text="New Ticket", route="new_project_ticket", projectid=params.projectid)

To setup our form for the new ticket an automatically populate the projectID hidden field, we can write our controller action like this.


That takes advantage of the built-in associations in Wheels and builds the new ticket object through the project object for us. You form would be a standard Wheels form but will automatically have the projectID set in the hidden field for you.

Great Intro To Git

I have been asked by several folks lately in the ColdFusion community about Git. It seems that alot of the CF developers are so in-grained with SVN that they either wont consider Git or are afraid of it. Not sure which is the case. But I will say that Git has completely changed the way I look at source control. Ric Roberts has posted a really nice intro to Git that anyone interested in Git should have a look at.
Im planning to do a presentation on Git for the Online CF Meetup as soon as I get time to get the slides together so hopefully that will help de-mystify Git for some of you as well.

Updates to Related_Selects plugin

Two weeks ago, Corey Ehmke sent a pull request for some updates he made to the related_selects plugin on GitHub. Im actually quite late in pulling these but I have been extremely busy with work and family stuff. Here is what Corey said about the changes:
 

I made a couple of changes that you will hopefully incorporate:

1) Expanded support for include_blank to match behaviour of standard Rails form options helper. You can now pass an :include_blank attribute consisting of true, false (default), a string, or a two-element array, just like the standard Rails select form helper. If you do not set this attribute, the related select will default to the first option.

2) I also edited the README and replaced the original examples with the (much more concise and comprehensible) example from your post on angry-fly.com.

 
This is great! Thanks again for the updates Corey!
 

Related_Select plugin update for Rails 2.3

Last night I was looking for a solution that would allow me to easily create a setup of related select boxes. I needed 3 of them to be exact. You know the ones that always seem to pop-up from time to time where the contents of one select box are populated after selecting a value on the first select box.
I started googling for a solution and came across a Rails plugin called related_select_forms that was written by Dimitrij Denissenko back in 2007. I figured “what the heck” it looks easy enough. Well after installing it from the svn repo on google code and setting up my initial form fields, I was welcomed by a plethora of errors. So I started digging into the plugin to find the issues and ended up fixing everything so that it now works with Rails 2.3.2.
I sent an email to Dimitrij asking about the status of the plugin but I havent heard back from him as of yet. I created a GitHub repo for the plugin to allow me to share my fixes with everyone. It can be found here:
http://github.com/russjohnson/related_select_forms/tree/master
Let me take just a second to show you how easy it was for me to create 3 related select boxes in my form with this plugin.
Consider the following models:


class Metro < ActiveRecord::Base

   has_many :areas

end


class Area < ActiveRecord::Base

   belongs_to :metro

   has_many :neighborhoods

end

class Neighborhood < ActiveRecord::Base
   belongs_to :area

end
Just looking at the models it should be plain to see how the selects should be related. You must first select a Metro, then that will populate the Areas. Once you select the area, that will populate the Neighborhoods.
Normally this would take a lot of handwritten Javascript to accomplish. And I know there are probably some super simple jQuery plugins for this thing, but this application is using the standard Prototype/Scriptaculous combo that ships with Rails.
So here is the how simple the form fields are using the plugin.

<%= f.label :metro_id, ', :class => 'title' %>
<%= f.collection_select :metro_id, Metro.find(:all, :order => "name"), :id, :name, :include_blank => true %>

<%= f.label :area_id, ', :class => 'title' %>
<%= related_collection_select(:sales_contact, :area_id, [:sales_contact_metro, :id], Area.find(:all,:order => "area" ), :id, :area, :metro_id) %>

<%= f.label :neighborhood_id, ', :class => "title" %>
<%= related_collection_select(:sales_contact, :neighborhood_id, [:sales_contact_area, :id], Neighborhood.find(:all,:order => "title"), :id, :title, :area_id) %>

Notice the first select is a standard collection_select then for each related field. We make calls to the plugin. The method signature is very similar to the standard collection_select with the addition of a couple parent related arguments.
Super simple!
If you have any improvements or additions, feel free to fork the repo and send me a pull request.

Deployment Gotchas – Git And Capistrano

Im just starting to take the leap with Capistrano and so far Im loving it. It simplifies deployment task down to a single command-line call. In my book, that beats syncing the FTP, manually running migrations, etc. One issue I ran into last night though, was during my deployment, I clone my git repository to get the latest version of the application. Thats baked into Capistrano, very simple stuff. However, I kept getting an error that ‘git-index-pack‘ was not a git command.
I spent literally hours googling, reading mailing-list archives, tweaking, updating git, reinstaliing git, etc. Nothing I did made one bit of difference. Being up until 5am probably didnt help me to think clearly either. So after a few hours sleep, I started investigating again. This time it hit me. It hit me like an Amtrak train at full speed.
I host on my own dedicated servers, with that being the case, I have my CentOS servers setup to use Jail-Shell by default for all new accounts in the web group. I completely forgot to change the shell for my deployment user so of course git was failing! Doh!
As soon as I changed the default shell for that user to bash and ran ‘cap deploy:update‘ it worked like a charm!
So if you are deploying to a shared host and running into this issue, make sure to check your shell. I know there are hosts out there that will Jail-Shell you by default.

Installing Git on CentOS 5

I have made the switch from SVN to Git for all of my projects and I’m loving it. So I decided as part of my deployment process, I would install Git on my server and use Git to fetch the release code to the deployment folder. This is alot like I did with Subversion, using svn export to push a tagged release to the server. Installing Git was quite simple even though there is no package for CentOS 5 yet. Here is the process I used to set it up.

The following packages are dependancies for Git, so make sure they are installed.

$: yum install zlib-devel
$: yum install openssl-devel
$: yum install perl
$: yum install cpio
$: yum install expat-devel
$: yum install gettext-devel

Next, if you do not already have Curl installed, follow the next step to get it up and running.

$: wget http://curl.haxx.se/download/curl-7.18.0.tar.gz
$: tar xzvf curl-7.18.0.tar.gz
$: cd curl-7.18.0
$: ./configure
$: make
$: make install

Next, make sure /usr/local/lib is in your ld.so.conf, this is required for git-http-push to correctly link up to the Curl version you are installing.

$: vi /etc/ld.so.conf

(Insert the following)

/usr/local/lib

Save the file, then run:

$: ldconfig

Now download and install Git

$: wget http://www.codemonkey.org.uk/projects/git-snapshots/git/git-latest.tar.gz
$: tar xzvf git-latest.tar.gz
$: cd git-{date}
$: autoconf
$: ./configure --with-curl=/usr/local
$: make
$: make install

Thats all there is to it! Simple enough. I will post a follow up on how I actually deploy using Git in the near future.

Im Going To Flex Camp Miami

Yes, Im going to brave the 95 for some 300+ miles headed south to Coral Gables for Flex Camp Miami. Why not? Its a full day of Flex goodness for a measly $30 and that includes lunch! Seriously though, Brian Rinaldi is known for putting on killer Flex Camps. The speaker line-up is top notch so you know your going to learn something. I think Im most excited that Laura Arguello from ASFusion is going to be there presenting about her Flex framework entitled Mate. I have looked at a few other Flex frameworks in the past and I wasnt really keen on any of them, however, from what I have seen of Mate, Im seriously impressed!
So c’mon! If you are in Florida and even remotely interested in Flex, you owe it to yourself to register and take a short trip to Miami. On that note, if you are interested in carpooling, let me know. I will be leaving from Jacksonville and have room for 4 people. I drive a 2007 Nissan Titan 4-door so there is plenty of room. I even have DVD players in the head rests so the guys in the backseat can watch movies on the way down! ha ha!

Skweegee Has Moved To GitHub

I have decided to take the plunge headfirst with git. Im really digging the distributed nature of it and the fact that I can make small commits to my local repo, then squash them into one atomic commit and push that to the master. It seems to really fit my workflow better.
Due to this switch, I have moved the Skweegee SVN repository over to GitHub. Importing it from SVN wasnt trivial as the GitHub importer failed constantly without telling me why. So I found svn2git and installed the gem. This made it pretty simple and it kept all of the history.
The Skweegee repo can be found here
The SVN repo is still up for now but will not be committed to unless I can find a way to mirror my git commits back to SVN automatically. If I cant, I will pull the SVN repo down later this month.

ColdBox Training in Southern California

ColdBox Platform Official Training Seminars Announces Early-Bird Special Pricing for March 14-15 2008 CBOX 101- Core ColdBox Seminar in Ontario, California.  Welcome to where it’s sunny and warm!

ColdBox Platform Official Training Seminars today announces an Early-Bird Special discount registration price of just $895 for our March 14-15th, 2009 ColdBox Platform 101 seminar to be held in Ontario, California.  Registrants can take advantage of this early bird savings over the full seminar price of $1,100 by completing their registration before 5PM (Pacific Daylight Time) on February 8th, 2009. (Discounts available for groups of 5 or more)

The ColdBox Platform 101 Seminar is a 2-Day Introduction to ColdBox and ColdBox Platform Application Development providing 16+ hours of intense, hands-on training with ColdBox author Luis Majano in an intimate setting with only 15-20 seats available.  The skills learned in this seminar can be immediately applied to a developer’s daily tasks.

For more information on ColdBox Platform Training or to register, please visit the ColdBox Training web site.

Forms With Nested Resources In Rails

This post is more of an informational post for myself in case I forget this later on. I have been working on a client project thats using Ruby on Rails and I was having a little trouble getting my head around nested resources and the routing that goes along with that. Once I had the routing figured out, the next thing to address was handling my forms. Now, as with all things in programming, there is more than one way to “skin a cat” when it comes to this in Rails but for the most part, this is from what I can tell, the preferred method and I like it better than the alternative.
To start off with, nested resources is simply a way to have much cleaner urls without having to append a query string to the url. So you can have something like:

mysite.com/category/1/products/3

instead of:


mysite.com/category/1?product=3

Of course the nice URL’s are not the only reason for doing this. When it comes to handling forms, Rails can handle all of the associations for you reducing the amount of code you are writing.
So, borrowing form the earlier example of products and categories, we can easily setup our form to handle the relationship by modifying our form_for arguments in the form like this:
form_for [@category, @product] do |f|
Now we no longer have to manually add a hidden field containing the category id.
In our controller, several methods are affected by this change, but lucky for us the changes are simple.
First, lets look at the new method which sets up the blank form:


def new
@category = Category.find_by_id(params[:user_id])
@product = @category.build_product
respond_to do |format|
format.html
end
end

Since we are referencing the product through the category, its best to use the category itself to build the new product object. This allows the previous change that we made to the form to work. Now lets have a look at the create method that will actually save our form:


def create
@category = Category.find_by_id(params[:user_id])
@product = @category.build_product(params[:product])
respond_to do |format|
if @product.save
flash[:notice] = 'Product was successfully created.'
format.html { redirect_to edit_category_product_path(@category,@product) }
else
format.html { render :action => "new" }
end
end
end

Notice again that we are building the product object through the category association and passing the product data to it from the form to populate it. Thats pretty much all of the magic sauce. There is one other thing to notice in that method that warrants attention. Notice the redirect_to method? Normally that would look something like this:


redirect_to edit_product_path(@product)

But since we are accessing products as a nested resource of categories, we simply add in the category part to the method and supply the category instance variable to the argument list. This will ensure we are redirected to the proper url.
Now I know this is a dumbed down version of this, but hey, like I said earlier, its more for my reference later on. Hopefully someone can get something out of it as well.