In troubling times, the power of humanity is through community. Showing up for each other.
This application is my attempt to foster that sense of togetherness through a useful place to connect those in need with those who want to volunteer.
As I finished up my lessons on using the Sinatra library to make a web application (which I thoroughly enjoyed), I once again was faced with the challenge: what can I build with these new skills that could be helpful to someone?
I thought about my first project, the CoVID-19 Tracking CLI App, that I made to give users a quick and easy way to be presented with summary statistics about COVID. I also thought about what I was doing at the time: 3 times per week, my father and I would meet at a senior center, pick up food, and go on a delivery route to pass out the food to senior citizens in North Philadelphia who couldn’t make it out to grocery stores.
On one of these delivery days, a woman asked us if her neighbor could get in touch with the Philly CARES team and have her sign up as well. At that moment, I realized that it would be very convenient for community members (and volunteers!) to have an application where they could track their delivery requests/meet their community members. Thus, the CoVID Community Food Bank was born.
Building interaction with the database: the models
When considering the models to build for this application, I knew right off the bat that I wanted two different user groups: community members and volunteers. What connects these two groups, I thought?
Simple: the delivery that the volunteer brings to the member.
A member can have many delivery requests (assuming that this CoVID quarantine scenario goes on for a while), and a volunteer can have many deliveries (because I thought about my own experience of passing out food - it’s much better to get in your car once and knock out a string of deliveries in a row). Also, a member has many volunteers that they interact with through their deliveries, and a volunteer has many members that they interact with through their deliveries.
Amazing! The has many through relationship worked because of the way I related my user models (Member and Volunteer) to the Delivery model, but it also passed the real-world litmus test! See a screenshot of my table relations below (or view the diagram on draw.io)
In order to keep my data clean, I used ActiveRecord Validations to only save objects to the database if certain criteria were met (namely, that all of the mandatory fields in creating new/editing profiles or delivery requests were filled out). This prevented bad data from persisting to my database, which is a must-have for any web application.
Additionally, I used ActiveRecord’s has_secure_password macro along with BCrypt to take users’ passwords and store them in a salted, hashed version, thereby preventing handling user’s passwords directly or in plain-text form (a very big security risk).
The logic of the application: the controllers
I used a controller for each model - so I had a DeliveryController
, MemberController
, and VolunteerController
. I followed RESTful naming conventions for the routes I built as best as I could, but there were some exceptions:
- A
Delivery
object belongs to both aMember
and aVolunteer
. Thus, I wanted to have unique routes for amember
editing the delivery details of a delivery and avolunteer
editing the status of the delivery. So, both the/deliveries/:delivery_id/edit
(members editing delivery details) and/deliveries/:delivery_id/volunteer
(volunteers editing status details of the delivery) routes exist! - The forms present in both
/deliveries/:delivery_id/edit
and/deliveries/:delivery_id/volunteer
submit a PATCH request to/deliveries/:delivery_id
. This is where I needed to use details stored in the session hash in order to determine whether a user is a member or a volunteer.
Additionally, in the MemberController and VolunteerController classes, I built a helper method designed to check to see if the username already exists in the database (for either the members table or the volunteers table):
def username_already_taken?(username)
!!Member.find_by(username: username)
end
When implementing this logic for a member or volunteer editing their profiles, I realized that the username of that current user would already be taken (by that user) when trying to edit their own profile and not changing the username, so I had to do an extra to check to allow a user to use the same username if the current user’s username is the “duplicate” username.
Quick note: this is what the logic looked like when a member/volunteer signed up or logged in. You can see here that I created a new key called :user_type
and stored that in the session hash to know whether the current user was a member or a volunteer.
if @member.save
session[:user_type] = "member"
session[:user_id] = @member.id
session[:message] = "Successfully created member profile - welcome, #{@member.name}!"
redirect "/deliveries"
Putting it on display: the views
I kept things pretty simple with the layouts that I used, but did have fun implementing my own “status message” feature that appears at various points in the user experience with this application.
<% if session[:message] %>
<div class="status_message" style="text-align:center;">
<br>
<%= session[:message] %>
<br><br>
<% session.delete(:message) %>
</div>
<% end %>
Essentially, if there was a key in the session hash of :message
, I display that message on the top of the page, and immediately delete the message key from the hash. Thus, if a user immediately reloads the page after seeing one of these messages, it will disappear, because on the second page load, there does not exist a key :message
in the session hash.
I am not the most design-inclined developer, so was very thankful to find Water.css, a very easy and intuitive external stylesheet that I included in my layout.erb
file to allow the application to have a clean feel. Check it out!