Rader on Rails

Dispatches from my web development journey.

Skinny Out Your Controllers With Inherited Resources

After building Rails apps for some time, you eventually find yourself writing a lot of the same RESTful controller code over and over again. Some resources just require simple CRUD operations that follow similar patterns across apps. I’ve found myself copying and pasting code for CRUD operations from previous apps into new ones, making minor adjustments, so that I could get things up and running quickly. Thankfully though, there’s a better way: the inherited resources gem.

Inherited resources handles the common CRUD actions you’ve written a hundred times, and it’s quite easy to customize to your needs.

To get started, just add the inherited resources gem to your gemfile and run bundle install.

Now let’s say we’ve got a Reddit-like forum application that has users, and users can make posts and comment on posts. Using inherited resources, you can set up your posts controller like this:

1
2
3
4
5
6
7
8
class PostsController < InheritedResources::Base

  private

    def post_params
      params.require(:post).permit(:title, :content)
    end
end

That’s it. Provided you declared resources :posts in your routes file, the seven RESTful controller actions (index, new, create, show, edit, update and delete) are defined behind the scenes by inheriting from InheritedResources::Base. Pretty nifty – a controller that would normally be at least 80 lines of code is reduced to just six!

Inherited resources also plays nicely with strong parameters. After you define your permitted parameters, they will automatically be used wherever params would normally be called.

Working with associations

We need to customize our posts controller a little bit to ensure that each post created is associated with a specific user. Inherited resources provides simple ways to make these kind of customizations and maintain much cleaner controllers.

To associate all posts with the logged-in user, inherited resources provides a method begin_of_association_chain that handles this nicely. Just add it as a protected method to the bottom of your posts controller:

1
2
3
4
5
6
7
8
9
class PostsController < InheritedResources::Base
  # ...

  protected

    def begin_of_association_chain
      current_user
    end
end

I defined a current_user helper method in my Application Controller that returns the @current_user stored in the session. Now whenever I make a post, it’s automatically associated with that user. Magic.

For comments, we do something a little different. Let’s say we want user’s to be able to add comments right on the posts show page, and we’ll list all comments right underneath the post.

In my routes, I’ve nested comments under posts.

1
2
3
resources :posts do
  resources :comments, only: [:create, :destroy]
end

In my posts controller, I need to make sure I instantiate a comment object for the form on the posts show page. I accomplish this by simply adding this on top of the default post show action:

1
2
3
4
5
6
7
8
class PostsController < InheritedResources::Base
  def show
    @comment = resource.comments.build
    super
  end

  # ....
end

And then I set up my comments controller like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class CommentsController < InheritedResources::Base
  belongs_to :post

  private

    def comment_params
      params.require(:comment).permit(:content)
    end

  protected

    def begin_of_association_chain
      current_user
    end
end

Notice the belongs_to :post? Not used to seeing that in a controller. This will ensure that each comment submitted is associated with the post that the user is viewing.

We’re not done with our comments controller though. By default, after a comment is created, the create action will try to redirect to a show page for the specific comment. For our purposes though, we want to make sure that the user stays on the page of the post that he or she just commented on. This is easy to accomplish with inherited resource’s smart redirects, and it only takes adding the line actions :create right underneath belongs_to :post:

1
2
3
4
5
6
class CommentsController < InheritedResources::Base
  belongs_to :post
  actions :create

  #...
end

The line actions :create tells the comment controller to redirect to the parent resource upon creation of the comment. Now I’ve got all the behavior I want for my simple forum application in a fraction of the code required to set up these actions.

Have fun with inherited resources

These are just a few quick examples of the power of the inherited resources gem. There are many more things you can do with it, such as ensure controller actions only respond with JSON for dynamically loaded content. All of this is nicely documented over at it’s GitHub page.

I’m just getting started with this gem and I’m excited to continue learning how it can slim out my controllers so that I can focus on more important things. It definitely provided a nice respite from the monotony of creating the same RESTful actions that I’ve written too many times to count among different projects.

Comments