Hey πβ¨
I've been reading through a book called Growing Rails Applications in practice and I decided to make a summary to get a better understanding of every chapter. Maybe it also works for you!
Normally, the developer does not struggle to learn about what a View or a Model is. But what about the controllers?
What are the major problems of dealing with them?
- It's often hard to decide if the logic goes into the controller or in the model.
- Normally, sharing code between the model and the screen requires too much code.
- Tested controller's code is hard to test and experiment with because of the parameters, sessions, requests etc they need.
- Without a design guide to follow, it is hard to find two controllers that look alike, making it a pain to recognize who every controller work.
We cannot get rid of them, but, by following simple guidelines, we can extract business logic out of the controllers, and take them to a better place.
Consistent controller design
It is important to have a standardized controller design. A developer who has to learn how to manage a new system every time he/she edits a controller file won't be happy. A lot of time will be spent here by doing this.
By reducing the variability in our controllers, we will improve our fellow developers' mental health. Wouldn't it be awesome that, by having a default design, you could focus exactly on what you want to do, and in the other parts of the system?
Also, the development speed of new controllers would be improved. You would only decide to use CRUD, and move to the parts you really care about.
Normalizing user interactions
How could you come up with a default controller design when your site has a lot of different user interactions? A good way to manage this would be to reduce every interaction to a CRUD controller, even if you feel that it's not necessarily a typical CRUD interface (at first).
Every interaction can be modelled as a CRUD. You may have a controller that manages a subscription. Why not DESTROY a subscription instead of cancelling it? Or CREATING it instead of activating a new subscription?
By normalizing every user interaction to a CRUD interaction, we can design a beautiful controller layout and reuse it again and again with little changes.
A better controller implementation
How should a controller truly be? We have seen many unloved controllers during our careers.
Let me give you some hints.
- They should be short, DRY and easy to read.
- Controllers should provide the minimum amount of glue code as possible. (No intermediary code between a request and a model)
- Always follow the standard design, unless there is a truly good reason.
A good way to do this is by creating an standart implementation of a controller that CRUDS an ActiveRecord class:
class NotesController < ApplicationController def index load_notes end def show load_note end def new build_note end def create build_note save_note or render 'new' end def edit load_note build_note end def update load_note build_note save_note or render 'edit' end def destroy load_note @note.destroy redirect_to notes_path end private # The controller actions are delegating most of their work to helper methods like load_note or build_note. This will make our code DRY and it will be easier to change the behaviour of multiple actions by just editing our helper method. def load_notes @notes ||= note_scope.to_a end def load_note @note ||= note_scope.find(params[:id]) end def build_note @note ||= note_scope.build @note.attributes = note_params end def save_note if @note.save redirect_to @note end end def note_params note_params = params[:note] note_params ? note_params.permit(:title, :text, :published) : {} end # note_params will allow us to only give access to those parameters we want the user to edit # authorization does not belong in a model, as no users should interact with data in there. def note_scope Note.all end # This note_scope method is used by every CRUD action. # It loads a Note with a given ID, or to load the list of all notes at the index method. # By having this guard access to the Model, we have a central place that tells exactly which records the controller can show. # We could limit this, for example, to only display specific user notes, just by changing the query we make to the model. end
Why have controllers at all?
By doing this, we are creating a simple and consistent controller design that pushes a lot of code into the model. So, why have controllers?
There are several responsibilities that belong in a controller.
- Security (authentication, authorization)
- Parsing and white-listing parameters
- Loading or instantiating the model
- Deciding which view to render
Remember that a controller never does the heavy lifting. They should contain as less amount of glue code as possible. π β¨
You can follow me so you are tuned to whenever I post something through my Twitter account, hope you liked it!
Top comments (3)
Nice post
Thanks for writing this post! I came across this book recently, but I had my doubts because it seems it was written a few years ago. Would you say it is still relevant for Rails 6 applications?
Glad you liked it β¨ I'd say that yes, it is relevant, as it has general knowledge that's useful even for other frameworks. π