Know It All 
Tags: ruby, rails, javascript, css
Published: 2021-03-14 16:09:56

Happy Spring, fellow developers! 

Since my recent Ruby on Rails project on [Plan My MD Visit](https://dev.to/codinghall/plan-my-md-visit-c0l), I have immersed myself in JavaScript Fundamentals from recognizing JavaScript events, DOM manipulation, ES6 Syntax Sugar and introduction of Object-Orientation. I plan to supplement my JavaScript learning materials after this project submission. 

I went through a few iterations in my head on [Single-Page Application (**SPA**)](https://en.wikipedia.org/wiki/Single-page_application) prior to settling on an idea. Overthinking as usual, but in my defense it is one `HTML` file, and lends itself too much freedom. 😅  Moving on, my husband loves trivia, and nothing is better than to surprise him by creating my own version of a trivia app, **Know It All**. The challenge becomes finding a completely free JSON API for use. This capstone project focuses on creating **Ruby on Rails** ***back-end*** and **JavaScript/HTML/CSS** ***front-end***.

---

#### Back-End Development
1. [Planning and Building Rails API](#chapter-1)
2. [Open Trivia DB API](#chapter-2)
3. [Generating Active Record Models](#chapter-3)
4. [Routes, Controllers and Serializers](#chapter-4)
5. [Communicating with the Server](#chapter-5)

#### Front-End Web Programming
1. [DOM Manipulation with JavaScript Event Listeners](#chapter-6)
2. [Re-factoring Early](#chapter-7)
3. [End Page Sequence](#chapter-8)
4. [Lessons Learned](#chapter-9)

#### Build Status and Future Improvement

---
    
### Back-End Development
  
#### 1. Planning and Building Rails API <a name="chapter-1"></a>

With `--api`, Rails removes a lot of default features and middleware, and our controllers by default inherit from `ActionController::API`. This differs slightly from traditional *Ruby on Rails* application. In my previous *RoR* project, I had my controllers inheriting from `ActionController::Base` with responsibilities in creating routes and rendering many `_.html.erb` files.

```
rails new know_it_all_backend --database=postgresql --api
```

The above command will generate a Rails API using [PostgreSQL](https://www.postgresql.org/) database. The intent is to deploy my backend application eventually on [Heroku](https://www.heroku.com/), which does not support [SQLite](https://sqlitebrowser.org/) database. One more important thing to add is to `bundle install` gem `rack-cors`. This is useful for handling **[Cross-Origin Resource Sharing (CORS)](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS)** configuration, allowing my front-end application to perform asynchronous requests.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/usi3l1qc4b1hp02ofm9r.gif)

I approached this project in a *vertical manner*, building out one model and/or feature at a time. This strategy streamlines any effort when dealing with complex relationships from back-end to front-end, and vice versa.


#### 2. Open Trivia DB API <a name="chapter-2"></a>

After traversing through the API universe, I got excited when finding an [Open Trivia Database](https://opentdb.com/) without the need for an API Key. Awesome sauce. 🙅🏻‍♀️ 

The challenge is less on acquiring the JSON API, but setting up the `Api` adapter class on Rails back-end. I utilized the `.shuffle` Ruby method to randomize the provided multiple choice. In the JavaScript front-end, I should be able to set up `if/else` conditionals when comparing the user's selected answer to the `correct_answer`. I managed to `JSON.parse` in `irb`, and confirmed responses back from the open/free API.

```ruby
> data["results"][0]
   => {"category"=>"Animals",
       "type"=>"multiple",
       "difficulty"=>"hard",
       "question"=>"What was the name of the Ethiopian Wolf before they knew it was related to wolves?",
       "correct_answer"=>"Simien Jackel",
       "incorrect_answers"=>["Ethiopian Coyote", 
       "Amharic Fox", "Canis Simiensis"]}
> [data["results"][0]["correct_answer"], data["results"][0]["incorrect_answers"][0], data["results"][0]["incorrect_answers"][1], data["results"][0]["incorrect_answers"][2]].shuffle
  => ["Amharic Fox", "Canis Simiensis", "Simien Jackel", "Ethiopian Coyote"]
> multiple_choice = _
  => ["Amharic Fox", "Canis Simiensis", "Simien Jackel", "Ethiopian Coyote"]
> multiple_choice[0]
  => "Amharic Fox"
> multiple_choice[1]
  => "Canis Simiensis"
> multiple_choice[2]
  => "Simien Jackel"
> multiple_choice[3]
  => "Ethiopian Coyote"
```

There will be a total of eight (8) Trivia categories: Animals, Celebrities, Computer Science, Geography, History, Mathematics, Music and Sports. Once the `Api` adapter class was fully set up, I initiated the creation of both `Category` and `Question` models in the `seeds.rb`.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x3rm7b3qq9zutzwjervx.gif)


#### 3. Generating Active Record Models <a name="chapter-3"></a>

```
$ rails g model User name avatar animals_score:integer celebrities_score:integer computer_science_score:integer geography_score:integer history_score:integer mathematics_score:integer music_score:integer sports_score:integer 
    invoke  active_record
    create    db/migrate/20210224154513_create_users.rb
    create    app/models/user.rb

$ rails g model Category name 
    invoke  active_record
    create    db/migrate/20210224045712_create_categories.rb
    create    app/models/category.rb

$ rails g model Question category_id:integer question:text choice1 choice2 choice3 choice4 answer
    invoke  active_record
    create    db/migrate/20210227220035_create_questions.rb
    create    app/models/question.rb

```

In the terminal, I can now run `rails db:create && rails db:migrate`. The `rails db:create` is necessary for PostgreSQL database. At first, I had an erroneous terminal return, and had to update my PostgreSQL 13. Once re-installed and 🐘  running, the command should create the database and run migration swiftly.

```
$ rails db:create && rails db:migrate
  Created database 'know_it_all_backend_development'
  Created database 'know_it_all_backend_test'
  == 20210224045712 CreateCategories: migrating =================================
  -- create_table(:categories)
     -> 0.0545s
  == 20210224045712 CreateCategories: migrated (0.0547s) ========================
	
  == 20210224154513 CreateUsers: migrating ======================================
  -- create_table(:users)
     -> 0.0575s
  == 20210224154513 CreateUsers: migrated (0.0575s) =============================
	
  == 20210227220035 CreateQuestions: migrating ==================================
  -- create_table(:questions)
     -> 0.0571s
  == 20210227220035 CreateQuestions: migrated (0.0572s) =========================
```

The next step would be to test my models and associations. My association between `Category` and `Question` would be as simple as category `has_many` questions, and a question `belongs_to` a category.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w8d46wqrriy41txad7of.png)

```ruby
class User < ApplicationRecord
end

class Category < ApplicationRecord
    has_many :questions, dependent: :destroy
end

class Question < ApplicationRecord
    belongs_to :category
end
```

The `dependent: :destroy` would be helpful for `.destroy_all` method in `seeds.rb` file. This is useful when triggering the `rails db:seed` command. 

As an [Active Record](https://guides.rubyonrails.org/association_basics.html) veteran, it is still a good practice to validate every single instance of  association relationships. *Note — presented model attributes resulted from extensive trial and error. I approached this project with one feature working simultaneously on the back-end and front-end, adding one model attribute at a time.*

```
001 > animals = Category.create(name: "Animals")
  (0.2ms) BEGIN
  Category Create (4.8ms)  INSERT INTO "categories" ("name", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id"  [["name", "Animals"], ["created_at", "2021-02-28 18:30:29.016555"], ["updated_at", "2021-02-28 18:30:29.016555"]]
  (40.4ms)  COMMIT
  => #<Category id: 1, name: "Animals", created_at: "2021-02-28 18:30:29", updated_at: "2021-02-28 18:30:29">
002 > animals_trivia = animals.questions.create(JSON.parse(File.read("animals.json")))
  (0.2ms) BEGIN
  Category Create (4.8ms)  INSERT INTO "categories" ("name", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id"  [["name", "Animals"], ["created_at", "2021-02-28 18:30:29.016555"], ["updated_at", "2021-02-28 18:30:29.016555"]]
  (40.4ms)  COMMIT
  => #<Category id: 1, name: "Animals", created_at: "2021-02-28 18:30:29", updated_at: "2021-02-28 18:30:29"> 
  (0.3ms) BEGIN
  Question Create (4.8ms)  INSERT INTO "questions" ("question", "choice1", "choice2", "choice3", "choice4", "answer", "category_id", "created_at", "updated_at") VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) RETURNING "id"  [["question", "What was the name of the Ethiopian Wolf before they knew it was related to wolves?"], ["choice1", "Canis Simiensis"], ["choice2", "Simien Jackel"], ["choice3", "Ethiopian Coyote"], ["choice4", "Amharic Fox"], ["answer", "Simien Jackel"], ["category_id", 1], ["created_at", "2021-02-28 18:30:42.398662"], ["updated_at", "2021-02-28 18:30:42.398662"]]
  (55.1ms)  COMMIT
  (0.2ms) BEGIN
  ...
003 > animals_trivia.all.count
  => 50
```

#### 4. Routes, Controllers and Serializers <a name="chapter-4"></a>

##### Routes

With the front-end application hosted on a specific domain, I would think it is prudent to *namespace* my back-end routes. It provides an indication that these back-end routes are associated with the API. For example, `https://knowitall.com/api/v1/categories`. The `api/v1` suggests my Rails API *version 1*. I might return and continue my effort on future build status (*version 2*, etc). In the `config/routes.rb`, I provided the intended *namespaced* routes and confirmed with `rails routes` command.

```ruby
Rails.application.routes.draw do
  namespace :api do 
    resources :users, only: [:index, :create, :show, :update]
  end 

  namespace :api do 
    namespace :v1 do
      resources :categories, only: [:index] do 
        resources :questions, only: [:index]
      end 
    end 
  end
end
```

##### Controllers

`rails g controller api/Users`, `rails g controller api/v1/Questions` and `rails g controller api/v1/Categories` create `UsersController`, `QuestionsController` and `CategoriesController`. These *namespaced* routes and their respective controllers nomenclature help tremendously in setting up filenames hierarchy. 

*Note — make sure the PostgreSQL 🐘  is running while configuring routes and controllers.*

```ruby
class Api::UsersController < ApplicationController
    def index
        users = User.all 
        render json: UserSerializer.new(users)
    end 

    def create 
        user = User.create(user_params)

        if user.save
            render json: UserSerializer.new(user), status: :accepted
        else
            render json: {errors: user.errors.full_messages}, status: :unprocessable_entity
        end 
    end 

    def show 
        user = User.find_by(id: params[:id])

        if user 
            render json: user
        else 
            render json: { message: 'User not found.' }
        end 
    end 

    def update 
        user = User.find_by(id: params[:id])
        user.update(user_params)

        if user.save
            render json: user
        else 
            render json: { message: 'User not saved.' }
        end 
    end 

    private 

        def user_params
            params.require(:user).permit(:name, :avatar, :animals_score, :celebrities_score, :computer_science_score, :geography_score, :history_score, :mathematics_score, :music_score, :sports_score)
        end 
end
```

I will only have the `UsersController` displayed here, and briefly convey the `render json`. My rails routes only strictly render JSON strings. This is useful when building JavaScript front-end on DOM manipulation and performing asynchronous requests. The `user_params` on `name`, `avatar` and all category `scores` will be included in the body of `POST` and `PATCH` requests when executing `fetch`. `status: :accepted` helps to inform the user of the [success 202 HTML status](https://httpstatuses.com/202) when submitting user input forms on the front-end application. If it fails to save, `status: :unprocessable_entity` notifies the [client error 422 HTML status](https://httpstatuses.com/422).

##### Serializers

`gem 'fast_jsonapi'` is a [JSON serializer](https://github.com/Netflix/fast_jsonapi#table-of-contents) for Rails APIs. It allows us to generate *serializer* classes. The goal of a serializer class is to keep controllers clear of excess logic, including  arranging my JSON data to display certain object attributes. It does not hurt to practice serializer early on, even though the current state of my **Minimum Viable Product (MVP)** does not necessarily require one.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1292fv1uicgs1mbk6ubl.gif)


#### 5. Communicating with the Server <a name="chapter-5"></a>

In order to make sure the Rails server back-end API worked, I tested a few **Asynchronous JavaScript and XML (AJAX)** calls on my browser console. While I have been using a lot of `fetch()` for this project, I have yet to challenge myself with [`async` / `await` function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function). I am glad my initial attempt of `fetch()` in browser console made successful requests. Moving on to front-end!

{% github fentyhall/KnowItAll_backend %}


---

### Front-End Web Programming

I have to say this part is my most challenging part! I was struggling to gather all of my new knowledge of **The Three Pillars of Web Programming**: *recognizing JavaScript events, Document Object Model (DOM) manipulation, and communicating with the server on a Single-Page Application (SPA)*. Separation of concerns, as a fundamental programming concept, is still applicable. **HTML** defines the *structure of the website*, **JavaScript** provides *functionality* and **CSS** defines the *visual presentation*.


#### 1. DOM Manipulation with JavaScript Event Listeners <a name="chapter-6"></a>

It took me a few days practicing on a set of hard-coded trivia questions and having my trivia cards update as the user progresses to the next question. Know It All includes a score tracker, questions quantity, progress bar, along with passing and/or failing User Interface (UI) alerts. Having Single-Page Application (SPA) required me to create an element with `document.createElement('...')` multiple times, and using either `.append()` or `.appendChild()` often. Also, trying to incorporate Bootstrap CSS early resulted in a slow and unproductive debugging process. A part of me loves spending gazillion hours on CSS elements. *Note to self — do not waste your time on CSS!* 😅

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/puhtkechgocews1de7pf.gif)

One particular challenge I found was to gather user input fields and update their back-end values with asynchronous JavaScript `PATCH`. Later I found that I got stuck on an erroneous `fetch` url, and corrected my string template literals to `${this.url}/${currentUser.id}`. While I used a lot of `standard` and `static` methods in my OO JavaScript, I plan to explore both `get` and `set` methods.


#### 2. Re-factoring Early <a name="chapter-7"></a>

After spending sometime working on basic event handlings, my `index.js` file piled up easily with 200+ lines of code. While I have spent the past month on [JavaScript Functional Programming](https://medium.com/javascript-scene/master-the-javascript-interview-what-is-functional-programming-7f218c68b3a0), **Object-Oriented (OO) JavaScript** offers better data control, easy to replicate (with `constructor` method and `new` syntax), and grants us the ability to write code that convey these relationships. I decided to build `class`es and their *execution contexts* in separate files, `api.js`, `category.js`, `user.js` and `question.js`. Each `class` has its own *lexical scope* of variables and functions, leaving `index.js` with global variables and callback functions necessary to support `index.html`.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ki08wso6ptq3wlca53ga.gif)

During this re-factoring exercise, I also removed all of my `var`s, and replaced them with either `const` or `let`. Variables declared with `const` and `let` are *block-scoped*.


#### 3. End Page Sequence <a name="chapter-8"></a>

Drum roll... 🥁  We are now coming close to an end. After each set of trivia questions, users should be able to see their final score, and whether or not they beat their previous score. If they do, the new (or higher) score will be saved in the Rails API user database. There will be two options for the user to either `Play Again` or return back to `Home` page.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/72vg4ykqygga7asrfuuw.gif)


#### 4. Lessons Learned <a name="chapter-9"></a>

After months of `GitHub`ing, I am getting really comfortable with working on a separate branch, and merge to master. The git command `git co -b <branch_name>` becomes my go-to `git` command. 

Understanding JavaScript's syntax and semantics after months dwelling on Ruby has been fun. For example, in JavaScript, functions are treated as *first-class* data, and understanding some of the concepts of *hoisting* and [scope chain](https://en.wikipedia.org/wiki/Turtles_all_the_way_down). JavaScript engine works in *compilation* phase and *execution* phase. Since I used a lot of JavaScript event `click` and `submit` for this project build, I would love to explore [other browser events](https://developer.mozilla.org/en-US/docs/Web/Events). This [YouTube tutorial](https://youtu.be/Bv_5Zv5c-Ts) helped me tremendously to better understand the weird parts of JavaScript.

{% github fentyhall/KnowItAll_frontend %}


---
    
### Build Status and Future Improvement

Know It All was completed in a 2-week timeframe from API data search, Ruby on Rails back-end, and JavaScript front-end User Interface. Future cycle of product development as follows:

- Add `Sub Category` to model associations. User should be able to select `Category` and its `Sub Category`. For example, science category has many sub categories including physics, mathematics, biology and so on.

    ![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w5dxmoafn6rlb1tb1dvq.png)

- Outsource APIs for the aforementioned `Sub Category` trivia questions.
- Gather user inputs on their favorite `Category` on future app improvement.
- Utilize `setInterval` and/or time limit of 15-20 seconds on each Trivia question.
- User authentication.
- Create a toggle track for dark mode 😎

***

Post Scriptum:
This is my Module 3 capstone project with Flatiron School. I believe one of the catalyst to becoming a good programmer is to welcome constructive criticism. Feel free to drop a message. 🙂
 
Keep Calm, and Code On.

External Sources:
[Open Trivia Database](https://opentdb.com/)
[CSS Bootstrap](https://getbootstrap.com/)
[Unsplash](https://unsplash.com/)

***
<center>[**fentybit**](https://fentybit.me/) | [GitHub](https://github.com/fentybit) | [Twitter](https://twitter.com/fentybit) | [LinkedIn](https://www.linkedin.com/in/fentybit/)</center>