Let’s Build a Sinatra App: Using MVC, CRUD and RESTful Routing

What is Sinatra, MVC, CRUD and RESTful routing?

According to Wikipedia, Sinatra is a free and open source software web application library and domain-specific language. It is a simple option to build a web application quickly using the Rack web server interface.

Model-View-Controller is a pattern used in popular programming languages to develop distinct, yet interconnected parts of an application. Simply put when a user interacts with a web application, it begins a cycle of the controller manipulating the model, updating the view and on until the user logs out of the current session.

Lastly, CRUD and RESTful routes round out how a user interacts with a web page. CRUD stands for Create, Read, Update and Delete; REST is Representational State Transfer.

Imagine going to Twitter.com for the first time and signing up as a new user; a new user is created. This is the “create” in CRUD and the signup page used a get HTTP request to render the sign up form and a post HTTP request to create the user, then direct the user to their page. The same for any tweets the user creates. Every time a user goes to their page to view their tweets, they are “reading” their page. When a user “updates” or “deletes” a tweet, a patch or a delete request is sent to the server. For more information on how CRUD and RESTful Routing works, check out the Ruby on Rails API guide.

Getting Started

Before I coding my application, I thought about what I wanted to build, the models to use (along with their attributes and associations), determine the minimum viable product to get the application working and finally stretch goals to make the application user-friendly, efficient and DRY.

I decided to build a web app for tech conferences because I’ve attended quite a few conferences since I’ve been on my coding journey and they all had a different focus/purpose. In the application, a user can sign up/log in and view other conferences added by other users as well as their own. Only a user can edit/delete a conference that they created. A user has many conferences and a conference belongs to a user.

I also used validations to confirm a user’s email/password and made sure they entered the requested information to sign up or log in. Conferences need a name, location, date, and category (women, diversity, coding, hackathon, open source, mixed, etc.)

Now I was ready to code. I installed the corneal gem that a former Flatiron School student built as a Sinatra application generator for future users who needed a simple Sinatra web application. Once I had the gem installed, I generated the app by running corneal new APP-NAME then bundle install. I ran shotgun in the terminal to make sure I installed everything correctly.

Once I had the app going, I initialized my repository on GitHub and connected the local repository on my computer to GitHub.

Note: commit early and often. When building a project, committing code that you want to keep is not only helpful for future debugging but also to memorialize any meaningful changes you want to keep.

I made 93 commits for this project!

Build ActiveRecord Migrations

The cool thing about the corneal gem is that you can run commands to generate a model or scaffold the entire MVC structure including a migration file. But I chose to go the local route and build the migrations, models, views, and controllers.

I created a migration each for the users and conferences tables. Each user has a name, email, and password digest. Each conference has a name, location, category, date, and a foreign key column for the user_id because a conference belongs to the user.

Then I built the user and conference models with the appropriate associations. The user model has_many :conferences and has_secure_password, which is the bcrypt macro to validate the password. The conference model belongs_to:user.

Then I created a seed file for the database so I had some users/conferences to work with. Finally, I migrated and seeded the database.

Note: if you need to clear out your database, delete development.sqlite file and re-run db:migrate and db:seed to start fresh.

Build controller actions and views one by one then view in shogun.

Always add dynamic routes like ‘/conferences/:id’ after ‘/conferences/new’ because the controller action will think that the ‘/conferences/new’ is a dynamic route and will not read that route first when you want to create a new conference.

As new routes are added, it’s good to exit and re-run shotgun to make sure changes take effect.

I think one interesting thing to building this app was making sure I iterated to show specific details in the show.erb file for the user. I had to go back and look at other code I created, to make sure I had the user who was logged in and then iterate over the user’s conferences collection to get the name of the conference and other details I wanted to display.

I added a few stretch goals added to this project:

  • ActiveRecord validations in the user model to validate the presence of name and email and that the email was unique.
  • Flash messages and errors logic were added to the layout.erbfile to display when a user correctly signed up or logged in or did not enter the correct information.
What a flash message and flash error looks like in the controller action.
  • I added the gem "sysrandom"for the session_secret to secure the session id. Read more here about using this gem as well as here about securring sessions.

The biggest challenge with this project

So, I wanted to have a date column in my table. When I first seeded my database, the first issue I ran into is that I had an Invalid octal digit (a leading zero: 07-11-2019). I then removed the leading zero for the dates that had one digit month or day.

date as an integer in the seeds file
date as a fixnum in the browser

After more googling and inquiring, I learned I needed to require the date gem. This gem runs on Ruby version 2.5.0 (I had to update the version on my project from 2.3.0 to 2.5.0); I then needed to change the bundler version to work with the updated ruby version in my project.

After all of this work, I still had some trouble getting the date to display the way I wanted. So I made a change to the conferences table. I created a new migration for change_column_date to change the date to a text datatype. I also changed the dates in my seed file to a string ‘1/10/2019′.

In the SQLite documentation, there is a list of accepted data types and date happens to be one of those that is not really supported, so you have to enter it as text, real, or integer.

Once I updated the column datatype to text, the app allowed the date to be entered as a string works in which the user can type the date as 1/10/2019 or 1-10-2019.

