I was going to start this post with a clever reference to Sinatra and how he was such a 'cool cat' but I sort of winced as I started typing that out so I'll skip it.
Anyway...this week at Flatiron, we've started to take the training wheels off and learn about connecting the applications we develop to the real, live Internet. I say 'take the training wheels off', but really we're putting them on because Sinatra does all the heaving lifting in the client/server model.
After many days of wrestling with Rack and Rack's middleware, the introduction of Sinatra was a relief, even as I struggled to understand exactly how it operates. To help myself out, I decided to develop a simple Sinatra app that meets one of society's greatest needs--easy access to photos of cats (obviously).
okay, maybe not this cat...
First things first...
Defining our tasks
What do we need to do in order to deliver as many cats as possible at the touch of a button?
-
Get all the cats (is there a cat API? Is that even a question?)
-
Create our controller--a class that will inherit from Sinatra::Base and be responsible for connecting the raw cat data to our view (the page that the user actually sees).
-
Create our view--the erb template that we will fill up with random cat pics.
-
Write our config.ru file, the file that runs our application (i.e. gets cats, funnels them into the view page and loads the page on the browser).
In order to understand the process that we're about to embark on, we need to understand the concept of Model-View-Controller.
The model is responsible for our actual data. It is a class that insanstiates the objects that we want the viewer to interact with. For example, if you were creating a blogging app, the model would be responsible for creating individual posts.
The controller is responsible for delivering the objects created by the model to the viewer. To stick with our blogging example, the controller is responsible for defining the URL that a viewer types into their browser when looking for their favorite blog and funneling the data from the model into the view page for the user to look at and interact with.
The view is written in ERB--a way of effectively combining HTML and Ruby. The view creates the page that the user sees when visitng the URL. This might be an archive of all the blog posts or an individual post, for example.
Okay, let's dive into creating "Cat Fancy--get all the cats from your browser, to your eyeballs, right away".
Getting all the cats
Thanks to the cat api, (where every day is 'caturday', according to their website), I was able to access a link that will generate 20 random cat pics with every reload of your browser, as XML, in the form of a Nokogiri document.
I defined my model to be the GetCats class and scraped the api, storing an array of links to 20 random cat pics.
class GetCats
URL = "http://thecatapi.com/api/images/get?
format=xml&results_per_page=20"
def scrape
get_cats = Nokogiri::HTML(open(URL))
cat_party = get_cats.css("url").children.collect
{|cat| cat.text}
end
end
Controlling the cats
Then, I defined my controller class. Let's take a look at that code, then we'll walk through how it functions:
require_relative 'config/environment'
class App < Sinatra::Base
get '/' do
connection = GetCats.new
@cats = connection.scrape
erb :cats
end
end
First, we need to require our environment file which will (via Bundler) make sure all of our gems are loaded, as well as our models (in this case, our only model is the GetCats class).
Then, we need to make sure our class inherits from Sinatra base. This will give it all of Sinatra's capabilities.
Next, we define our route. A route is an HTTP method (i.e. GETS or POST) that is paired with a specific URL pattern. When your Sinatra application receives a request from the client, it will match the route from the request to a specific controller action defined in your block.
In this case, my controller class is saying: If the client sends a GET request with a URL ending in '/', do the following: create a new instance of the GetCats class, call the scrape method on that instance to return an array of cat pic links and render the contents of the view file called "cats.erb".
Cat party
Lastly, we'll create our view file, cats.erb.
< p >Every moment is purrrrfect with these cats to keep you company < /p >
<% @cats.each do |cat| %>
< p > < img src="<%= cat %> / > < /p >
<% end %>
Recall that the @cats
instance variable was actually defined within the get
method of our controller. Sinatra allows you to access it here in your view file and operate on it to render the content of your page.
We're almost done. We need to create our config.ru file. This is the file that gets read first when your application recieves a request from the client via the server. The config.ru file instantiates a new instance of your controller class which has the effect of, in turn, instantiating your model class, connecting that data to the view page and routing the client's request to the appropriate view page based on their URL.
Here's my config.ru file:
require './config/environment'
run App
And that's it! If you run rackup
or shotgun
from your terminal inside the root directory of this application, your browswer will display 20 random and glorious cats every time you hit 'refresh.' Fork it, clone it, play with it, improve it and let me know your feedback!