Mike Slinn

Sinatra Request Explorer

Published 2022-12-02. Last modified 2022-12-04.
Time to read: 3 minutes.

This page is part of the ruby collection.

Sinatra is a F/OSS DSL, also referred to as a micro-framework, for quickly creating web applications in Ruby with minimal effort.

About Sinatra

Sinatra is tiny; its source code is less than 2000 lines. Small, but mighty, and very influential. Since its release in 2007, a partial list of other web frameworks that Sinatra has inspired and are currently active at the time of writing includes Compojure, Express.js, Fiber, Finatra, Flask, Javalin, Padrino, Pedestal, Play Framework, Roda, Scalatra, Scotty, Spark, Spring Boot, and Trot.

Sinatra is the second-most popular web framework in the Ruby ecosystem, after Ruby on Rails. Sinatra and Ruby on Rails are somewhat similar because they both extend Rack. This means that middleware that conforms to the rack specification works with all web frameworks that extend rack.

Sinatra is a good choice for system integration because it is simple, malleable, has few dependencies and is reasonably efficient. Sinatra webapps are a good mechanism for integrating web servers such as nginx and Apache httpd with other services, such as e-commerce payment gateways.

The following builds upon this Sinatra webapp:

If you are eager to learn Sinatra, here are two good references:

Automatic Restart

Sinatra applications can automatically be restarted when their source code changes. This requires a helper application, such as rerun; other projects also do similar things. To install rerun on Ubuntu:

Shell
$ yes | sudo apt install rerun

Rerun is not currently compatible with WSL or WSL2. Future versions of WSL2 will likely work with rerun.

Setting Up a Ruby Development

Previously, I wrote about Setting Up a Ruby Development Environment. Follow those instructions first if you want to type along with this article.

Sinatra Request Explorer

Get the project:

Shell
$ git clone https://github.com/mslinn/SinatraRequestExplorer.git
$ cd SinatraRequestExplorer

Install the SinatraRequestExplorer gems:

Shell
$ bundle install

Run Sinatra Request Explorer

Development

Shell
$ rerun ruby main.rb
9:39:55 [rerun] Sinatrarequestexplorer launched
09:39:55 [rerun] Rerun (760521) running Sinatrarequestexplorer (760542)
== Sinatra (v3.0.3) has taken the stage on 9876 for development with backup from Puma
Puma starting in single mode...
* Puma version: 6.0.0 (ruby 3.1.0-p0) ("Sunflower")
*  Min threads: 0
*  Max threads: 5
*  Environment: development
*          PID: 760542
* Listening on http://127.0.0.1:9876
* Listening on http://[::1]:9876
Use Ctrl-C to stop
09:39:57 [rerun] Watching . for **/*.{rb,js,coffee,css,scss,sass,erb,html,haml,ru,yml,slim,md,feature,c,h} with Linux adapter 

Visit the SinatraRequestExplorer webapp at port 9876.

The web page displayed in response to the GET Test link above is:

Shell
HTTP_USER_AGENT=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36
Method: GET
request.accept:
  */*
  application/signed-exchange
  application/xhtml+xml
  application/xml
  image/apng
  image/avif
  image/webp
  text/html

request.url=http://localhost:9876/dump?a=b&c=d
request.fullpath=/dump?a=b&c=d
request.path_info=/dump

request.params:
  a=b
  c=d

Computed Content-Type: plain

The web page displayed in response to the POST Test button above is:

Shell
HTTP_USER_AGENT=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36
Method: POST
request.accept:
  */*
  application/signed-exchange
  application/xhtml+xml
  application/xml
  image/apng
  image/avif
  image/webp
  text/html
request.url=http://localhost:9876/dump request.fullpath=/dump request.path_info=/dump
request.params:
Computed Content-Type: plain

The web page displayed in response to the JSON Test button above is:

Shell
HTTP_USER_AGENT=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36
Method: POST
request.accept:
  */*
  application/signed-exchange
  application/xhtml+xml
  application/xml
  image/apng
  image/avif
  image/webp
  text/html
request.url=http://localhost:9876/dump request.fullpath=/dump request.path_info=/dump
request.params: field1=value1 field2=value2
Computed Content-Type: plain

Using Curl

Use curl to invoke the SinatraRequestExplorer webapp:

Shell
$ URL=http://localhost:9876/dump

$ curl -d "param1=value1&param2=value2" -X POST "$URL"
<title>SinatraRequestExplorer dump</title>
<h2>Post Method</h2>
<p>Generated by the post /dump handler.</p>
<p>Go to <a href="/">Root</a>.</p>
<h2>Request Parameters</h2>
<pre>
request.url=http://localhost:9876/dump
request.fullpath=/dump
request.path_info=/dump
request.params=
  param1=value1
  param2=value2

HTTP_USER_AGENT=curl/7.85.0
</pre> 

The test script in SinatraRequestExplorer automates the above invocation and includes others.

#!/bin/bash

# Curl, and most browsers, default to sending Content-Type: application/x-www-form-urlencoded for a POST.
# Get requests should not specify Content-type because they do not have a body.

# Curl defaults to Accept: */*, however each browser defaults to a different default value.
# Chrome defaults to Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
# See https://developer.mozilla.org/en-US/docs/Web/HTTP/Content_negotiation/List_of_default_Accept_values

shopt -s expand_aliases
alias curl='curl -L --silent --show-error'

function label {
  printf "\n\n=== $@ ===\n"
}

URL=http://localhost:9876/dump

label "Send GET, accept any type of response"
curl "$URL?a=b&c=d"

label "Send GET, expect a JSON response"
curl -H "Accept: application/json" "$URL?a=b&c=d"

label "Send POST, accept any type of response"
curl -d "param1=value1&param2=value2" "$URL"

label "Send POST with a form-urlencoded body and a query string, accept any type of response"
curl -d "param1=value1&param2=value2" "$URL?a=b&c=d"

label "Send POST with a form-urlencoded body, expect a JSON response"
curl -d "param1=value1&param2=value2" \
  -H "Accept: application/json" \
  $URL

label "Send POST with a JSON body, expect a JSON response"
curl -d '{"param3":"value3", "param4":"value4"}' \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  $URL

Production

Rerun is not something you would want or need in production. Instead, you could launch Sinatra webapps such as SinatraRequestExplorer when the system boots with an entry in crontab, like this:

crontab
@reboot ruby /work/ruby/dependencies/sinatra/SinatraRequestExplorer/main.rb

About the Author

I, Mike Slinn, have been working with Ruby for a long time now. Back in 2005, I was the product marketing manager at CodeGear (the company was formerly known as Borland) for their 3rd Rail IDE. 3rd Rail supported Ruby and Ruby on Rails at launch.

In 2006, I co-chaired the Silicon Valley Ruby Conference on behalf of the SD Forum in Silicon Valley. As you can see, I have the t-shirt. I was the sole chairman of the 2007 Silicon Valley Ruby Conference.

Several court cases have come my way over the years in my capacity as a software expert witness. The court cases featured questions about IP misappropriation for Ruby on Rails programs. You can read about my experience as a software expert if that interests you.

I currently enjoy writing Jekyll plugins in Ruby for this website and others, as well as Ruby utilities.

* indicates a required field.

Please select the following to receive Mike Slinn’s newsletter:

You can unsubscribe at any time by clicking the link in the footer of emails.

Mike Slinn uses Mailchimp as his marketing platform. By clicking below to subscribe, you acknowledge that your information will be transferred to Mailchimp for processing. Learn more about Mailchimp’s privacy practices.