Rails: DRY controllers with ResourceController

Rail's generated RESTful controllers are great for quickly building up an interface to your models, but I quickly grew tired of writing the same code for simply managing resources through REST. This was compounded once I started using Rspec to test my controllers, and found myself writing endless virtually identical tests. So, inspired by some reading, I set about DRYing up all this boilerplate code and built a ResourceController class.

ResourceController extends the normal ActionController, but provides a series of hooks for fetching, creating, updating and deleting resources. By default, these hook methods are called automatically by the default REST actions (index, show, new, create, edit, update, and destroy). This means creating a standard controller is as simple as:

# app/controllers/apples_controller.rb
class ApplesController < ResourceController
end

The hook methods are protected, so their default functionality can be overridden:

fetch_resources - called before index fetch_resource - called before show, edit, update, destroy create_resource - called before new save_resource - called by create, update destroy_resource - called by delete

ResourceController infers the name of the resource it is managing from the name of the subclass, so an ApplesController will manage Apple models. Where appropriate, the name will be pluralized, so in this example, the index action will automatically generate an instance variable array called @apples.

The code comes with RSpec controller examples.

Caveats Currently, ResourceController cannot handle resources that are nested.

Installation and Use Assuming a new Rails project with a users model:

rails demo
cd demo
script/generate rspec
script/generate model user email_address:string password_hash:string password_salt:string active:boolean
rake db:migrate

ResourceController depends on RESTful routes, so add an entry to your config/routes.rb:

# config/routes.rb
# ...
map.resources :users
# ...

Next, in your project folder, generate a controller in the normal way

script/generate rspec_controller users index show new create edit update destroy

Modify the generated controller to extend ResourceController:

# app/controllers/users_controller.rb
class UsersController < ResourceController
  # Remove any generated stub methods
end

Copy the ResourceController files into your project:

cp resource_controller.rb {app_path}/lib/
cp resource_controller_spec.rb {app_path}/spec/controllers/

Next, modify the UsersController's spec file, removing RSpec's auto-generated examples:

# spec/controllers/users_controller_spec.rb
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
require File.expand_path(File.dirname(__FILE__) + '/resource_controller_spec') # Be sure to add this line

describe UsersController do
  it_should_behave_like "a resource controller"
end

You can now check that the controller works as expected by running:

rake spec:controllers

Please feel free to make use of and modify this code as you wish; if you do improve it, please leave a comment below or email me and I'll update here. I'll also post the code on either Launchpad or github. I'm preferring bazaar's simplicity to git at the moment.

Resources resource_controller.tar.gz resource_controller_demo.tar.gz

« Previous Post

avatar

Chris Blunt

Plymouth Software

Devon, England

twitter.com/cblunt

github.com/cblunt

 

facebook.com/cbluntuk

flickr.com/photos/cblunt