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