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
1class Author < ActiveRecord::Base
2 has_and_belongs_to_many :books
3end
4
5class Book < ActiveRecord::Base
6 has_and_belongs_to_many :authors
7end
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.
1class AuthorController < ApplicationController
2 def add_book
3 end
4
5 def remove_book
6 end
7end
This doesn't seem that harmless, but what if we wanted to add authors to a book?
1class BookController < ApplicationController
2 def add_author
3 end
4
5 def remove_author
6 end
7end
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.
1class Author < ActiveRecord::Base
2 has_many :authorships
3 has_many :books, :through => :authorships
4end
5
6class Authorship < ActiveRecord::Base
7 belongs_to :author
8 belongs_to :book
9end
10
11class Book < ActiveRecord::Base
12 has_many :authorships
13 has_many :authors, :through => :authorships
14end
Because the link has been identified, a controller can be created with the ideals of CRUD put right into place.
1class AuthorshipController < ApplicationController
2 def create
3 #Authorship.create(:author_id => params[:author_id], :book => params[:book_id])
4 end
5
6 def destroy
7 end
8end
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:
1class SomethingController < ApplicationController
2
3 # POST /something
4 def create
5 end
6
7 # GET /something/1
8 def show
9 end
10
11 # PUT /something/1
12 def update
13 end
14
15 # DELETE /people/1
16 def destroy
17 end
18
19end
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:
1class SomethingController < ApplicationController
2
3 # GET /something
4 def index
5 end
6
7 # GET /something;new
8 def new
9 end
10
11 # GET /something/1;edit
12 def edit
13 end
14
15end
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