The last update I made to the date format was to change the input type in the views from text to date in the new.erb and edit.erb files so there’s consistency in entering and viewing dates.

Input type=”date” name=”date” in new.erb file

Stretch goals for updates to this project

Some of the stretch goals I thought of for my project are to:

  • Bootstrap CSS to make the website more user-friendly.
  • Google maps gem to show conference locations
  • Testing with Capybara and RSpec

Last thing to note, read the documentation for any programming language, framework, gems, etc., before coding your application. Had I read the SQLite3 docs, I would have known that the date datatype is not supported and to enter the date as text.

Like they say, “You live and you learn.”

Check out the GitHub repo: https://github.com/AlwinaO/conferences

Building a CLI Data Gem App and Scraping with Nokogiri

Can I say wow??!!! I just built my first project, a command line interface app that scraped data from a website and provided a list of key information on the page.

Slow and steady wins the race, right? I hope so because I really took my time to build this project.

So why did I choose to scrape a website that listed the best ice cream parlors?

Ice cream is amazing!!! There are literally tons of options, from salted caramel and matcha green tea to the old faithfuls, vanilla and chocolate.

Mikey Likes It

Naturally, I wanted to know where to find the best ice cream parlors in NY and the world.

Check the website before scraping

Before I could start scraping websites, I used curl to check websites for excessive javascript, etc. If there was a ton of javascript for Nokogiri to crawl through, it might be challenging to find the CSS selectors I needed for my code.

Local environment vs. Learn IDE

I chose to work in the local environment versus the Learn IDE.  I was a bit confused setting up the app via the IDE, so I decided this was a perfect time to be acquainted with my terminal, Git/GitHub, etc.

It was definitely challenging working in the terminal but once I learned a few things, I was on my way to coding my app.

Since I’m a Mac user, I used the following steps to make sure my computer was set up properly. Please note that Mac computers come with Ruby and other programming languages pre-installed, which means you can’t easily make edits to the software.

Step 1: Download Homebrew

Mac computers are great but they don’t come with all the goodies that you find in Homebrew, “The missing package manager for macOS”.

Step 2: Install packages

Scroll down to and click on Homebrew packages then click on browse all formulae.

CTRL-F for rbenv, which is the Ruby version manager. Once that is installed, you can install the version of Ruby you would like on your computer. I installed Ruby 2.30.

Install bundler-completion for bash in the terminal to recognize Bundler.

Once I had these installed, I was able to work with bundler to install other gems I needed like Nokogiri and Pry.

Install Bundler and Build Your Gem

Bundler provides simple documentation for installing bundle on your Mac.

I also used this video to see how it was done to navigate through potential hiccups I would see in the terminal. When I ran bundle gem best_icecream --test --coc --mit, all the files I needed were created and the repository was set up.

Once I built my gem, I was ready to link my gem to my GitHub repository and begin building my CLI app.

Building the CLI Class

I followed Avi’s Daily Deals CLI Gem walkthrough video and made adjustments to my code accordingly.

What I like about Avi’s walkthrough video is that he explains the thought process behind the steps he took to build the CLI app. I could start by scraping the website but if my command line isn’t working, I won’t know where to begin to fix any problems that occur.

I built the CLI class by adding the line BestIcecream::CLI.new.call to ./bin/best-icecream. I stubbed out the CLI class with some fake data to make sure the interface was working. I added four methods: call, list_parlors, menu, and goodbye. The call method collaborated with the BestIcecream::Parlor.scrape_parlors and included the other three methods.

The first level of the CLI was list_parlors and the second level required input from the user to input either a number, type “list” or “exit”. Anything else would ask the user to enter a number on the list, type “list” or “exit”. Of course, typing “exit” would exit the program.

The Parlor Class

The Parlor Class has two functions: to instantiate new objects and scrape the website with Nokogiri. I built the self.scrape_parlors method first.

As I stated previously when I curled the website, a lot of javascript loaded. Nokogiri has to crawl this to find the CSS selectors I would use in the project.

I used binding.pry to find the selectors needed for the name, location, phone, and url objects.

Through iteration and the pry console, each selector was located. I worked with a tech coach to further organize the code, adding in an initialize method to instantiate new objects with name, location, phone, and url; self.all and save, and the class variable @@all to hold all the instances of the class.

Conclusion

This was definitely a challenging project. The hardest part was finding the best website to scrape.

After I finished the CLI class and began to build the Parlor (previously called the Icecream class), I had some difficulty scraping my original website. I realized a lot of the websites I previously reviewed, listed the name and location on the same line, so I found ny.eater.com, which had the name and location on different lines and different selectors.

This website was also challenging to scrape because I couldn’t retrieve the phone selector and I wasn’t able to retrieve all 16 ice cream parlors listed. The tech coach (David K.) I worked with, suggested we see where the code was broken or malformed, so we could separate the “cards” we needed. Once we did that we sliced the last card in the iteration method.

Possible re-factor for this CLI app would be to create a scraper class and pull in that information to the Parlor class that finds and instantiates new objects.