ShiftEleven

Looking towards Rails 1.2 with CRUD

A couple of months ago, I was shown the article that DHH(David Heinemeier Hannson) wrote after he gave his presentation at RailsConf 2006. I looked at it then and kind of shrugged it off, mainly because I really didn't know what the presentation was really about.

Tonight I found video of that presentation and I got excited about CRUD(Create, Read, Update, Delete). It pointed out that I've been making some controllers a little bloated. I have coupled much functionality into one controller; when looking back, I clearly could have separated out some functionality.

Learning to love has_many :through even more with CRUD

One of the points about this is that the coupling often happens when you have models that are joined by a table, like in a HABTM(has_many_and_belongs_to) relationship. The table that joins the two other tables as an amorphous name, whose role changes depending on which side of the join table you are looking. has_many :through fixes this ambiguity as it gives a name to a new model, and thus a new domain to think about creating clean uses of CRUD. An example will make this much clearer.

The non-CRUD way

class Author < ActiveRecord::Base
  has_and_belongs_to_many :books
end

class Book < ActiveRecord::Base
  has_and_belongs_to_many :authors
end

Let's say I have a AuthorController and we want to add books to an author, or remove a book from an author, we could write the following.

class AuthorController < ApplicationController
  def add_book
  end

  def remove_book
  end
end

This doesn't seem that harmless, but what if we wanted to add authors to a book?

class BookController < ApplicationController
  def add_author
  end

  def remove_author
  end
end

This does not follow the ways of DRY(Don't Repeat Yourself), because depending on which side of the coin you are looking at, you have two different ways to accomplish pretty much the same thing.

Enter has_many :through

DHH points out that if we can identify the link between Authors and Books, there will be a new a less ambiguous and more elegant way to think about this issue. The link to identify here is Authorship. Now let's change our models some.

class Author < ActiveRecord::Base
  has_many :authorships
  has_many :books, :through => :authorships
end

class Authorship < ActiveRecord::Base
  belongs_to :author
  belongs_to :book
end

class Book < ActiveRecord::Base
  has_many :authorships
  has_many :authors, :through => :authorships
end

Because the link has been identified, a controller can be created with the ideals of CRUD put right into place.

class AuthorshipController < ApplicationController
  def create
    #Authorship.create(:author_id => params[:author_id], :book => params[:book_id])
  end

  def destroy
  end
end

Everything is right with the world again

POST, GET, PUT, DELETE

The other big thing that was mentioned was mapping CRUD functionality to HTTP(hypertext transfer protocol) method definitions: POST, GET, PUT, and DELETE respectively. The HTTP method acts as the verb for what the controller should do. Here is the basic outline, the comments are how things fit into the lovely world the the HTTP methods as verbs:

class SomethingController < ApplicationController

  # POST /something
  def create
  end

  # GET /something/1
  def show
  end

  # PUT /something/1
  def update
  end

  # DELETE /people/1
  def destroy
  end

end

Since browsers don't use PUT or DELETE, Rails has a hack for this; it's going to send hidden fields with the actions so that the mappings know how to handle everything. Those methods aren't the only ones that are apart of the CRUD; there are other ones that are common to most controllers:

class SomethingController < ApplicationController

  # GET /something
  def index
  end

  # GET /something;new
  def new
  end

  # GET /something/1;edit
  def edit
  end

end

index is responsible for listing all of the somethings, while new and edit are how users first start their journey to creating or updating something.

Clarity in this layout isn't the other reason to use this. The other reason Rails is going to be embracing REST(Representational State Transfer) ideals into the way Rails will route your URLs. More on this later when I dive deeper into it.

Because of this presentation, I'm thinking about how I'm doing my controllers. I usually don't have this type of setup, but I am definitely going to start using it. From now on, I will not be afraid do use the command script/generate controller Something index new create show edit update destroy and start to think about controllers from a CRUD perspective rather than creating more monolithic controllers, and to be ready for when Rails 1.2 comes in full effect.

Comments

comments powered by Disqus