While some logic in Rails views is inevitable, recently I have been trying to move any unnecessary logic in the view to the model or controller–especially if it’s a database query. This problem came into view while I was coding a Friendship “Accept” button in a Rails Facebook clone. I had it setup so that users could accept or decline friend requests from the Notifications#index page. The problem was I had to locate the respective friendship object from within Notifications#index. This required a database query. Once I wrote the correct database query in the view to find the Friendship object associated with a notification, I started thinking of ways to remove it from the view. Below is how I approached the problem.
Every notification references a
sender and a
receiver, both belonging to the User class. Similarly, a Friendship object is initiated with a
sender and a
receiver. If a user has many friend requests on their notifications page, it is necessary to determine which specific friend request is being accepted or declined when the receiver clicks “Accept” or “Decline”, respectively.
Therefore, when rendering the notifications collection, we can find the soon-to-be-accepted or soon-to-be-declined Friendship with
notification.receiver. The “Accept” button is as follows:
Initial “Accept” Button
We can eliminate the need for the local variable by creating a hash of all friend requests sent to the current user, mapped to the
sender_id. The current user will always be the receiver in this situation and to find the Friendship request sent to the current user, we only need the
sender_id. I thought it made sense to make this method an instance method on User but there are probably better ways to do this. Let me know in the comments! Here are the relevant models and associations.
User, Friendship, Notification
And now the method that maps a sender id to a Friendship object:
received_pending_requests.count == 1 at the moment. A user with an id of 3 has sent
current_user a request.
The good thing about doing it this way is that the controller can now request this information from the User model prior to rendering the Notifications#index view:
Finally, we can return to our “Accept” button which has access to the current notification being rendered.
Note the absence of the
friendship local variable:
Final “Accept” Button
Another added benefit of this approach is improved readability. Which friendship are we updating? The one the sender of a notification requested –>
friendship_path(@friendships[notification.sender.id]). This is arguably more expressive than
friendship_path(friendship), because it provides the context in which we are searching for a Friendship object.
Balancing responsibilities between the Model, View, and Controller is sometimes challenging, but making your way towards a solution piece by piece can be very educational and rewarding.
This was my first blog post. Thank you so much for reading. Having finished it I realize the benefit it has in regards to understanding. I hope to write more posts in the near future.