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. π