This post describes my experience organizing Rails controllers using only REST actions, an approach popularized by the mighty DHH and his cronies.
This article was published on 06/05/2019.
As demoed by DHH here and explained in depth here, organzing your Rails controllers with REST only actions, and then nesting sub controllers, you can make your application more consistent and approachable.
Let’s say I have posts
resource with your usual REST actions. Now I want to allow users to “correct” a post. One alternative is adding a correction
route:
class PostsController < ApplicationController
def correct
# ...
end
end
And in routes.rb
, you would add the extra route. Something like:
resources :posts do
get 'correct', controller: 'posts/correct'
end
The pattern DHH and friends describe encourages you to make a new controller for any non standard REST method. So we make a new file in app/controllers/posts/corrections_controller.rb
:
module Posts
class CorrectionsController < ApplicationController
def index
end
end
end
Now, since it is a “nested” resource, we naturally want access to the parent resource (eg, the post
). The simple way would be to just do @post = Post.find(params[:post_id])
. However, there is a convention that comes along with this nested controllers approach to create a concern to set the post for us. In app/controllers/concerns/post_scoped.rb
add:
module PostScoped
extend ActiveSupport::Concern
included do
before_action :set_post
end
private
def set_post
@post = Post.find(params[:post_id])
end
end
And in posts/corrections_controller.rb
:
module Posts
class CorrectionsController < ApplicationController
include PostScoped
def index
end
end
end
Now all the methods in the CorrectionsController
have access to @post
.
Lastly, routes.rb
looks like this:
Rails.application.routes.draw do
resources :posts do
scope module: 'posts' do
resources :corrections
end
end
end
I am excited to try this pattern out. It seems clean and scalable.