Template for writing semantic.works services in Ruby using Sinatra
Requires: a semantic.works stack, like mu-project.
Create a new folder for your microservice.
In the folder, create your microservice in web.rb
:
get '/hello' do
status 200
{
message: "Hello mu-ruby-template"
}.to_json
end
This service will respond with 'Hello mu-ruby-template' when receiving a GET request on '/hello'.
Add the mu-ruby-template to your docker-compose.yml
with the sources mounted directly.
version: '3.4'
services:
your-microservice-name:
image: semtech/mu-ruby-template:3.1.0
environment:
RACK_ENV: "development"
ports:
- 8888:80
volumes:
- /absolute/path/to/your/sources/:/app/
Next, create the service by running
docker-compose up -d your-microservice-name
A curl
call to the microservice will show you to message
curl http://localhost:8888/hello
# Hello mu-ruby-template
Requires:
- a semantic.works stack, like mu-project
- 'Develop your first microservice'
When developing inside an existing mu.semte.ch stack, it is easiest to set the development mode by setting the RACK_ENV
environment variable to development
and mount the sources directly. This makes it easy to setup links to the database and the dispatcher. Livereload is enabled automatically when running in development mode.
version: ...
services:
...
your-microservice-name:
image: semtech/mu-ruby-template:3.1.0
environment:
RACK_ENV: "development"
volumes:
- /absolute/path/to/your/sources/:/app/
Requires:
- a semantic.works stack, like mu-project
- 'Develop your first microservice'
Add a Dockerfile with the following contents:
FROM semtech/mu-ruby-template:3.1.0
LABEL maintainer="[email protected]"
There are various ways to build a Docker image. For a production service we advise to setup automatic builds, but here we will build it locally. You can choose any name, but we will call ours 'say-hello-service'.
From the root of your microservice folder execute the following command:
docker build -t say-hello-service .
Add the newly built service to your application stack in docker-compose.yml
version: ...
services:
...
say-hello:
image: say-hello-service
Launch the new container in your app
docker-compose up -d say-hello
Requires: 'Develop in a mu.semte.ch stack'.
If desired, debug and Better Errors can be used during development, giving advanced ruby debugging features.
Requires: 'Access your microservice directly'.
When an error occurs, an interactive Better Errors error page is available at http://localhost:8888/__better_errors
.
When running in development mode, you can attach the debugger to your microservice and add breakpoints as you're used to. The debugger requires port 9229 to be forwarded, and your service to run in development mode.
my-ruby-service:
image: semtech/mu-ruby-template:3.1.0
ports:
- 9229:9229
environment:
RACK_ENV: "development"
volumes:
- /absolute/path/to/your/sources/:/app/
Add a breakpoint in your code by inserting a binding.break
(alias debugger
, binding.b
) statement.
After launching your service, open Google Chrome or Chromium and visit chrome://inspect. Once you reach the breakpoint, the file containing your code will be automatically opened in the 'Sources' tab.
Requires: 'Build a microservice based on mu-ruby-template' or 'Develop in a mu.semte.ch stack'
If you doubt your requests are arriving at your microservice correctly, you can publish it port to access it directly. In the example below, port 8888 is used to access the service directly.
Note this means you will not have the headers set by the identifier and dispatcher.
Update your service definition in docker-compose.yml
as follows:
your-microservice-name:
...
ports:
- 8888:80
Next, recreate the container by executing
docker-compose up -d your-microservice-name
You can install additional dependencies by including a Gemfile
file next to your web.rb
. It works as you would expect: just specify the dependencies in the Gemfile
. They will be installed automatically at build time. In development mode you will need to restart the container.
The template provides several helpers. One of them, Mu::query
, allows to easily execute a SPARQL query as shown in the following example:
get '/triples' do
solutions = Mu::query("SELECT * WHERE { ?s ?p ?o }")
triples = solutions.map do |solution|
{
subject: solution[:s],
predicate: solution[:p],
object: solution[:o]
}
end
status 200
{
data: triples
}.to_json
end
The utils can be included as global functions by including the Mu
module. This makes the code somewhat shorter but may cause conflicts with other libraries in the global namespace.
For example Mu::query
can then be written as query
:
include Mu
get '/triples' do
solutions = query("SELECT * WHERE { ?s ?p ?o }")
...
end
To test your app, run the container with RACK_ENV
set to test
. All rspec tests matching *_spec.rb
in spec/
and its subdirectories will be executed.
docker run --rm -e RACK_ENV=test microservice-image
To run the tests while developing, start an interactive container in the test enviroment with your code folder mounted in /app
:
docker run --volume /path/to/your/code:/app
-e RACK_ENV=test
-it semtech/mu-ruby-template:3.1.0 /bin/bash
You can now run your tests inside the container with:
bundle install
rspec
The mu-ruby-template is built on Sinatra. Check Sinatra's Getting Started guide to learn how to build a REST API in Sinatra.
The template offers a Mu
module with utils to facilitate development.
Returns the application graph configured through the MU_APPLICATION_GRAPH
.
Generate a random UUID (String).
The template provides a Logger log
object to the user for logging. Just do Mu::log.info "Hello world"
. The log level can be set through the LOG_LEVEL
environment variable (default: info
, values: debug
, info
, warn
, error
, fatal
).
Logs are written to the /logs
directory and STDOUT
in the docker container.
Executes the given SPARQL select/ask/construct query.
Returns a SPARQL::Client instance connection to the SPARQL endpoint configured through the MU_SPARQL_ENDPOINT
environment variable.
The Ruby templates extends the core classes String
, Date
, DateTime
, Time
, Integer
, Float
, Boolean
and URI
with a sparql_escape
method. This method can be used to avoid SPARQL injection by escaping user input while constructing a SPARQL query. E.g.
query = " INSERT DATA {"
query += " GRAPH <#{Mu::graph}> {"
query += " #{Mu::sparql_escape_uri(user_uri)} a <#{RDF::Vocab::FOAF.Person}> ;"
query += " <#{RDF::Vocab::FOAF.name}> #{name.sparql_escape} ;"
query += " <#{RDF::Vocab::DC.created}> #{now.sparql_escape} ."
query += " }"
query += " }"
Next to the extensions, the template also provides a helper function per datatype that takes any value as parameter. E.g. Mu::sparql_escape_uri("http://mu.semte.ch/application")
.
Executes the given SPARQL update query.
Executes a SPARQL query to update the modification date of the given subject URI (string). The date defaults to now.
The template provides the following Sinatra helpers which can only be used in a route-handling context:
The parsed JSON body of the request.
Returns a JSONAPI compliant error response with the given status code (default: 400
).
Get the rewrite URL from the request headers.
Get the session id from the request headers.
Validate whether the Content-Type header contains the JSONAPI Content-Type. Returns a 400
otherwise.
Validate whether the type specified in the JSON data is equal to the expected type. Returns a 409
otherwise.
ruby/debug supports multiple frontends for remote debugging of which we advise the Chromium inspector. You can configure the frontend via RUBY_DEBUG_OPEN_FRONTEND
environment variable. Other options are untested.
The template supports the following environment variables:
MU_SPARQL_ENDPOINT
: SPARQL endpoint URL. Default:http://database:8890/sparql
MU_SPARQL_TIMEOUT
: timeout (in seconds) for SPARQL queries. Default: 60 seconds.LOG_LEVEL
: the level of logging (default:info
, values:debug
,info
,warn
,error
,fatal
).USE_LEGACY_UTILS
: when enabled (using"true"
or"yes"
) legacy utils from v2 will be included in the root file so they can be used as before (e.g.query
instead ofMu::query
). Default:"true"
PRINT_DEPRECATION_WARNINGS
: Deprecation warnings will be printed for each usage of a legacy util. Default:"true"
.RACK_ENV
: environment to start the Sinatra application in. Default:production
. Possible valuesproduction
,development
,test
.RUBY_DEBUG_PORT
: port to use for remote debugging. Default:9229
.RUBY_DEBUG_OPEN_FRONTEND
: frontend to use for debugging. Default:chrome
. Other options are untested.RUBY_OPTIONS
: options to pass to the ruby command on startup. Default:--jit
.
To execute custom bash statements during the image build (e.g. to install aditional system libraries), provide an on-build.sh
script in the root of your service. It will be automatically picked up and executed by the Docker build.