Let’s refactor the Rails views!
One of the bigger struggles for newer developers on Rails is refactoring the MVC. What belongs in the Model, View and Controller. It can be a tough task. I’ll focus first on what belongs in each and then how to refactor them. This will be 3 part series to help keep it concise and specific. I’ll start with the view first and provide links to the other two.
The view can easily get cluttered. It seems the easiest to put logic there since we can and since it’s dealing directly with info the user is seeing. When I was first coding I found myself putting in
if else like crazy. An recent example of mine where I could have easily done this is with a user photo and user avatar. I have a social network where a person viewing someone else’s profile shouldn’t see their photo, but their avatar if the two were not connected. Same goes with the name and anonymous username - but for now I’ll just use the photo as the example.
I have a few pages that show a user’s regular profile and anonymous profile. So that means I had a lot of
if else in quite a few views. Something like:
1 2 3 4 5 <% if user.connected? %> <img src="<%= user.photo %>" /> <% else %> <img src="<%= user.avatar %>" /> <% end %>
5 lines. Surely there’s a better way!
And there is. This kind of logic doesn’t really belong in the view. If you need to use any kind of Ruby in the view, it should be for things like:
- iterating through a list of objects
- calling an attribute on a Model object to show it’s value (i.e,
- Any kind of forms (
- Rendering views
I believe that’s the bulk of what would go in a view. Most everything else can fit elsewhere. Let’s take a look where.
For the example above, one thing we could do is write a method in the
User model. We could write it something like:
1 2 3 def show_photo connected? ? photo : avatar end
Then the view would be:
<img src="<%= user.show_photo %>" />
This does a couple great things for us. One is now the logic is much cleaner. The code in the view is one line and the code in the model is one line (2 extra if you cound
end. Also, this can be used across any view. Every single one. So now instead of 5 lines for every view, we have one line across every view and the associated model method.
Is this the best place for it? Some would argue no, others would argue yes. Die hards would say that logic that has nothing to do with the actual data does not belong in the model. That is typically what the model is for. So where else could it go?
A helper. If you did a
rails g model User to get your model, I imagine you got a
UsersHelper to go along with it. If not, go ahead and create this file inside your
helpers directory called
users_helper.rb This file can now hold most if not all the logic that is in our view. It can deal with any kind of model, but normally to keep things organized you would only put
User specific logic here.
Can we write the same method in here as we did in the
User model? No. Our
user object is not an instance of
UsersHelper, so the attributes on
user can’t be used the way we did with the above
show_photo method. What we can do, is pass in the
user object as an argument to the method.
1 2 3 def show_photo(user) user.connected? ? user.photo : user.avatar end
And then in the view
<img src="<%= show_photo(user) %>" />
This same thing can be done with any kind of
if else, which I think is the most common kind of logic that developers place in the view. Do the checking inside a helper and inside the method, return whatever needs to be seen by the user. In this particular case, I feel it’s perfectly fine to place this inside the
User model, however there are many times the logic should not go in there - perhaps because it has nothing to do with the actual model.