DEV Community

Cover image for Bat Researchers' Log MVC Sinatra
Dorthy Thielsen
Dorthy Thielsen

Posted on

Bat Researchers' Log MVC Sinatra

Today I finished my MVC Sinatra project for Flatiron School. I am happy to share with you my Bat Researchers’ Log, which is my first web application. The goal of this application is to help researchers from all over the world to communicate about the bats that they are researching.

I first wanted to say how much I loved learning Sinatra. Before this section of Flatiron, everything was CLI which was cool but not as rewarding as seeing something on the internet that I created, although just ran from my localhost. I even got to do my own CSS and HTML styling which was incredible. I did use Bootstrap for a lot of my styling, but definitely made it my own.

MVC stands for Models, Views, and Controllers. Views are what the user sees when they open up a web application. The views are written in .erb files which stands for embedded Ruby. This allows you to add ruby into your HTML. For instance, I often used erb tags to add if statements or to auto generate attributes of a certain bat so you don’t have to hard code in each individual bat. Here is an example from my user show page or account page:

<h3><%= @user.username %></h3>
Email: <%= @user.email %><br>
Organization: <%= @user.organization %><br>
Bats:
   <ol>
       <% @user.bats.all.each do |bat| %>
           <li><a class="button" href="/bats/<%= bat.identification %>"> <%= bat.identification %></a></li>
       <% end %>
   </ol>

<% if logged_in? && current_user.id == @user.id %>
   <a class="btn btn-warning btn-sm active" href="/user/<%= @user.slug %>/edit">Edit Your Account</a><br><br>
   <a class="btn btn-danger btn-sm active" href="/user/<%= @user.slug %>/delete">Delete Your Account</a>
<% end %>
Enter fullscreen mode Exit fullscreen mode

The erb tags are <% %> or <%= %>. If the logic isn’t something that will be shown on the page, you will use <% %>. Otherwise you use <%= %> to display specific information to the user. The views allow the user to communicate through HTTP requests to our controllers.

The controllers relay data from the browser to the application and from the application to the browser. The controller takes requests from the views and then talks to the models. The controller takes requests that are associated with what the user sees as urls. For example my most basic controller function is to load the welcome page, which looks like:

 get "/" do
   erb :welcome
 end
Enter fullscreen mode Exit fullscreen mode

Which is just saying if the request is to get the root then load the view welcome. Basically just loading the home page of my application. If you wanted to save new data, for instance when a new user creates an account, then that would be a post request. We are saving, or posting data to our database.

post '/signup' do
       user = User.new(params[:user])
       #binding.pry
       if user.save
           session[:user_id] = user.id
           redirect to "/bats"
       else
           flash[:error] = "#{user.errors.full_messages.join(", ")}"
           redirect to "/signup"
       end
   end
Enter fullscreen mode Exit fullscreen mode

If we are editing an existing entry in our database, then that would be a patch request.

patch '/bats/:identification' do
       bat_authorization
       #binding.pry
       if @bat.update(params[:bat])
           flash[:message] = "Your bat has been updated."
           redirect to "/bats/#{@bat.identification}"
       else
           flash[:error] = "#{@bat.errors.full_messages.join(", ")}"
           redirect to "/bats/#{@bat.identification}/edit"
       end
   end
Enter fullscreen mode Exit fullscreen mode

Note: to use patch or delete, you must use Rack::MethodOverride which you add that line of code to config.ru with your other controllers. It must be at the top of the list of your controllers, otherwise it won't work.

The models are where data is manipulated and or saved. It connects to our database to create new instances of a class. Basically every time you make a new bat or a new user, that is the job of the model. The model sets the attributes, relationships, and validates aspects as each new bat or user is created. In my case, I have two models, bats and users.

class User < ActiveRecord::Base
   has_secure_password

   has_many :bats

   validates :username, presence: true, uniqueness: true, length: {minimum: 5}
   validates :email, presence: true, uniqueness: true, format: {with: URI::MailTo::EMAIL_REGEXP}
   # /\A[a-zA-Z0-9.!\#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*\z/
   validates :password, length: {in: 6..20}

   def slug
       username.downcase.gsub(" ", "-")
   end

   def self.find_by_slug(slug)
       User.all.find{|user| user.slug == slug}
   end
end
Enter fullscreen mode Exit fullscreen mode

The has_many line creates an association between the two models. The user has many bats and a bat belongs to a user. Using ActiveRecord validations, I am making sure that the user has a secure password that is between 6-20 characters. I am also making sure that the username is unique so there won’t be multiples of one user and that the length of the username is 5 characters. In the email validations I am also making sure that the email is unique and follows basic email format by using format: {{with: URI::MailTo::EMAIL_REGEXP}
. It uses regex to do a basic checking of alphanumeric characters, an @ symbol, and the like. In the notes below that validation I wrote out the long version of that regex. I also wanted to make sure that the url for each user was their username instead of the user_id, so I put my slug methods here.

On the topic of validations, I also used rack flash for my error messages. To use rack flash, in your gemfile add gem 'rack-flash3', '~>1.0', in your environment file add require 'rack-flash', and in your application controller configure statement add use Rack::Flash, :sweep => true. I added the flash messages in a lot of my controllers. Here is one example:

   patch '/user/:slug' do
       user = User.find_by_slug(params[:slug])

       user.update(params[:user])
       #binding.pry
       if user.save
           flash[:message] = "Your account has been updated."
           redirect to "/user/#{user.slug}"
       else
           flash[:error] = "#{user.errors.full_messages.join(", ")}"
           redirect to "/user/#{user.slug}/edit"
       end
   end
Enter fullscreen mode Exit fullscreen mode

I wanted to have success messages which are my flash[:message] and error messages flash[:error]. In the above example if the user account is updated a message will flash saying “Your account has been updated.” If there was a problem with the update, then the error message based on that specific error will flash. In this case it would most likely be about the format of the email, the password length, or an already existing username. In my layout.erb file I added:

<% if flash[:error] %>
  <div class="alert alert-danger" role="alert">
   <%= flash[:error] %>
  </div>
  <% end %>

 <% if flash[:message] %>
   <div class="alert alert-success" role="alert">
   <%= flash[:message] %>
  </div>
  <% end %>
Enter fullscreen mode Exit fullscreen mode

​​I wanted messages with a color background so they are very obvious, but if you don’t add the if statements, the color band will always show. My repo can be viewed here. I would love any feedback on this app. My app is very simple but I am happy with how my first attempt at an app turned out.

Discussion (0)