Sinatra Web App: MVC, Sessions, and Routes
Tags: ruby, sql, webdev, beginners
Published: 2020-01-20 03:53:48

# A Sinatra Web App for Boulderers

My Sinatra-powered web app — **[Bolderer](https://github.com/jacqueline-lam/bolderer_sinatra_app)**, is designed for boulderers who are eager to track their climbing progress, strengths and weaknesses by logging routes/ problems that they have climbed.  They can also see others' profiles since bouldering is a personal yet social sport, where the climbing community is supportive and would always cheer for your 'sends'.

I started my project by mapping out the models and their relationships with one another. This helped me plan the models, database tables, and the `has_many`, `belongs_to` and `many-to-many` relationships on the models.
### Bolderer Association Diagram:
![Bolderer Association Diagram](https://dev-to-uploads.s3.amazonaws.com/i/v52io2rt7is5yqrntfb4.png)

# Key Concepts

I would like to highlight some key building blocks of my Sinatra project — **MVC**, **Sessions**, **CRUD Actions**, and **RESTful Routes**, which have allowed me to create a functional app and taught me the base concepts of a Content Management System ([CMS](http://www.businessdictionary.com/definition/content-management-system-CMS.html)).

## [Model-View-Controller (MVC)](https://www.codecademy.com/articles/mvc)
*An organized way to build frameworks for web apps.*

I found this immensely useful in helping me structure my code. It provided a separation of concerns by grouping my files by functionalities -- **Models** (logic, where data is manipulated and saved), **Views** (front-end), and **Controllers** (middleman - relays data from browser to app, and from app to the browser). 

### My MVC directories:
![Model-View-Controller](https://thepracticaldev.s3.amazonaws.com/i/v2d57wwptzdxaw8phedu.png)

--------------------------------------------

## User Password Encryption with BCrypt
I used the Ruby Gem [BCrypt](https://rubygems.org/gems/bcrypt/versions/3.1.12) to secure the users' data by encrypting their passwords. It allowed my app to sign up and log-in a user with a secure password.
 
Implementing Bcrypt by adding the password column as `pasword_digest` in my `users` table:

```
class CreateUsers < ActiveRecord::Migration
  def change
    create_table :users do |t|
      t.string :username
      t.string :password_digest
    end
  end
end
```
I also added [ActiveRecord](https://guides.rubyonrails.org/active_record_basics.html)'s macro `has_secure_password` to my User model, which let me access the user's `password` attribute even though the *users* database only had the column called `password_digest`. 

The macro also provided another useful method, `authenticate`:

```
post '/login' do
    user = User.find_by(username: params[:username])

    if user && user.authenticate(params[:password])
      #storing user_id key in session hash
      session[:user_id] = user.id
      redirect "/users/#{user.id}"
    else
      @error = true
      erb :"/users/login"
    end
  end
```
The *User*'s `authenticate` method turned the user's password input into a hash that would get compared with the hashed password stored in the database. If the user authenticated, the method would return the *User* instance; otherwise, it would return `false`. This was helpful for logging in users.


## Sessions and User Authentication and Authorization
- Sessions were used to verify a user's identity, as the users were requested to verify their identity by logging in with valid credentials
- Hence, I used sessions to filter and dictate what a user could see or edit:
  - The users could only view the Problems index page if they were logged in
  - The users could only edit/ delete the resources (problems) that they created

- To use sessions in Sinatra, I enabled my application to use the `sessions` keyword to access the session hash:
```
  configure do
    set :public_folder, 'public'
    set :views, 'app/views'
    enable :sessions
    set :session_secret, "session_encryption"
  end
```

- When a user signs in successfuly, the `user_id` key would get stored in the session hash: `session[:user_id] = user.id`. 

---------------------------------------------------------
## Defining RESTful routes

Another key lesson was learning to use restful routes to implement CRUD actions. I realized how important it was to plan my routes in order to handle the HTTP verbs and URLs in an organized and standardized way.
 
I designed my `ProblemsController`, `UsersController` and `SessionsController` controller actions to follow restful conventions and to map the HTTP verbs (`get`, `post`, `patch`, `delete`) to the controller CRUD actions. 

For my app, I wanted a logged-in user to be able to access all CRUD actions: the ability to create sessions (login)/ problems, read, update or delete their own problems.


My Problems Controller:

| HTTP Verb | Route              | CRUD Action | Used for/ result                                                              |
| --------- | ------------------ | ------ | ----------------------------------------------------------------------------- |
| GET       | /                  | index  | index page to welcome user - login/ signup                                    |
| GET       | /problems          | index  | displays all problem (all problems are rendered)                              |
| POST      | /problems          | create | creates a problem; save to db                                                 |
| GET       | /problems/:id      | show   | displays one problem based on ID in the url<br>(just one problem is rendered) |
| GET       | /problems/:id/edit | edit   | displays edit form based on ID in the URL                                     |
| PATCH     | /problems/:id      | update | modifies an existing problem based on ID in the url                           |
| DELETE    | /problems/:id      | delete | deletes one article based on ID in the URL                                    |


My Users Controller and Sessions Controller:

| HTTP Verb | Route            | CRUD Action | Used for/ result                                          |
| --------- | ---------------- | ------ | --------------------------------------------------------- |
| GET       | /signup          | index  | display signup form                                       |
| POST      | /signup          | create | creates a user                                            |
| GET       | /login           | index  | displays login form                                       |
| POST      | /login           | create | create a session                                          |
| GET       | /logout          | delete | delete session/ log out                                   |
| GET       | /users/:username | show   | display one user’s problems based on :username in the url |



### 

## Other resources:
* **Seeding Data**: The dummy data was very important as it helped to demonstrate to the first user(s) how this web app could be used. There was the option of using APIs to seed data (especially for general topics like recipes), but I decided to create my own instances of Users, Problems, and Styles and their associations to make my data more personal (as it reflected my actual bouldering logs)
* **Bootstrap**: This allowed me to add styling to my forms, buttons, etc. without having to build the CSS from scratch