Sinatra ToDo App
Tags: ruby, sinatra, activerecord
Published: 2019-10-29 15:31:24

---
title: Sinatra ToDo App
published: true
description: 
tags: ruby, sinatra, activerecord
series: Sinatra App
cover_image: https://thepracticaldev.s3.amazonaws.com/i/xyda5m5lnf5tewa0fnlc.jpg
---
*This post begins a series of articles about designing, creating, and deploying a Sinatra ToDo app as part of the [Flatiron School](https://flatironschool.com/) curriculum*

## Introduction 

### Requirements

- Build an `MVC` Sinatra application.
- Use `ActiveRecord` with Sinatra.
- Use multiple models.
- Use at least one `has_many` relationship on a User model and one `belongs_to` relationship on another model.
- Must have user accounts - users must be able to sign up, sign in, and sign out.
- Validate uniqueness of user login attribute (username or email).
- Once logged in, a user must have the ability to create, read, update and destroy (CRUD) the resource that `belongs_to` user.
- Ensure that users can edit and delete only their own resources - not resources created by other users.
- Validate user input so bad data cannot be persisted to the database.
- BONUS: Display validation failures to user with error messages. (This is an optional feature, challenge yourself and give it a shot!)

**TLTR**: Feel free to get the [source code](https://github.com/eclectic-coding/sinatra-todo-app).

## App Design
The overall plan for this app was to design a web site with a full-stack design, using [Sinatra](http://sinatrarb.com/) back-end, ERB files to display the front-end, and using [Bootstrap](https://getbootstrap.com/) to quickly prototype the site. 

Based on the requirements the site includes a user account model which allows the end user to create a Todo list secure from other users. 

### Security
To handle the security of the user model, this app utilizes the `bcrypt` gem, and `securerandom` to secure the `session_secret`. In addition, it uses `DOTENV` locally during store the `session_secret`, and of course the `.env` file is excluded from the `.gitignore`. 


## Data Design
The database design is fairly simple. First the user table uses `password_digest` as the password field to utilize the salt and hash provided by `bcrypt`:
```
class CreateUsers < ActiveRecord::Migration
  def change
    create_table :users do |t|
      t.string :name
      t.string :email
      t.string :password_digest
      t.timestamps null: false
    end
  end
end
```

The table for the ToDo's `belongs_to` the `user_id` so each list item can be connects to the user:
```
class CreateTodos < ActiveRecord::Migration
  def change
    create_table :todos do |t|
      t.string :title
      t.integer :user_id
      t.timestamp null: false
    end
  end
end

```

## Controller Design 
It would be easy to limit the number of controllers but when I was designing the application, I decided to separate the concerns a little more. 

The `application_controller` controls the app. It includes the security setup, flash messages using `rack-flash`, sets the root path, and includes a few helpers methods for the user model. Specifically, the `current_user` method which is used to compare the user and session_id to the todo item later in the source code. 
```
def current_user
      @current_user ||= User.find_by(id: session[:user_id]) if session[:user_id]
    end
```
The `sessions_controller` validates the user login:
```
post '/login' do
    user = User.find_by(name: params[:name])
    if user&.authenticate(params[:password])
      session[:user_id] = user.id
      redirect '/todos'
    else
      flash[:danger] = 'Invalid login credentials!'
      redirect '/login'
    end
  end
```
The new user account creation is handled in the `user_controller`. Therefore, exiting accounts and new accounts have their own separate controllers: 
```
post '/signup' do
    @user = User.new(params)
    if @user.save
      session[:user_id] = @user.id
      flash[:success] = 'Successfully created user account.'
      redirect '/todos'
    else
      flash[:danger] = 'Please enter valid registration data!'
      redirect '/signup'
    end
  end
```
The `todos_controller`, well you guessed it 🤔 handles the Todo list items. 

```
  get '/todos' do
    if logged_in?
      @user = User.find(session[:user_id])
      @todos = Todo.where(user_id: current_user)
      erb :'/todos/index'
    else
      redirect '/login'
    end
  end
```
It is in this method that the current logged in user is retrieved and compared to the `user_id` of each ToDo item. Based on the associations in the database tables, this is the method would pulls together the corr3ct list. 

*In the next article I will write about the challenges to deploy to Heroku*