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.
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
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.