GraalVM

GraalVM Ruby Basics

What You’ll Need

  1. GraalVM installed and bin directory in your PATH variable
  2. Text editor (VS Code, vi, etc)
  3. REST API client (cURL, Postman, etc)
  4. Using Ubuntu based distribution for this example, so some commands (e.g. apt) could be different for you.

What You’ll Get

Installation of Ruby on GraalVM (TruffleRuby), some installed gems, and a bare-bones REST API with Sinatra and SQLite3.  According to TruffleRuby stats, your implementation should be significantly more performant than standard Ruby.

Install Ruby / TruffleRuby

Use the graal updater to install Ruby using the command below:

gu install ruby

WIth many of the gems I’ve tried to install, I was greeted with the error below.

ERROR:  Loading command: install (RuntimeError)
    you may need to install the system OpenSSL library libssl - see https://github.com/oracle/truffleruby/blob/master/doc/user/installing-libssl.md (libssl.so.10: cannot open shared object file: No such file or directory)
ERROR:  While executing gem ... (NoMethodError)
    undefined method `invoke_with_build_args' for nil:NilClass

To resolve this, you will need to install libssl-dev and then run a script provided in the TruffleRuby installation named postinstallhook.sh.  These are instructions for Ubuntu based distros, but more details may be found here: https://github.com/oracle/truffleruby/blob/master/doc/user/installing-libssl.md

#Also take opportunity to install build-essential if you do not have, since it will be needed for the post_install_hook.sh:

sudo apt install libssl-dev build-essential

#Go into "<path to your graalvm parent>/graalvm/languages/ruby/lib/truffle
cd path_to_where_your_graalvm_dir_is/graalvm/languages/ruby/lib/truffle
./post_install_hook.sh

Install Some Gems

For our project, we’ll install sqlite3 and sinatra in order to create a very basic REST API.

#Go back to your home directory:
cd ~

#Install required sqlite packages
sudo apt install libsqlite3-dev

#Specify that no documentation should be added during gem installs.  This will significantly reduce install times:
echo "gem: --no-document" >> .gemrc

#Install required Gems:
gem install sinatra sqlite3

Standalone Example - Connecting to Sqlite database

require 'sqlite3'

db = SQLite3::Database.new('myDb.db')
db.execute "CREATE TABLE IF NOT EXISTS beers(name VARCHAR(50))"
db.execute "INSERT INTO beers VALUES('Stella')"
rows = db.execute "SELECT * FROM beers"
puts rows
db.close

Standalone Example - Sinatra REST API

require 'sinatra'

get '/beers' do
    content_type 'application/json'
    beers = ['Stella', 'Jai Alai', 'Lagunitas']
    beers.to_json
end

Put it All Together

require 'sinatra'
require 'sqlite3'

db = SQLite3::Database.new('myDb.db')
db.execute "CREATE TABLE IF NOT EXISTS beers(name VARCHAR(50))"

post '/beers' do
    #Rewind body in case already read:
    request.body.rewind
    request_body = request.body.read.to_s
    
    #Parse JSON body:
    beer = JSON.parse request_body

    #Insert record.  Use prepared statement to bind varibles:
    stmt = db.prepare "INSERT INTO beers VALUES(?)"
    stmt.bind_params beer['name']
    stmt.execute
end

get '/beers' do
    content_type :json
    
    #Query all beers:
    result = db.execute "SELECT * FROM beers"
    
    #Iterate records, convert to json compatible dict, and push to array:
    beers = []
    result.each do |row|
    beers.push({:name => row[0]})
    end
    
    #Return array converted to JSON:
    beers.to_json
end 

Try posting a new beer using cURL:

curl -H "Content-Type: application/json" -d "{\"name\":\"Lagunitas\"}" -X POST http://localhost:4567/beers

curl -H "Content-Type: application/json" -d "{\"name\":\"Stella\"}" -X POST http://localhost:4567/beers

GET Records using cURL:

curl http://localhost:4567/beers

#Result:
[{"name":"Lagunitas"},{"name":"Stella"}]