For our second project at Flatiron School, we had to build a content management website using Sinatra. My project idea was to create an application to keep track of a user’s collection of board games, as well as being able to view other user’s collections. This may seem silly to someone who is not an avid board gamer, however when you own over 200 games, it’s nice to be able to keep track of which games you have. I populated my database with an API from my boyfriend’s Board Game Geek account to get started, but also will allow users to create a new board game if they do not find it in the database.
Active Record Associations
In order to get this app functioning in the way I envisioned, I needed to set up a many-to-many relationship between users and board games (i.e. a user has many board games, but a board game can also be owned by many different users). I achieved this by creating a join table called user_boardgames.
The join table, user_boardgames, acts as a middle man to keep track of which users own which board games. This table is necessary because if multiple user_ids were added to the boardgames table, there would be too many columns and it would not be easy to interpret. The User class will then have many boardgames through user_boardgames and similarly the boardgame class will have many users through user_boardgames.
A requirement of this project was to give users the ability to use CRUD (create, read, update, and destroy) actions, but only for content they created. Because I got data from an API, I needed to protect these games from being edited by users. At the same time, I needed a way to associate users to the board games they created/added to the database. I achieved this by aliasing my user class.
First, I added a column in the boardgames table called “creator_id”. I let the boardgame class know that it belongs to a creator, which is actually an alias for the User class. I also had to set up the association in the User class by telling it it “has_many created_boardgames”, which belongs to the Boardgame class, and responds to the foreign key of “creator_id”. When a user creates a new boardgame, I was able to make the association in the “post ‘/boardgames’” route like so:
(where current_user is the user that is logged in at the time the board game is created).
I was able to build a private method in the boardgame_controller utilizing the creator_id. This method helped keep my code DRY (don’t repeat yourself), as well as making it more readable.
If the boardgame.creator_id != current_user.id, then no changes can be made. This means that no one can edit the board games from the API, since their creator_ids are all “nil” by default. I added this method to the “patch” and “delete” routes in the boardgame_controller to ensure that only the user who created a board game is able to make changes to it. The flash message let’s the user know why they are being redirected.
Another functionality of this app is allowing users to add and remove board games from their collection. Pry was essential at this point in order to keep track of what the params hash looked like after the user submitted the form. Each user has a “home” (i.e. show) page, which renders all the board games in their collection. On this page there is an option to add games, which will take the user to an edit page containing a list of every board game with a checkbox that is either checked or unchecked depending on whether it is included in the collection.
What’s happening here is I’m sorting all the board games in alphabetical order and then iterating over that array to add a checkbox next to each board game. In order to keep track of which board games are checked, I need the params hash to contain an array of the board game ids associated to the checked items. By setting the “name” parameter equal to “user[boardgame_ids]” and the value equal to the id of the board game, my params hash will contain an array of the ids of checked items. In the users patch route, I am then able to iterate over this array of boardgame_ids and ultimately add the game to the collection.
The “if” statement is important to prevent duplicate board games from being added to the collection.
Adding games seemed relatively straightforward to me, it was removing a game from a user’s collection that I struggled with. I wanted to take a different approach from how a user added games via the checkboxes. My thought process was that it’s probably unlikely a user is going to get rid of multiple games at once, so I decided to implement a “remove from collection” button under each game in a user’s home page.
A nested route was necessary here in order to keep track of both the user id and the board game id. The params hash then has info for both the user’s id (:id) and board game id (:bg_id), so the associated board game can be removed from the user’s boardgames array. I am using the verb “post” over “delete” since I’m not actually deleting anything from the database, just removing an association between a user and a board game.
Overall, I enjoyed this project (even though it seemed insurmountable at times). The key to success is to take it piece by piece and trust the process. A big thanks to my instructor for guiding me and helping my ideas come to life!