Building a Quiz app with Ruby on Rails
Tags: Web Development, Bootcamp, Portfolio, Ruby on Rails, Programming

Validations

User Validations

The user class validates the presence of a first name last name and email. In addition to a simple presence check, email is also validated for uniqueness and email format via regex.

The user class also validates the length of the password (minimum 8 characters).

Group

The group class uses a custom validator class GroupValidator which which prevents duplicate groups by checking if a user already has a group with the same name.

Quiz

The Quiz model uses two separate custom validation classes QuizValidator and PublicationValidator. QuizValidator prevents duplicate named quizzes from being created and PublicationValidator prevents a quiz from being published with zero questions.

Question

The Question model validates with QuestionValidator which makes sure a Question is not a duplicate, has 4 answer options with a single correct answer and no duplicate answers.

Result

The Result model uses ResultValidator which makes sure every question is answered before submission and prevents a student from taking a quiz twice.

Authentication

Quizzical utilizes two separate authentication schemes, the first uses ActiveModel::SecurePassword, adding the user id to the session after checking the user submitted password against the user’s password digest stored in the database.

The second scheme uses the zquests OmniAuth gem to authenticate users using a User class method #from_omniauth which uses the return value from the OAuth transaction to find a user, if one exists it returns the found user, if no user exists to match the returned data, a new user is created (as a student) and returned.

Creating users via OAuth

There are two types of users in our app and we want both of them to be able to sign up easily via OAuth, so we’re going to have to be a little clever about how we handle user signup via OAuth.

Sign Up Page

We can see from the screenshot that our login form has a checkbox which allows the user to specify whether they are a teacher, when checked, the teacher parameter sent by the form is set to true. We can also see two separate signup via google links. To pass parameters “through” the OAuth transaction we can add them to the link like so:

the teacher parameter with be passed “through” the OAuth transaction and is available in the Sessions Controller.

Authorization

For authorization I used the varvet/pundit gem which provides a simple way to set up powerful authorization policies. The readme on the github page has more than enough information to get you up and running so I wont go into detail about how to set it up but I will provide a high level overview of how the gem works.

Running the generator from the gems readme creates a “policies” folder containing an “application_policy” file.

Within the application_policy you could restrict actions site-wide, for instance; restricting all actions unless logged in. To set specific policies you create new policy files like so:

This excerpt shows a policy which only allows a user to destroy a question record if that user is both a teacher and owns the quiz to which the question belongs.

To use this policy a few lines are added to the relevant controller

To access and unauthorized action a user would have to actively alter a URL in an attempt to so, that being said if a user were to do that they would be met with a 403 page, this is accomplished by adding the following to application.rb

Routes

To reflect the relationships between models in the app I have made use of nested routes. A student accessing a quiz must so by first accessing a group, so it makes sense for the quiz resource to be nested within the group resource like so:

This nested resource setup provides us with the following routes.

group_enrollments- A POST route that enrolls a user in a class.

A POST route that enrolls a user in a class. group_quizzes- Another POST route which allows a user to create a quiz.

Another POST route which allows a user to create a quiz. new_group_quiz- A GET route which renders a form to post data to group_quizzes.

A GET route which renders a form to post data to group_quizzes. group_quiz- A GET route which renders a list of students who have taken the given quiz with grades and a list of students who have not.

A GET route which renders a list of students who have taken the given quiz with grades and a list of students who have not. groups- Responds to a GET request by rendering the index of available groups, accepts POST requests from new_group to create a new group.

Responds to a GET request by rendering the index of available groups, accepts POST requests from to create a new group. new_group- A GET route renders a form to create a new group.

A GET route renders a form to create a new group. group- A GET route which renders a list of quizzes for a given group, along with the group name and teacher full name.

In addition to the nested resources I have several explicit routes for things like logging in, signing up and OAuth:

The advantage of explicit routes is being able to name routes for easier readability, it makes more sense to redirect a user the “login” path rather than a “new_session” path, or to link to “sign_up_path” rather than “new_user_path”. In addition to readability, OAuth requires specially formed routes.

Frontend

I didn’t really regard this project as much of a frontend endeavor, however I do plan to redo the interface once I’ve learned react. I did heavily rely on the use of rails partials and Action View helpers to simplify my layout and views. Breaking more complex layouts down into component partials increases readability and the modular nature of partials allows you to really focus on one thing at a time and implement some slick features in a simple way.

For Instance; I would like the navigation links to change based on the context of the view that is being rendered (you don’t need a “Delete this Class” link when you are not viewing a group) to accomplish this i used content_for? in the site_nav partial to yield content from the current view like so:

Then in our view we can provide content with the Identifier :page_specific_link.

I also created a helper to greet a user by name with the time of day i.e. “Good Morning Samuel!”, “Good Evening Sam”

For views where I wanted to iterate through a list I created component sized partials to simplify the logic:

The First partial will display the title of and grade the user scored on a given quiz, the second will display the quiz title and question count of a quiz a user has not taken.

Lessons Learned

Plan as much as you can, I planned this project out extensively and realized I had not planned as much as I could have which means I wasted time. Rails, though considered to be aging by many nowadays is a very robust and and usable framework for turning an idea into a working thing quickly and you can easily extend rails with gems that provide and insane amount of functionality in a few lines of code and terminal commands.

The thing I like most about rails is that it amplifies the thing that draws me to programming in the first place- the ability to create.