This post begins a series of articles about designing, creating, and deploying a Sinatra ToDo app as part of the Flatiron School curriculum
- Build an
- Use multiple models.
- Use at least one
has_manyrelationship on a User model and one
belongs_torelationship 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
- 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.
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.
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
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
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
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
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.
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
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
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