This post originally appeared on Engine Yard.
It also appeard on the Quick Left Blog.
In the modern web, API-based projects are becoming the norm. Why? For one thing, APIs are necessary to serve Single Page Applications, which are all the rage right now. From a business standpoint, APIs give companies a new way to charge others for access to their data. If you are part of a company that offers such a service, a great way to generate interest in your API is to offer a Ruby gem that makes fetching and consuming your data easy for Ruby developers.
Today, we’ll take a look at how to wrap an imaginary API in a new Ruby gem and share it with the world. If you want to follow along at home, you can clone the project from my GitHub account.
Let’s pretend we have an application called Ben’s Benzes that serves data about cars for sale. We’re exposing a RESTful API so that developers from other companies can serve our car data on their websites. For our first iteration, here are the routes we’ve set up:
Get info about a certain car currently for sale:
Get all the cars that are currently for sale:
Setting Up the Gem
We’ll be using bundler, so begin by making sure you have that installed (you probably do). We’ll create the gem by running
bundle gem <gem-name> from the command line. I’m going to use
benzinator as the name of my gem. That name is now taken, so you’ll have to come up with your own. Open up the project directory and you should see:
1 2 3 4 5 6 7 8 9
Let’s open up
benzinator.gemspec and do a little configuration. Update the following lines with your name, email and a summary and description of the gem.
1 2 3 4 5 6
1 2 3 4 5 6 7 8
Writing a Test
We’ll need to make a
test folder, and put a
test_helper.rb into it. We’ll use the helper to pull in our gem and testing dependencies. We’ll also configure VCR and Webmock, which we’re using to stub out our server responses so that our gem isn’t dependent on access to the API for testing. See the VCR documentation for more about how this works.
1 2 3 4 5 6 7 8 9 10
Don’t forget to create the
test/fixtures folder so VCR has somewhere to put the fixtures. With that done, we’ll write the first test for our gem. Our goal is to create an object called Benzinator::Car that exposes
#find methods to wrap calls to our API. Let’s start by making sure that object exists:
1 2 3 4 5 6 7 8
Creating The Wrapper Model
If we now run
ruby test/car/car_test.rb, we get this error:
uninitialized constant Benzinator::Car. Looks like it’s time to write some code:
1 2 3 4 5
We’ll also need to require it in
1 2 3
Now if we run the test, it passes. Let’s write another one to make sure that our
Benzinator::Car model can give back the data for a car. Let’s imagine that an API call to
http://www.bensbenzes.com/api/v1/cars/active/68 returns this JSON object:
1 2 3 4 5 6 7 8 9
We’ll want to make sure that our Benzinator::Car object has getter convenience methods all of the fields shown for each car. Given these API results, we could write a test like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
Running this test, we get
undefined method 'find' for Benzinator::Car:Class. Let’s go define it.
1 2 3 4 5 6 7 8 9 10 11 12 13 14
Now the test says we forgot to make it a Benzinator::Car model:
We can fix that, and make the attributes into getters at the same time.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
That should take care of it. Now to test the
Let’s imagine that a call to
http://www.bensbenzes.com/api/v1/cars/active responds with an array of 64 cars, with this Honda being the first one. We can rely on the API call giving back 64 cars today, but we hope there will be 6000 listed tomorrow. This is why we’re using VCR to save the result of the call as a fixture.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
Running this, we get
undefined method 'all' for Benzinator::Car:Class. Let’s define it:
1 2 3 4 5 6 7 8 9 10 11 12
Sweet success! The tests pass!
Publishing and Using Our Gem
Now that our gem works, we can publish it to RubyGems. This is a pretty easy process. First, we’ll bump the version to 1.0:
1 2 3 4
Then we can bundle it up by running
gem build benzinator.gemspec. This will create a
benzinator-0.1.0.gem file in our project directory. To publish it , all we have to do is run
gem push benzinator-0.1.0.gem. You’ll be prompted for your RubyGems username and password, which you’ll need to create if you don’t have an account yet. After you enter your credentials, your gem is live!
Now anyone can use our gem in their Ruby projects. All they have to do is add it to their Gemfile with
gem 'benzinator', and run bundle.
Winning! Now the whole wide world can access the Ben’s Benzes API from their Ruby project, with convenience methods to make things easier to work with.
For our next iteration, we could get a lot more in depth. We might want to add the ability to create, edit or destroy cars. If we decided to that, we might first build sort of authentication process into the gem. Once users have gotten a taste of our data and rely on it, our API might get so popular that we need to limit the number of API calls a user can make per day. We could then write subscription service to allow users greater access to your data at a cost.
One last note: you might want to use this technique to wrap someone else’s API, too! If you’re using a service that doesn’t offer a gem for its API, you can always write one and release it as open source!