Ruby is an awesome language for getting things done in a simple, to-the-point sort of way. So as one of my side projects I’ve decided to make a simple weather app (mobile web app) to play with a few APIs and stretch my skills. Coming from a PHP background and having been working mostly on backend technologies for the past year there was a lot I needed to learn to get this working. Follow along and I’ll walk you through the steps of how to create a weather app using AngularJS as a front end that’ll grab data from a Sinatra API.
Part 1: Setting up your environment (Hello World!)
This is the first in a series of posts on creating a RESTful API and API client using Ruby and JavaScript. Part 1 of the series will take you through the steps you need to get a simple API written in Sinatra to respond with ‘Hello World!’.
Choosing the right tools
Before you start any project you need to choose the right tools for the job. To do that you have to start off thinking about your needs. This is what we’ll need in this project and why:
Sinatra – Sinatra is a DSL (domain-specific language) that lets you create web applications in Ruby. There are two main reasons I chose Sinatra. First, despite knowing PHP inside and out I wanted to do this in Ruby since I knew I could do it quicker and with less code to maintain. The other question that will come up is “why didn’t you use Rails”? Rails is too powerful for what we need in this case. We only need a couple of routes and a few simple functions to get the weather so there’s no reason to run a full blown Rails app when we can contain our entire API to a couple of script files. Sinatra is a very light and easy framework to work with.
Angular JS – We’ll be using Angular for the front end. Because our app is going to be focused on mobile users we want it to be quick and responsive. If we can create a very small front end then we can have the app download once and from then on whenever an API call is made the client will only need to download a small bit of JSON data. Using Angular means we won’t have to deal with page reloads and downloading new markup and content each time. It’ll be quick, responsive, and even work offline.
The APIs we’ll be using are IPinfo.io, Forecast.io, and the Google Maps API. We’ll be using these for Geolocation and to grab the weather.
Set up your environment
Before we begin let’s set up our environment. We’ll be working with a codebase for our API and one for our front end client. All of this should also be in version control. I use Git for version control. So first let’s set up our directory structure. On my local machine (running OS X 10.9) I put all my development work in the ~/Sites/
folder. Create a directory structure that looks like this:
1 2 3 4 5 6 7 |
|
You do this by opening the terminal and running:
1 2 3 |
|
Setting Up Version Control
I’m not going to walk you through setting up the Git repositories here but I recommend that each folder inside of weatherapp
get its own git repository or become submodules of a main weatherapp.git
repo. I started off with the weatherapp
folder being a Git repository but soon found that I wanted the api
and client
folders to be maintained separately for logical reasons and for deployment purposes as well. Your own opinions and mileage may vary on this.
Create the API
Before we get into the fun front-end stuff we’re going to create our Sinatra API. Drop into your api
folder and get your Ruby environment ready.
Prepare your Ruby environment
In this case I’m going to be using Ruby 2.0 and some Rubygems. I always create a new Gemset whenever I start a new Ruby project so I can be sure that the Gems I install and use will not conflict with Gems needed for other projects. I use RVM which is quite common. If you’re not using RVM to manage your Ruby versions and gemsets then you can still follow along but I can’t make any guarantees about the reliability of the code.
Manage Ruby Version and Gemset
First, let’s make sure we have the Ruby we want installed and then create the Gemset we need (a gemset, again, is just a set of Rubygems that are separated from the others on your system by a name you assign them – they’re just used so you can maintain different versions and collections of Rubygems on a per-project basis).
First, install Ruby 2.0 with
1
|
|
That command will install Ruby 2.0 on your system. Once that’s finished we’ll create a new Gemset with
1
|
|
Great. Now we have the right Rubygemset installed and we’re using Ruby 2.0. Now, we’ll need to remember to tell RVM to use Ruby 2 and our “weatherapp” gemset each time we work on this project. What a pain. So instead of having to remember, let’s create a couple of files that will automatically switch us to using Ruby 2 and the correct gemset. Run the following commands in your terminal to get started:
1 2 |
|
These two files tell whichever program you use to manage multiple Ruby versions (RVM in our case) which version of Ruby to use when working in this folder and which gemset to use.
In order to know what to put in our .ruby-version file we need to know the exact version of Ruby we have installed. The best, and most accurate, way to do this when using RVM is to run rvm list
. This will give you a list of installed Rubies and show which one you are currently using. It will look something like this:
1 2 3 4 5 6 7 8 |
|
The key at the bottom shows you how to read the output. In this case my default Ruby would be ruby-1.9.3-p286
and the one I’m currently using is ruby-2.0.0-p247
. If the command shows that you aren’t currently using a version of Ruby 2.0 or higher, then copy the ruby from the list and paste it into the command shown below. In my case, the command would be:
1
|
|
The “-pXXX” is the patch level of the current Ruby. You don’t need to know what that means right now but what you do need to know is that multiple versions of the same Ruby can exist at the same time. You can have multiple Ruby 2.0.0’s installed each with a different patch level. Because of this, you’ll want to be as specific as possible when copying the Ruby version into your .ruby-version
file. In my case I would open the file and on the first line paste in:
1
|
|
Now save that file and any time you cd
into that directory you’ll automatically be switched to using that Ruby. Make sure to enter the version of Ruby you have installed which is listed in the output of rvm list
. Do not just blindly use the one I’m using. At this point, when you’re ready to run your app on a different server you’ll need to make sure that exact version of Ruby is installed. This allows us to avoid problems caused by small differences in the different Ruby versions. To do this, you’d simply copy the contents of your .ruby-version
file and run rvm install [your version of ruby here]
on the server and half the battle of making sure your development and production environments match up is taken care of.
Now we need to make sure that the app uses the same gemset no matter where it’s run. To do this you’ll need to open up the .ruby-gemset
file we created earlier and type in weatherapp
. Now save the file.
The line weatherapp
corresponds to the name of the gemset we created earlier and are currently using. When you deploy your app to a server you’ll need to make sure you create a new gemset of the same name. To do this you’ll first need to switch over to the version of Ruby your app required (or install it if it doesn’t exist) then run rvm gemset create [gemset name]
. If the Ruby version is installed on the server and the gemset already exists then your app will automatically use them when you deploy it.
Back on our local machine, the .ruby-gemset
and .ruby-version
files allow us to do the same thing. Now each time you cd
into your project’s folder you’ll end up automatically being switched to the correct version of Ruby and the corresponding gemset.
Now that all the housekeeping is taken care of, let’s start some real coding.
Creating the Sinatra app
Our Sinatra app will connect to several API’s and return data from them. In order to create this API we need to be able to handle incoming requests to a web server, parsing JSON, and sending out requests to an API. To do this we’ll install three Rubygems for the app’s functionality: Sinatra, RestClient, and JSON. We’ll also install a couple that’ll allow us to work more easily: Thin and Rerun.
We’ll want to manage these gems in a way that makes it easy to set up the app in different environments like our server and our local machine. To do this we’ll first create a Gemfile. A Gemfile is file that lists all the dependencies your app requires. It allows you to track the Gems needed and their versions across multiple machines. It does for Rubygems what RVM’s .ruby-version
and .ruby-gemset
files do for creating a consistent Ruby environment. To create the file run touch Gemfile
then open it and paste in the following code:
1 2 3 4 5 6 7 8 9 10 |
|
Before we continue let’s go through what each line does. The first line (source 'http://rubygems.org'
) declares where we should look for the gem files we’re going to install. Then each line beginning with gem 'gem-name', 'version-number'
specify what gem to install and what version of it to install. See the block that begins with group
? That’s how we tell Bundler (the program that’s already installed with RVM) that the gems listed in that block are all related. In our case the block is for gems we only need to run on our local machine but won’t be needed when we deploy our app live. When we run the bundle
command on our server we’ll tell it to ignore the :development
block so we only get the gems we need in production.
Once you’ve saved the Gemfile
in the root of your project you’ll want to install the gems. Run bundle install
and all the gems listed in the Gemfile will be installed into the weatherapp
gemset. Thin is a webserver we’ll be using to run our API locally and Rerun will allow us to make changes to our Ruby code and have the changes reflected immediately without having to stop and restart our server.
The main app file
Okay, now I promise that all the housekeeping is taken care of. What you’ll want to do now is create a file called app.rb
in the root of your API folder. This will be the main file that runs our API. Open the file and paste in the following code:
1 2 3 4 5 6 7 8 9 |
|
The first 5 lines are comments. If you’re not familiar with Ruby, comments begin with a hash #
symbol. After that we have three require
statements. These statements tell the Ruby interpreter that we’ll be using code in the Sinatra, JSON, and RestClient gems as part of our app. We can also include separate Ruby files we’ve created too and we will later on.
Below the require
statements add the following:
1 2 3 4 |
|
The code above is the basis for what Sinatra is. What the get
block is doing is saying “When there is an HTTP GET request to the root directory (whateverDomain.com/) set the content type to JSON and then respond with a message saying ‘Hello World!’”. You could change get
to any HTTP verb like POST, PUT, DELETE, PATCH, or UPDATE but in our case we only need a simple GET request.
The .to_json
at the end of the last line in the block converts the string to JSON so that API clients can read it.
Want to see it in action? If you’re on a Mac or Linux you’ll have everything you need already. If you’re on Windows you’ll need to install cURL or use your browser for the next part.
In your terminal, still in the API’s directory, run rerun 'ruby app.rb'
. This will start the app. rerun
is the gem we installed earlier that automatically reloads our app when it detects changes to its files. The 'ruby app.rb'
part of the command tells Rerun that we want it to start and stop this command automatically. If we weren’t using Rerun we would have simply typed ruby app.rb
and the Ruby interpreter would have run our app. The only difference when using Rerun is that we don’t need to manually start and stop the app each time we change the code.
So now that the app is running open a new terminal window or tab and run the command curl -i http://localhost:4567/
. This command is using the cURL utility to fetch the contents of that URL (http://localhost:4567 is the host and port that Sinatra apps will run on locally). You should have gotten something like the following as a response:
1 2 3 4 5 6 7 8 9 |
|
That’s our API at work! It responded with what we told it to. Now, open up app.rb
again and make some changes. First, I want you to change Hello World!
to whatever your name is. Then, below the first get
block, add the following code:
1 2 3 4 |
|
Your app should still be running and the changes should be reloaded because of Rerun. So now, in the same terminal window you used for the first cURL request, run curl -i http://localhost:4567/
. You should get a response of “Hello {YOUR NAME HERE}”. That’s Rerun in action.
But what about that new block we added? What does that do exactly? I’m sure you could guess that it handles 404 errors but how? Well, not_found
is a special method in Sinatra for running blocks of code whenever you don’t have a route set up for the URL that someone asks for. You could redirect them to the homepage or run any number of different tasks here but in our case what we’re doing is once again setting the content type to JSON (because we’re running an API) and then responding with an error message. The halt 404
stops execution of any request block and responds with whatever you put after it. In this case we’ve written 404
which is how we tell Sinatra to respond with an HTTP status of 404. Everything that comes after the comma is the response itself. We just wrote a simple ruby string and made sure it was converted to JSON in the response.
To see the 404 block in action, run curl -i http://localhost:4567/bad-url
. You’ll get a reponse that looks like this:
1 2 3 4 5 6 7 8 9 |
|
To stop the server and end the Ruby program go to the terminal window that you started Rerun in and press Control + C at the same time. This will end the execution of anything running in that window.
Conclusion
At this point we have a very simple API written in Ruby with help from Sinatra that responds to a few URLs with JSON. In part 2 we’ll add on to the API and make it do a few things that are more useful. You’ll learn how to make external API calls, parse JSON, set some Sinatra settings, and learn about using before
blocks in Sinatra to set headers specifically meant to help in the creation of APIs. And remember, if you’re looking for a great place to host your code online while you learn, Digital Ocean is perfect for hosting experimental code for cheap. They bill hourly and you can get a VPS up and running for just $5 a month.
Comments