Building a Games Guide
This blog post describes the process that I employed to build the second project requirement in Flatiron School’s software engineer program. The project was a CRUD/MVC (Create, update, delete / model, view controller) application, using Sinatra, an open source software web application library and domain-specific language written in Ruby.
The domain model that I focused on was a collection of board games. Users can create an account and login to the website. Authenticated users can add board games to the domain, describing them with multiple attributes and assigning them a type or category. Other users can “tag” the game with a descriptive word or phrase so that the collection will have become dynamic and self described by the community of users. Authenticated users can comment on games, hopefully sparking discussion.
Requirements and Goals
The major requirements of the project included:
- Use of the MVC pattern
- Use of Active Record ORM
- Have user accounts, where a user can sign up and login/out
- Have appropriate has_many and belongs_to relationships
Beyond these base requirements, I implementing additional features:
First, I included a many_to_many relationship within the domain. For this feature, I used the concept of a tag. Logged in users can “tag” a game with a descriptive word or short phrase. For example, say the game is a classic, perhaps Yahtzee, a game that has been around for years and is well known and popular. Tags like “classic”, “popular”, “dice” would apply to it and users can add them to a game. Games can have many tags and a tag can apply to many games.
Second, I provided validation for objects before saving to the database and to provide feedback to the user. Active Record provides great validation functionality that I was able to leverage in this application.
Game — The basic object of the application, which is a board game described with multiple attributes. Games can be tagged and commented on by authenticated users.
User — Those who login, create game entries, tag them and make comments.
Type — Types are types of games. Examples include “party” games, “strategy” games and “family” games. Types are primarily static and provide a way to group games. There will not be any way to edit, add or delete categories in the application.
Comment — Games can be commented on by authenticated users, a short phrase or perspective on a game from fellow users.
Tag — As described above, a tag is a descriptive word or short phrase that users can apply to a game and provide a way for the users to group the games in the collection in a more dynamic way than game types.
A game belongs_to a user and a game belongs_to a type. A game has_many comments and has_many tags. A game has_many :game_tags. The game/tag relationship is a many-to-many relationship, whereby a game has_many tags, through: game_tags
A user has_many games. Each game will belong_to a user. A user has_many tags. Each tag belongs to a user. A user has_many comments. Each comment belongs_to a user.
A type as_many games. A game belongs_to a type.
A comment belongs_to a game and a comment belongs_to a user.
A tag belongs_to a user and has_many games. The tag/game relationship is a many-to-many relationship, whereby a game has_many tags, through: game_tags, so has_many :game_tags. This will allow a tag to be associated with many games and a game to have many associated tags.
I am a visual thinker, and one of the best ways for me to understand something that I am to build is to draw it out. The best place to start is an ERD diagram that will show the objects and their relationships to one another. Here is the initial ERD I came up with.
I proceeded by making preliminary mock-ups of the forms needed to populate the objects in the domain.
Next came the time to actually create a starting point for application. I used a previous lab (FTwitter) as a starting point to get the scaffolding of folders.
Here is my Gemfile with gems for the project:
Using the shotgun gem, I was able to get a site running:
The next step was to create the tables, models and relationships described above.
In the game object, the many to many relationship was set up, so that a game can have many tags through the join table (game_tags) and the GameTag object.
Notice that I have created_at and updated_at columns on most of the tables. Conveniently, Active Record will populate these tables with appropriate values when the objects are saved. I used these timestamps later when displaying objects on the application’s home page.
Test Objects and Relationships
Now that the objects were created and being persisted to the database, the next step was to see if they related properly. The Tux gem is a great way to experiment with the objects in the domain.
First, I created a user in tux:
>> will = User.new
>> will.username = “willcarter”
>> will.email = “firstname.lastname@example.org”
>> will.password = “password”
Then, I checked that it saved to the db correctly:
Next, to test the secure password functionality. I got an instance of a “will” User object, ran the authenticate instance method with the correct “password”, and saw it display the username. When I attempted to authenticate with a wrong password, I correctly get a return of false.
Next, I populated the Types.
As for games, I added a couple (Monopoly and Clue), assigned them to the “will” user above and the Family type. Then, I confirmed that both games belong to the “will” user and the Family type.
I needed to make sure my tags/games many-to-many relationship was working correctly. So, I created two tag objects, one with the name, “classic” and one with the name “popular”. I assigned them both to my monopoly and clue games.
The tags showed correctly from the game perspective:
The games showed correctly from the tag perspective:
Lastly, I created a comment, assigned it to monopoly game, and checked that it is correctly associated:
Satisfied with testing the objects and how they relate, the next step is to build views and forms in order to populate the objects via the web application.
There are a number of views that work in conjunction with paired controllers in the application which allow creation, updating and deleting of objects. The general pattern is as follows:
- If an object is to be created, the controller routes to a view called new.erb, which contains a form with fields necessary to create a new object. The user fills out the form, which is POSTed back to the controller at a route that creates the object in the database.
- For updating, the controller routes to an object edit form in the edit.erb view. The user updates the object attributes in the edit.erb form, and that information is PATCHed back to the controller, where the object is updated in the database.
- For deleting an object, the delete action is initiated in edit.erb and sent to the controller via a DELETE method. Then the object is deleted in the database.
I started with the User object to allow users to create accounts and log in to the website.
A new user is created at the /signup route in the UsersController, which points to and renders a signup form at the view (users/new.erb). When the user information is entered at the signup form in the view, it is sent back via the POST method to the /signup route in the controller, where the user is persisted to the database.
When editing a user, the UsersController route (/users/:id/edit) directs to the form contained in the view (users/edit.erb), passing the existing user’s information. The edits to the user are made in the edit view and the form is submitted with the PATCH method back to the UserController route (users/:id/edit), where the updates are persisted to the database.
The deletion of a user is initiated at the view (users/edit.erb) and a DELETE method is sent to the controller route(/users/:id), where the user is actually deleted from the database.
Here is a glimpse of the application at this point. I created the first user here:
Successful user logins are driven by the authenticate method is available to the user object because we are using the bcrypt gem, which allows us to store encrypted passwords in the database. Additionally, when the user is authenticated, I keep track of the user across page requests by setting a session variable for the user_id.
Here you can see that the user’s password was encrypted in the database, in the password_digest column.
Adding forms and making adjustments
In working through the remaining forms according to the mock-ups above, I came across an issue that called for an adjustment to the database schema and object relationships. The issue arises when a user object is deleted. It seemed logical that when a user is deleted, that all of their activity within the application should be deleted as well (comments, games, and tag assignments).
Luckily, Active Record has the ability to cascade the deletion of child objects when the parent is deleted using:
When dependent: destroy is added to the has_many relationships to games, tags, and comments, they are deleted when the parent user is deleted.
This logic seemed correct until I pondered on it a bit more. 😕
The power of tags is that they can be shared across different games, dynamically grouping them. Consider the situation where a tag is created by a user and is assigned to a game. Then, say the same tag is assigned to another game by another user. This tagging of the second game with the same tag is really not owned by the user that originated the tag, as they did not make the second game association. This becomes a problem if the originating user is deleted because tag objects that they originated would be deleted as well under the current relationship model. The second tagging association would be lost even though the deleted user did not make the second tag association.
To resolve this issue, I decided to make a second many-to-many relationship, this time between users and tags, where users can assign many tags and a tag can be assigned by multiple users. So, if a user is deleted, then their tag associations would be deleted, but other users tag associations with the same tag would remain intact.
In order to do this, I moved the user_id from the tag table to a new join table, this time called game_tag_users. The game/tag many-to-many relationship remains driven by this join table.
Then, I changed my user to have tags through the game_tag_users table.
Testing of the change can be seen on in the overview video at the end of this post.
Completing the Views
Here is a sampling of what some of the views looked like at this point in the build process. They were raw and not styled, only functional. Each view was connected to the appropriate CRUD route in their respective controller.
Once the views were complete, I added validations to them in order to keep bad data from being saved in the database. Again, Active Record provided very helpful methods for this functionality.
The business logic for the application dictated that a username, email and password were required for a User object to be saved to the database. Additionally the username and email address must be unique. To do this, all that was needed was to add the following validations to the User model.
With these in place, a User will not be saved if any of the validations fail. To check, I put a binding.pry in the UsersController route where the signup form posts to, so we can see if the user is valid based on the form entries. If the new user is not valid, the user object will be populated with full error messages.
If the new user is not valid, it is passed back to the view, so that errors can be displayed back to the user. This Learn.co lesson was very helpful to implement validations.
Finally, without going into too much detail about this goal of the project (this topic is going to drive another blog post 😃), I got some good practice with responsive design techniques on this project.
Also, to make the site a bit more useful and attractive, I added a few more attributes to the game object, most importantly an image, which drove the design of the site.
I was able to get a decent understanding of CSS Grid and Flexbox, along with media queries to get the final version of the site adapting to different screen sizes. All of these tools are very useful to adapt modern websites to display well on screens that range from watches to giant flat panels. Here are some views of the Games Guide’s responsive capabilities.
I learned a lot on this project. In particular I gained a lot of ❤️ for Active Record and what it can do to make development faster and less error prone. The responsive design practice was invaluable as well and I am happy to be able to learn new CSS techniques.
Here is a walk through of the website’s functionality.
Check out the site here: https://gamesguide.herokuapp.com/