So back before this Covid-19 Pandemic took over the world, I was very much into traveling. I loved going to new places and even looking going online to look for new places to go to. Whenever I got on social media or even just the internet, I would see a picture of a new place and instantly in my mind I knew that I wanted to go there. But with the lack of time, money, friends, or even now the pandemic, whatever it was, sometimes I just couldn't go that same moment. So I would save the picture on my phone and I would tell myself, "Someday I'll go". And that was the end of it. By the next day I had already forgotten about that picture that was saved onto my phone.
Insert My Endeavors. This app that I built specializes in keeping track of all the endeavors you want to partake in, with the added feature of also being able to log the endeavors you have already partook in. I wanted one singular place to store all of my adventures, my journeys, one singular place to store all of My Endeavors.
To make my app unique, I wanted to keep the endeavors accessible only to those who created them. So with this in mind, this project came down to having only two models. A user class with its own table for the database, and an Endeavor class also with its own table for the database. With these two, I was able to save each new User that has signed up into the User class with their own attributes and same with the endeavors.
With these two models, it was fairly simple to store and access all the information a user needed to make an endeavors post. Essentially an endeavor was created and saved with a title, description, a link to a picture, a caption for the picture, a user's id to associate it to a user, and also whether or not it was already completed. Here is the contents of the schema file to show you how the information was set up and stored.
create_table "endeavors", force: :cascade do |t|
t.text "title"
t.text "description"
t.integer "user_id"
t.text "pic"
t.text "pic_caption"
t.text "status"
end
With those attributes for each endeavor, we were able to collect all the information out of the user, store that, and later use that information to show it back to the user whenever they wanted it.
Following the CRUD lay out (C-reate, R-ead, U-pdate, D-elete) for the routes, I made the Create route ask for these specific attributes to link it to the endeavor model. Then it would save into the database, acquiring an id, and storing that into the User who made it. With the Read routes, I just showed it back onto the page by getting a specific id and looking for the specific model using that id. That specific id is passed in to the .erb
file by getting it from the params
in the first place, and then storing the corresponding model into an instance variable for the .erb
to deal with.
Now onto the User model. The user model was much simpler but at the same time more complicated. Each user had only two attributes and those were the username
and the password
attributes. Here is the schema for that model.
create_table "users", force: :cascade do |t|
t.text "username"
t.text "password_digest"
end
As you can see from the schema, the column for the password is actually called password_digest
. This is because of the gem BCrypt
that we used to help encrypt our password. Long story short, not only does Bcrypt encrypt our password, but they also add a random string into the mix in order to 'salt' the password and that salted and encrypted password is what we store in our database (More on salting and encrypting passwords here).
One of the issues that came up for me was how to keep track of only allowing the user who created the endeavor to see, edit, and delete the endeavor.
I got to doing this by flipping the question around for the answer. It wasn't 'How do I show the endeavor they made to the user?' but it was actually 'How do I show the user the endeavors they made?'. If that doesn't help you understand the problem, that's okay because it helped me, and alot at that actually.
The actual fix that I used for this was to validate the user everytime they tried to access a page with an endeavor on it. By validating, I mean that I would check that not only was a user logged in through the sessions cookie, but also if the user in the params
matched the user in the session
. This was an issue because if I only checked to see if a user was logged in but not who was logged in, a user could enter any other user's home page by replacing their username with their username in the URL. Therefore I always checked if the user in the URL (or in the params
) matched the user in the session cookie by comparing their User ID's that was assigned to them by the database when they were made and saved.
To do this more efficiently and as some would call it, more 'dry', I made a method in a whole new class named 'Helper'. Now we have a new model called 'Helper" but this model was did not have its own table, it's sole purpose was to help the .erb
files with logic. In my 'Helper' model, I have three methods that help us deal with logic when it comes to the routes and the views. The methods were all class methods and they were,
class Helper
def self.current_user(session_hash)
User.find(session_hash[:id])
end
def self.is_logged_in?(session_hash)
!!session_hash[:id]
end
def self.rightuser?(params,session)
if self.is_logged_in?(session)
User.find_by(username: params[:username]) == User.find(session[:id])
end
end
end
As you can probably deduct from the method names,
#Helper.current_user(session_hash)
: takes in the session hash of the browser and returns the current user by using the ID stored in the session to find the user.
#Helper.is_logged_in?(session_hash)
: also takes in the session hash but this method just returns a boolean. True if the a user is logged in and false if no user is logged in. Pay attention to the fact that it's true regardless of the fact of who is actually logged in.
#Helper.rightuser?(params,session)
: this is how I managed to solve the issue where any user that was logged in could view another users account and information just by being logged in. This method takes in the params hash that is provided by the URL and also the session hash. With these two hashes, the method takes both the ID values and makes sure they match to ensure that the user who is trying to access the URL is in fact the owner of the info on the URL.
For this project since we were using Sinatra, that was one of my biggest issue. To validate that the user was in fact the owner of the endeavor. But with the help of a helper method from a 'Helper' model, I was able to do it.
Here is the link to the repository on my github, My Endeavors.
Top comments (0)