Building a Ruby on Rails (RoR) API Part I: Introduction

Building a Rails API for tracking agri-food products from farm to table, with detailed project insights!

  ·   6 min read

For a recent project I had to build up a backend as an API using Rails. I’ve learned a lot doing it, but coming up with it has been a process of reading several pages, tutorials, blogs, official documentation, and more… So I’ve decided to summarize what I’ve done for my project in an attempt to documentate the whole process for future references and let others know from my experience, and why not? Get a feedback.

The problem

The API comes as a proposal for the final project of Software Engineering. This is about a system able to allow real time tracking to implement an Agri-Food Traceability Application. The idea is to gather as much as possible information about agri-food products from their inception to the hands of the final consumer.

The dynamic is as follows, a producer has a series of products they made. During the process everything is logged into a CropLog, thus allowing to know how the making process was. The producer sells their goods to warehouses that buy from them and/or other producers. Then a warehouse can sell to smaller warehouses, until the product reaches the final user hands.

The idea is to offer the final user all the information about the product through a simple website.

Preliminars

For starters these are the versions of ruby and Rails at the time of writing this post:

1$ ruby -v && rails -v
2ruby 2.4.0p0 (2016-12-24 revision 57164) [x86_64-linux]
3rails 5.1.0.rc1

Then I’ve started my API generating a new Rails app as an api (--api) without default testing (-T). As you can see the DB is PostgreSQL:

1$ rails new shimizusuntou -d postgresql --api -T

About the gems

Now, let’s talk about the gems I’ve installed to get the project working:

  • rack-cors is a Rack Middleware for handling Cross-Origin Resource Sharing (CORS), which makes cross-origin AJAX possible.
  • rack-attack is a Rack middleware for blocking & throttling
  • active_model_serializers is a gem that helps you define the final structure of your json responses.
  • has-scope is a Map incoming controller parameters to named scopes in your resources
  • rqrcode_png Uses rQRCode and chunky_png to produce .png images of QR codes in pure Ruby
  • apipie-Rails is a Ruby on Rails API documentation tool.
  • rspec-Rails is a testing framework for Rails 3.x, 4.x and 5.0.
  • hirb is A mini view framework for console/irb that’s easy to use, even while under its influence. Console goodies include a no-wrap table, auto-pager, tree and menu.
  • factory_girl_rails is a fixtures replacement with a straightforward definition syntax.
  • shoulda-matchers is a Collection of testing matchers extracted from Shoulda http://matchers.shoulda.io
  • faker is A library for generating fake data such as names, addresses, and phone numbers.
  • database_cleaner provides Strategies for cleaning databases in Ruby. Can be used to ensure a clean state for testing.

Initial configurations

Now, let’s review how this gems are setup in the Gemfile:

 1source 'https://rubygems.org'
 2
 3git_source(:github) do |repo_name|
 4  repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include?("/")
 5  "https://github.com/#{repo_name}.git"
 6end
 7
 8
 9gem 'Rails', '~> 5.1.0'
10gem 'pg', '~> 0.18'
11gem 'puma', '~> 3.7'
12# Use ActiveModel has_secure_password
13# gem 'bcrypt', '~> 3.1.7'
14gem 'rack-cors'
15gem 'rack-attack'
16gem 'active_model_serializers'
17gem 'will_paginate', '~> 3.1.0'
18gem 'has_scope'
19gem 'rqrcode_png'
20
21gem 'apipie-Rails', '~> 0.5.0'
22
23group :development, :test do
24  # Call 'byebug' anywhere in the code to stop execution and get a debugger console
25  gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
26  gem 'rspec-Rails', '~> 3.5'
27  gem 'hirb'
28end
29
30group :test do
31  gem 'factory_girl_rails', '~> 4.0'
32  gem 'shoulda-matchers', '~> 3.1'
33  gem 'faker'
34  gem 'database_cleaner'
35end
36
37group :development do
38  gem 'listen', '>= 3.0.5', '< 3.2'
39  gem 'spring'
40  gem 'spring-watcher-listen', '~> 2.0.0'
41end
42
43gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]

The first step we have take is to initialize the rspec configuration, generating three files .rspec, spec/spec_helper.rb, spec/rails_helper.rb

1$ rails generate rspec:install

Now, in order to have our rspec tests working we have to provide a way to build dummy objects right away from fake data, but a kind of data that represents very well how our real object will look like. For that we have to setup all the support for factories. First we have to create the folder where our factories will live.

1$ mkdir spec/factories

In order to have rspec understanding how to use the factories we have to modify the spec/rails_helper.rb file.

 1require 'database_cleaner'
 2
 3Shoulda::Matchers.configure do |config|
 4  config.integrate do |with|
 5    with.test_framework :rspec
 6    with.library :Rails
 7  end
 8end
 9
10RSpec.configure do |config|
11  config.include FactoryGirl::Syntax::Methods
12  config.include RequestSpecHelper, type: :request
13  config.before(:suite) do
14    DatabaseCleaner.clean_with(:truncation)
15    DatabaseCleaner.strategy = :transaction
16  end
17  config.around(:each) do |example|
18    DatabaseCleaner.cleaning do
19      example.run
20    end
21  end
22end

That’s the setup to use rspec and how the testing suite should interact with the testing database.

The model

First I have to talk about my idea of what the model is in the DB:

db_model
DB Model

This is how the actual model is visualized in Rails:

rails_model
Rails Model

First I want to talk about the simple models:

  • Product: information about a product.
  • Container: information about the containers for a product (basket, pack, etc).
  • Crop: general information about a crop, has a relationship to Product, Container.
  • CropLog: details for a Crop.
  • Route: binding between two Places.
  • RouteLog: details for a specific Route, where the humidity, temperature, latitude and longitude are registered

Now it’s time to talk about the more complex models:

First, I have the Package model. This one tells how many Containers of certain Product are shipped from one Place to another through a Route. A Package can have another Package as parent meaning that the original one has been divided into smaller amounts.

As it can be seen from both pictures there is a model User that is meant to hold users information, however the User model is never used as it is but I distinguish between two types of users: Producer and Warehouse. Thus, making an inheritance relationship between them.

Finally, the most complex model is Place. They belong to, either a Produce or a Warehouse, so the model has to be defined as a polymorphic relationship to those. They also have many origins or destination in a Route.

I don’t pretend to show all the details of the models, but I’ll point the key parts of each model so a better understanding of what I’ve explained above can be reached. By the time I’m writing this article, I might have modified the code, so all the source of truth should be the official repository at GitHub.

In the next article for this series, I’ll talk about tests and further configurations. Stay tuned!