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.

However, 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 want to organize all the artifacts a little bit. That’s why I’ve chosen the following structure

Sinatra project 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    
puts 'processed'

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 Task.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 necessary kinds of routes for a universal REST service. Next, let’s review the main entry file

# main.rb

# encoding: UTF-8
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 loading the init files from both subdirectories (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 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 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.

require 'dm-migrations'

desc "List all routes"
task :routes do
  puts `grep '^[get|post|put|delete].*do$' routes/*.rb | sed 's/ do$//'`
end

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 straightforward in Ruby with Sinatra and DataMapper. To access the entire sample go and check my GitHub repository at https://github.com/ThorstenHans/Sinatra.REST.Sample.