Sinatra is classy.

My First Sinatra Project

Slowly, but surely, I am working my way through the Flatiron School’s curriculum. Though it is taking me a bit longer than I expected, I also have relocated to Vermont, sold a house, and settled into my new home. So, I guess I shouldn’t be too hard on myself. Living in Vermont is great and I’m hopeful that I can refocus now on getting my Flatiron work done.

To that end, I have completed my first Sinatra application. Sinatara is a framework for web apps that, along with ActiveRecord, can generate some pretty powerful CRUD based apps. Sinatra provides ‘routes’, which are basically pathways to content that your app will follow in response to HTTP requests. These routes allow for loading and manipulating data through Ruby methods.

For my Sinatra app, I imagined a survey system that would allow users to both create and take surveys. I made some decisions along the way to limit the scope of what I would create for this porject. These limitations were selected so that I could fulfill the requirements of the Flatiron curriculum, but also get the project done without going crazy. I’m still going crazy anyway.

To begin, I though about my database and the relationships between the data. I came up with a model that looks something like this:

Sinatra Project Database Model

The “responses” model was not copmleted as previously indicated. So, there are four databases that I used:

So, with that basic model in mind, I got to building the CRUD functionality, models, routes, etc.

To illustrate some of what I learned, let me go over the user authentication and login procedures:

class SessionController < ApplicationController 
  
  get '/login' do
    if !logged_in? 
      erb :'sessions/login'
    else
      @user = User.find_by(email: session[:email])
      redirect :'/'
    end
  end

  get '/registration' do
    if !logged_in?
      erb :'sessions/registration'
    else
      @user = User.find_by(email: session[:email])
      redirect :'/'
    end
  end

  post '/registration' do
     @user = User.new(username: params[:username], firstname: params[:firstname], lastname: params[:lastname], password: params[:password], email: params[:email])
     if !logged_in? && @user.valid?
      @user.save
      session[:email] = params[:email]
      @my_surveys = @user.surveys
      @all_surveys = Survey.all
      redirect '/surveys'
    else
      flash[:message] = ""
      @user.errors.messages.each do |key, message|
        message.each do |text|
        flash[:message] += text + "<br />"
        end
      end
      redirect :'/'
    end
  end

  post '/sessions' do
    login(params[:email], params[:password])
    @user = current_user
    @my_surveys = @user.surveys
    @all_surveys = Survey.all
    redirect '/surveys' 
  end

  get '/logout' do
    session.clear
    redirect :'/'
  end

end

The SessionController manages the user login experience. But three helpers I created in ApplicationController are also relevant:


  helpers do 

    def logged_in?
      !!session[:email]
    end

    def login(email, password)
      user = User.find_by(email: email)
      if user && user.authenticate(password)
       session[:email] = user.email
       session[:user_name] = user.firstname + ' ' + user.lastname
     else
       redirect '/login'
     end
    end

    def current_user
      if logged_in?
        User.find_by(email: session[:email])
      else
        redirect '/' 
      end
    end
  
  end

These helper methods assist the login process by providing the following funtionality:

Now, looking back at SessionController, there are four Sinatra routes that manage the login process:

get ‘/login’ do: This route checks to see if a user is logged in already. If not, it renders the login form. If they are logged in, the user is redirected to their home page.

get ‘/registration’ do: This route renders the registration form if the user is not logged in already.

post ‘/registration’ do: This route handles the registration process. This is where I used validation and flash messages to provide feedback to the user. The validation flash settings are configured in the User class:

class User < ActiveRecord::Base

  validates :username, presence: { message: "You must provide a username." }, uniqueness: { message: "Username is already taken." }
  validates :email, presence: { message: "You must provide an email." }, uniqueness: { message: "That email is alreday taken." } 
  validates :password, presence: { message: "Password cannot be blank." }
  
  has_secure_password validations: false

  has_many :surveys

end

post ‘/sessions’ do: Here, this route logs the user in and then send them to the ‘/surveys’ route where their surveys will be listed for editing, creating, etc.

get ‘/logtout’ do: Finally, this route clears the session hash, logging the user out.

Ok. This post is long and fairly boring, so I’m going to stop here. The CRUD aspect of this app will have to wait for another blog post. That stuff is handled by the SurveyController class. Fun!