1. Cleaner controllers with has_scope
has_scope gem: github.com/plataformatec/has_scope
Source code: github.com/code-snack/001-has_scope
In this episode I’m gonna show you a gem that I find quite useful to clean up controllers and not many developers know about. It’s the
To illustrate its purpose, I have here a simple Rails app with a products page. In this page the user can search products by name, price and also navigate through pages using pagination.
Now I’m gonna show you the code in the controller.
Here you can see we check for each different filter that may be applied and also apply pagination after that. This is not ideal, controllers should be thin because their code is not reusable and fat controllers are hard to maintain.
Let’s remove all the code responsible for building conditions from the controller and move it to the Product model, where it should be.
Notice that for the price filter I’m creating a single scope. This is to illustrate a different scenario later on. Keep in mind that this is a contrived example and I would keep these filters in separate scopes in a real application.
Cool, now our controller is a bit cleaner and we can start using the gem. First, let’s add it to our Gemfile and run
bundle to install it. Remember to restart your Rails server after this.
With the gem installed, we can reduce the code in this controller even further.
The gem’s purpose is to apply this kind scopes for us. To do that, we simply need to call
has_scope and give it some configuration options. The first scope is the simpler one because we want the
search parameter to be mapped to the
search scope. Therefore, all we need to do is to add
has_scope :search , call
apply_scopes in the action and get rid of the manual
Keep in mind that the gem will only apply the filter if the parameter is present, which is what we want. If the parameter is missing, the scope will not be called.
Now we have the price filter, which is a bit more complicated. The
price parameter is a Hash, but the scope takes two arguments instead. To handle this, we need to use
type: :hash, using: [:min, :max]. The gem will use the keys in the array to get those values out of the
price parameter and pass them down to the scope in that order.
Also, the parameter we’re getting is called
price, but the scope is called
by_price. To tackle that,
has_scope allows us to customize the parameter name with the
Now we can just drop the price filter calls from the action.
We have one more scope to get rid of. This is an easy one. The only difference here is that we want the
page scope applied all the time, regardless of
the parameter being there or not.
To achieve that, we just add
has_scope :page, default: 1. When you give
has_scope a default value, it will always applied.
Then we can remove the
page call and it's done.
So here you have it: a cleaner controller and reusable scopes. The gem provides a few more features I haven't covered in this episode. Make sure you check out their docs if you're interested.
Thank you for watching and I hope you enjoyed this snack!