During pre REST days, my controllers were so ugly and fat. with too many actions to count and do a lot more than the controller meant to do in an MVC framework. Then came RESTful controller which was really, really a nice way to help application designer to organize and "design" the application. RESTful controllers concept is revolutionary in that it simplified or abstract away the complexity of the application plumbing and repetitive routines and made my poor little mind focus on the application design instead.
And then came resources scaffold
The scaffold generator now do us all the work to have a nice RESTful controller that can CRUD your model and responds to HTML, XML, JS. But I need to confess that I've never liked generators. Heck, it is the only reason why I don't use IDEs. They pollute my code with lines I didn't write, and that's plain ugly. It is lovely to have your controller speak REST in XML, but it was not a good feeling when I see all those respond_to lines scattered all over my controllers at the time when my application is not yet designed to be used as such.
And what made it really worse is that most of the time the generated controller needed almost 80% editing, to scope my models I use in the controllers, like @site.pages, @user.posts, and to paginate results, set model attributes assignments..etc. So it was obvious that generators wouldn't cut it. We needed some help to abstract all the repetitive codes in our controller and find a solution for problems like relations aware controller that respect scoping, can deal with nested resource and namespaces. I am sure that any rails developer (or any MVC web developer) knows the kind of problem I am talking about.
Resource Controller plugin
More than a year ago I found some Rails plugins to do just that. But I was too skeptical to use them in production. Time passed and there are now a really good, tested and tried solution for the kind of problem I am talking about. It is called "Resource Controller".
It is a fine plugin written by James Golick. Thank you James. The other plugins like resource_this and make_resourceful were steps in the right direction but Resource_Controller is the way to go.

After months of using it I feel pretty confident with resource controller. And as all tools that abstract away repetitive noise it allowed me to focus more on my application design and actually I developed more productive patterns in developing my application. Patterns that changed the face of my rails controllers beyond recognition. Here is a minimal Resource Controller.
1 | class UsersController < ResourceController::Base |
Suddenly, I had this ActiveRecord thrill again, but with Resource Controller this time. I bet this is the skinniest controller one could dream about. Come on Rails core team, when will we see Jamis' Resource controller merged!
Well the next thing to do is playing around and try to see how to solve the problems I mentioned above. But only after introducing my newest love, "Named scope". which prove very useful in DRYing my controller even more.
Named Scopes
I always thought that the best feature in ActiveRecord is scoping through associations. Like, '@user.posts.find(:all, :limit => 10)'. This feature alone is enough to use ActiveRecord. The next best ever feature in ActiveRecord is named_scope. I couldn't recommend enough using named_scope. Just use it. It is core since Rails 2.1.
Coupling named_scope with Resource Controllers really open new application design territory. Imagine the following requirement.
- One pages controller that can be used to list all the pages in a portal
- It could be used nested under users resource, so it should scope pages to user.
- It should handle posts pending for approvals.
- It should fetch the next and previous pages according to the context or the scope it works within, be it portal pages, users pages, or user's pending pages or portal's pending pages.
Let's build the controller
First we do the routes, a simple nested routes and then the controller.
1 | map.resources :users do |user| |
1 | class PagesController < ResourceController::Base |
The set_scope method is interesting because I stack up the scope conditionally according to the context within which the controller is running. I mean if the controller is nested in users resource it will find params[:user_id] and the parent_object return a user object and then the @scope of users pages will be added. Which is a named scope in this case "Page.by_user()". I could have use parent_object.pages which is the natural association scoping of ActiveRecord I got with the "has_many :pages" but because my application serves several portals, and @portal got assigned according to subdomains on the application controller, I don't won't to loose the @portal scope '@portal.pages' so it was either make a named_scope for portals or users.
Notice here that @scope.find(:all, :conditions => {:user_id => parent_object.id}) will not do us any good because you will not be able then to narrow down those scopes by stacking them. But sure an inline or anonymous named_scope will suffice instead.
Now I need to make the controller serve pages pending for approvals and fetch the next and previous pages properly according to the scope. Some routes need to be set first. Changing the scope is only a matter of adding a new line to the set_scope method according to the passed params[], so all I need to do is passing non blank params[:pending]. Of course passing it hanged on the url like '/user/hany/pages?pending=1' is not an option. I need it to be clean like '/user/hany/pending_pages' and to say the truth it is not about cleanliness, it is about setting proper url automagically as I will show you later.
1 | map.resources :users do |user| |
Here I added pending_pages resources and pass explicitly the controller name because I will use my same pages controller. I made the resources properly set the pending parameter to true or false. I don't believe Rails 2.2 shallow routes option will spare me the pages resources repetitions. May be we need another way to make the parent resources optional?! but anyways, now that we have the routes properly set, let's see the controller.
1 | class PagesController < ResourceController::Base |
Here I added one line to set_scope to add the pending scope if the params[:pending] was true. But for a change I didn't check for params[:pending], instead I pass it to the named_scope which will check internally if it set or not and do the scope accordingly. Actually I did this in my real application to not be forced to make another not_pending named_scope. Because in real life you will need not to publish unpublished pages. errr.. ok, i mean the pending scope is unlike users scope, which is like, "if parent user is not set just show all the pages anyway", this will not work here, we need it to be, "if pending status is false just list only the published pages". And now it's a good chance to demonstrate how to pass arguments to named_scope.
1 | class Page < ActiveRecord::Base |
It is a very healthy practice to provide a next and previous item links in your show item page. For many obvious reasons, for one is to spare the user going back to pages list which will stress your server as well. Just give her the links or previous and next page. But you have to prepare the links first in the show action. In Resource controller actions are abstracted so you don't have to do the repetitive code each time, just do what is specific to each controller action. In this case I need the show action to fetch the next and previous page according to the scope within which the pages controller works. And do that just before rendering the template or to be precise just before responding.
1 | class PagesController < ResourceController::Base |
1 | class Page < ActiveRecord::Base |
/pages/:idThe surprise is that Resource Controller will know exactly which route to use according to the context. Actually it does it using Urligence url helper. The helper themselves are too easy. You will have object_url and collection_url, that's it. In Rails resources native tongue: object_url to refer to any member action, and collection_url to refer to any collection action. And of course the HTTP verb or the request method will decide which action to address, like, post to collection_url to create and put to object_url to upate and such.
/user/:user_id/pages/:id
/user/:user_id/pending_pages/:id
1 | <% form_for :page, @object, :url => object_url, :html => {:method => :put} do |f| %> |
What about controlling the flow of the application. What if you need to change the default flow of the resource controller and make the "delete" action redirect to the next item show page if it exists and to the pages index if there is no next page.
1 | delete do |
There are lot more in ResourceController, it lets you customize flash message for success and failure, add responds to methods, respect namespaces automagically and more.
Anyway, that's it regarding how ResourceController and Named Scopes has changed the way I write Rails ActionControllers. for more information refer to the following links:
