Exposing data using a REST service is an easy task which can be achieved using almost any programming language. In the past I’ve used ASP.NET MVC WebApi to achieve this.

But as soon as you’ve to run your service on different platforms, CLR isn’t the best option. Programming languages like Ruby and Python or Node.js are stronger when it comes to cross platform support.

I love the Ruby syntax and that’s why I’ll show how to build a small REST Service exposing tasks using Ruby’s Sinatra framework.Sinatra doesn’t require a fix or predefined project structure. I’d like to organise all the artefacts a little bit. That’s why I’ve chosen the following structure

First let’s review the model. I’ve built the model using ruby’s datamapper gem.

#models/task.rb

class Task
  include DataMapper::Resource

  property :id,           Serial
  property :title,        String
  property :completed,    Boolean
  property :description,  String
end

Next and perhaps most important part is of course the routing for the service.

# routes/tasks.rb
get '/api/tasks' do
  Task.all.to_json
end
	
get '/api/tasks/:id' do
  t = Task.get(params[:id])
  if t.nil?
    halt 404
  end
  t.to_json
end
	
post '/api/tasks' do
  body = JSON.parse request.body.read
  t = Task.create(
    title:    body['title'],
    director: body['director'],
    year:     body['year']
  )
  status 201
  t.to_json 
end
	
put '/api/tasks/:id' do
  body = JSON.parse request.body.read
  t = Task.get(params[:id])
  if t.nil?
    halt 404
  end
  halt 500 unless t.update(
    title:      body['title'],
    director:   body['director'],
    year:       body['year'] 
  )
  t.to_json
end
	
delete '/api/tasks/:id' do
  t = Task.get(params[:id])
  if t.nil?
    halt 404
  end
  halt 500 unless t.destroy
end

As you can see it’s pretty easy and straight forward to create all required kinds of routs for a common REST service. Next let’s review the main entry file

# main.rb

require 'json'
require 'sinatra'
require 'data_mapper'
require 'dm-migrations'
	
configure :development do
  DataMapper::Logger.new($stdout, :debug)
  DataMapper.setup(:default, ENV['DATABASE_URL'] || "sqlite3://#{Dir.pwd}/development.db")
end 
	
require './models/init' 
require './routes/init'
	
DataMapper.finalize

The main.rb itself is just loading all the dependencies. Right after that the DataMapper is configured to work with my local sqlite3 database. Last important part is actually loading the init files from both sub directories (models and routes). These init files are then again loading all specific models and routes.

Installing the dependencies

First you need of course ruby, gem and sqlite3 itself on your system. There are plenty of samples and articles out there describing how to install all these components on any kind of platform.

Installing DataMapper

Installing DataMapper and the required adapter for sqlite3 is also straight forward. Go and check out their website for detailed installation instructions

Using Bundler

Bundler is a dependency manager for ruby like nuget for clr or npm for Node.js. I use gemrat in order to automatically update the gemfile (defines all the dependencies) from the terminal.

$ gemrat json
$ gemrat sinatra
$ gemrat data_mapper
$ gemrat dm-sqlite-adapter

Automate things using a Rakefile

DataMapper is offering some great hooks for automating database generation or updating the database based on your model classes.

# Rakefile
require 'dm-migrations'
	
desc "migrates the db"
task :migrate do
  require './main'
  DataMapper.auto_migrate!
end

desc "upgrades the db"
task :upgrade do
  require './main'
  DataMapper.auto_upgrade! 
end

Starting the REST service

Starting the REST service can be done by executing the following command

$ ruby main.rb

Summary / SourceCode

As you can see things are really easy in Ruby with Sinatra and DataMapper. In order to access the entire sample go and check my github repository at https://github.com/ThorstenHans/Sinatra.REST.Sample