What are we building?
We’ll create Noterizer, a simple Rails application that requests XML from externally hosted endpoints and re-renders the XML data as JSON at a single URL. To assist in this post, I’ve created NotesXmlService, a basic web application that serves two XML-based endpoints:
Why is this necessary in a real-world scenario?
Fronting external endpoints with an application like Noterizer opens up a few opportunities:
- Noterizer’s endpoint could reformat the externally hosted data to better serve its own clients’ data formatting preferences.
- Noterizer’s endpoint is a single interface to the data; multiple requests are abstracted away by its backend.
- Noterizer provides caching opportunities. While it’s beyond the scope of this series, Rails can cache external request data, thus offloading traffic to the external API and avoiding any terms of service or rate limit violations imposed by the external service.
For this series, I’m using Mac OS 10.9.4, Ruby 2.1.2, and Rails 4.1.4. I’m assuming some basic familiarity with Git and the command line.
Clone and set up the repo
I’ve created a basic Rails 4 Noterizer app. Clone its repo, enter the project directory, and check out its tutorial branch:
$ git clone http://github.com/mdb/noterizer && cd noterizer && git checkout tutorial
Install its dependencies:
$ bundle install
Set up the test framework
Let’s install RSpec for testing.
Add the following to the project’s Gemfile:
gem 'rspec-rails', '3.0.1'
$ bundle install
There’s now an rspec generator available for the rails command. Let’s generate a basic RSpec installation:
$ rails generate rspec:install
This creates a few new files in a spec directory:
│ ├── rails_helper.rb
│ └── spec_helper.rb
We’re going to make a few adjustments to our RSpec installation.
First, because Noterizer does not use a relational database, delete the following ActiveRecord reference in spec/rails_helper.rb:
# Checks for pending migrations before tests are run. # If you are not using ActiveRecord, you can remove this line. ActiveRecord::Migration.maintain_test_schema!
Next, configure RSpec to be less verbose in its warning output; such verbose warnings are beyond the scope of this series. Remove the following line from .rspec:
The RSpec installation also provides a spec rake task. Test this by running the following:
$ rake spec
You should see the following output, as there aren’t yet any RSpec tests:
No examples found. Finished in 0.00021 seconds (files took 0.0422 seconds to load) 0 examples, 0 failures
Note that a default Rails installation assumes tests live in a tests directory. RSpec uses a spec directory. For clarity’s sake, you’re free to delete the test directory from Noterizer.
Building a basic route and controller
Currently, Noterizer does not have any URLs; we’ll create a single/notes URL route.
Creating the controller
First, generate a controller:
$ rails g controller notes
$ git clean -f
Now, open config/application.rb and add the following generator configuration:
config.generators do |g| g.helper false g.assets false end
Re-running the generate command will now create only the desired files:
$ rails g controller notes
Testing the controller
Let’s add a basic NotesController#index test to spec/controllers/notes_spec.rb. The test looks like this:
require 'rails_helper' describe NotesController, :type => :controller do describe '#index' do before :each do get :index end it 'successfully responds to requests' do expect(response).to be_success end end end
This test currently fails when running rake spec, as we haven’t yet created a corresponding route.
Add the following route to config/routes.rb
get 'notes' => 'notes#index'
The test still fails when running rake spec, because there isn’t a proper #index controller action.
Create an empty index method in app/controllers/notes_controller.rb
class NotesController < ApplicationController def index end end
rake spec still yields failing tests, this time because we haven’t yet created a corresponding view. Let’s create a view:
$ touch app/views/notes/index.json.jbuilder
To use this view, we’ll need to tweak the NotesController a bit. Let’s ensure that requests to the /notes route always returns JSON via a before_filter run before each controller action:
class NotesController < ApplicationController before_filter :force_json def index end private def force_json request.format = :json end end
Now, rake spec yields passing tests:
$ rake spec . Finished in 0.0107 seconds (files took 1.09 seconds to load) 1 example, 0 failures
Let’s write one more test, asserting that the response returns the correct content type. Add the following to spec/controllers/notes_controller_spec.rb
it 'returns JSON' do expect(response.content_type).to eq 'application/json' end
Assuming rake spec confirms that the second test passes, you can also run the Rails server via the rails server command and visit the currently-empty Noterizer http://localhost:3000/notes URL in your web browser.
In this first part of the series we have created the basic route and controller for Noterizer, which is a basic example of a Rails application that fronts an external API. In the next blog post (Part 2), you will learn how to build out the backend, test the model, build up and test the controller, and also test the app with JBuilder.
About this Author