Sinatra in a Nutshell

Posted by Cameron J. Leverett on May 30, 2020

​ Creating a web application is no easy task, I mean not for your average beginner, and If you are like me you might have a hard time trying to figure out where to begin. So where do we begin or better yet where did I begin?

1. NUMERO UNO

If your anything like me, you’ll want to cut out as much monotonous work as possible this includes building a File structure. Luckily for us, there is a ruby gem for that. corneal will do the bulk of the work for us. Just like any other gem, you’ll want to install said gem like so:

gem install corneal
#run
corneal new App-Name
bundle install

This should give access to a ton of important gems that we’ll need for our app such as activerecords, bcrypt, rake, and, most importantly Sinata and, more; check out the corneal link above for full details.

​ Also thanks to corneal our we have a file structure that should look something like this

├── config.ru
├── Gemfile
├── Gemfile.lock
├── Rakefile
├── README
├── app
│   ├── controllers
│   │   └── application_controller.rb
│   ├── models
│   └── views
│       ├── layout.erb
│       └── welcome.erb
├── config
│   ├── initializers
│   └── environment.rb
├── db
│   └── migrate
├── lib
│   └── .gitkeep
└── public
|   ├── images
|   ├── javascripts
|   └── stylesheets
|       └── main.css
└── spec
    ├── application_controller_spec.rb
    └── spec_helper.rb

NEXT

Now we can start by building out our migrations table.

first, you’ll want to start with our has_many and belongs to relationship, It is important to remember that, has_many is an activerecords method. Let say that we were building a simple user application. we could create our rake commands such as:

rake db:create_migration NAME=user
rake db:create_migration NAME=Item

This should give us two migrations that look like this

#db/migrate/10001_create_users.rb
class CreateUsers < ActiveRecord::Migration
  def change
    create_table :user do |t|
     t.timestamps null: false
    end
  end
end
#db/migrations/10002_create_Items.rb
class CreateItems < ActiveRecord::Migration
  def change
    create_table :user do |t|
     t.timestamps null: false
    end
  end
end

we’ll want to build out the the table with the appropriate colums like so:

#db/migrate/10001_create_users.rb
class CreateUsers < ActiveRecord::Migration
  def change
    create_table :user do |t|
     t.string :user_name
     t.email
     t.password_digest
     t.string :item_name
     t.timestamps null: false
    end
  end
end
#db/migrations/10002_create_Items.rb
class CreateItems < ActiveRecord::Migration
  def change
    create_table :user do |t|
    t.string  :name
    t.integer :item_id 
     t.timestamps null: false
    end
  end
end

Now in our models class, we want to make our association that way we can use association methods that we inherit from ActiveRecord.

#app/model/user.rb
class User 
has_many :items
end

#app/model/item.rb
class Item 
belongs_to :user
end

Now, in a real-world situation, we wouldn’t want to do something like “user has_many: through: :user_items” but for this demonstration we’ll keep it pretty simple.

and Then

Now after running rake migrate we can start building our routes lets start with the login process

authentication

we can’t very well build an application that just allows our user’s password to be passed around with no security. so we’ll use some validation methods inherited from active records

#app/model/user.rb
class User 
has_many :items
 has_secure_password #mehtod, for salting our password aka password digest
  validates :user_name, :email, presence: true,  uniqueness: true
    #validates user information to make sure  it is "valid"
  validates :password, presence: true
  validates :password_confirmation, confirmation: true
end

#app/model/item.rb
class Item 
    #for this example we won't need to validate our belongs_to method
belongs_to :user
end
require './config/environment'
require 'sinatra/flash'

class ApplicationController < Sinatra::Base

  configure do
    set :public_folder, 'public'
    set :views, 'app/views'
    enable :sessions #this will set a session so that we can store the user id in a hash and grab the current_user's ID
    register Sinatra::Flash #this is so we can display error messages to the user.
    set :session_secret, "no_peeping_jerrys" #makes** it **harder** for **someone to create a fake session_id and hack into your site** without signing up or signing in.
      
  end
  
  get "/" do
    erb :index
  end
  
  helpers do
    def logged_in? # checks to see if user is a user is logged in
      !!session[:user_id]
    end

    def current_user #returns true if the user making changes is the current user.
      user.find(session[:user_id])
    end
  end
end

This will All of our classes in our “models” folder will inherit this class

Now we can build our user and items Route :

class usersController < ApplicationController

  get '/signup' do #renders the signup page for the client
    if !logged_in? #checks to make sure they are not already logged in

      erb :'/users/new.html' 
     else
      redirect to '/users'
    end
  end
    
   post '/signup' do 
   @user = User.new(name:#obj information goes here)
       if @user.save # if true
           session[:user_id] = @user.id #sets the id attribute to be equal to session id
           redirect to "users/#{@user.id}" #send to a spicific user page       
       end
   end  
       get '/login' do #if the user has an existing account this renders the login
    if !logged_in?

      erb:'users/index.html'
     else
      redirect to'/users'
    end
  end
        post '/login' do #this will be passed into params so we can minuplate in our controller with "post"
    @user = User.find_by(user_name: params[:user_name]) #AR dynamic method allows us to grab the user by attribute input
    if @user && @user.authenticate(params[:password])
      session[:user_id] = @user.id

      redirect to "/users/#{@user.id}"
     else
      redirect to '/login'
    end
  end

  get '/logout' do
    if logged_in?
      session.clear #clears session when the user would like to leave
      redirect to '/'
    end
  end
end

This will give us access to all the user’s via instance variable

class ItemsController < ApplicationController
get "/items/new" do
    erb :"/items/new.html"
  end

  # POST: /items
  post "/items" do
    params.delete_if {|p| p == "submit"}
    if !item.find_by(name: params[:name])
      @item = item.new(params)
      @item.save
      current_user.items << @item
      current_user.save
        
      redirect to "/items/#{@item.id}"    
  end
end

Viola, now you have the start of a working web app Congrats!! Now I know there is a lot of unpacking to do here but, unpacking and, dissecting is our jobs its what, I believe to be a good engineer in a general sense, so if your reading this and you find yourself overwhelmed, just keep in mind that this is how you truly learn to understand code.

If you would like to view a Sinatra project that I built using this GoRobo

RESOUCES

AR assosiations

AR query interface