Mike Slinn

Debugging Rackup Webapps With Visual Studio Code

Published 2023-01-31. Last modified 2023-11-04.
Time to read: 3 minutes.

This page is part of the ruby collection.

Rackup provides a command-line interface for running rack-compatible web applications. Two well-known application servers that adhere to the rack standard are Ruby on Rails and Sinatra.

The rack gem is a dependency of the rackup gem.

To debug rack-compatible webapps in Visual Studio Code, you need to somehow launch the Ruby debugger on the rackup gem in such a way that all the other dependencies (also gems) are loaded.

Surely, reliable debugging must be an oxymoron, 😁 like awfully good, same difference, and original copy.

All the advice I could find on the Interwebs discussed a brittle approach, which would break every time a different version of the rackup gem was required. This article discusses how to use a manually created binstub for reliable debugging.

What is Rackup?

The rackup command is a short bash script that loads and executes a gem of the same name. The bash script is actually packaged within the gem and is deployed when the gem is installed.

rackup
#!/usr/bin/env bash
set -e
[ -n "$RBENV_DEBUG" ] && set -x

program="${0##*/}"
if [ "$program" = "ruby" ]; then
  for arg; do
    case "$arg" in
    -e* | -- ) break ;;
    */* )
      if [ -f "$arg" ]; then
        export RBENV_DIR="${arg%/*}"
        break
      fi
      ;;
    esac
  done
fi

export RBENV_ROOT="/home/mslinn/.rbenv"
exec "/usr/lib/rbenv/libexec/rbenv" exec "$program" "$@"

This is the rackup help message:

Shell
$ rackup -h
Usage: rackup [ruby options] [rack options] [rackup config]
Ruby options: -e, --eval LINE evaluate a LINE of code -d, --debug set debugging flags (set $DEBUG to true) -w, --warn turn warnings on for your script -q, --quiet turn off logging -I, --include PATH specify $LOAD_PATH (may be used more than once) -r, --require LIBRARY require the library, before executing your script
Rack options: -b BUILDER_LINE, evaluate a BUILDER_LINE of code as a builder script --builder -s, --server SERVER serve using SERVER (thin/puma/webrick) -o, --host HOST listen on HOST (default: localhost) -p, --port PORT use PORT (default: 9292) -O NAME[=VALUE], pass VALUE to the server as option NAME. If no VALUE, sets it to true. Run '$HOME/.rbenv/versions/3.1.0/bin/rackup -s SERVER -h' to get a list of options for SERVER --option -E, --env ENVIRONMENT use ENVIRONMENT for defaults (default: development) -D, --daemonize run daemonized in the background -P, --pid FILE file to store PID
Profiling options: --heap HEAPFILE Build the application, then dump the heap to HEAPFILE --profile PROFILE Dump CPU or Memory profile to PROFILE (defaults to a tempfile) --profile-mode MODE Profile mode (cpu|wall|object)
Common options: -h, -?, --help Show this message --version Show version
Server-specific options for Rack::Handler::Puma: -O Threads=MIN:MAX min:max threads to use (default 0:16) -O Verbose Don’t report each request (default: false)

Gem Directory Tree

Gems are either installed system-wide or in user-specific locations. If you are using rbenv to work with Ruby (and you should), the directory that your gems are installed to is $HOME/.rbenv/versions/$RUBY_VERSION/lib/;ruby/;gems/$RUBY_VERSION/$GEM_NAME-$GEM_VERSION. The various versions of each gem reside in sibling directories.

The project I was working on when I ran the following command used Ruby version 3.1.0, and the rackup version 1.0.0 gem used by that project was installed at:

Shell
$ bundle info rackup
/home/mslinn/.rbenv/versions/3.1.0/lib/ruby/gems/3.1.0/gems/rackup-1.0.0

The entry point is $HOME/.rbenv/versions/3.1.0/lib/ruby/gems/3.1.0/gems/rackup-1.0.0/lib/rackup.rb

The rackup command was installed to:

Shell
$ which rackup
/home/mslinn/.rbenv/shims/rackup

The following is the brittle and out-of-date Visual Studio Code launch configuration that you find on the Interwebs.

  1. It specifies the out-of-date rebornix.Ruby Visual Studio Code extension, which should not be used anymore.
  2. To amplify what I said before, this launch configuration is brittle. If you change the Ruby version or the version of the rackup gem, the program path will also need to change, or the launch configuration will stop working.

I highlighted the brittle part:

.vscode/launch.json
{
  "cwd": "${workspaceRoot}",
  "name": "Debug rackup application",
  "pathToBundler": "${userHome}/.rbenv/shims/bundle",
  "pathToRDebugIDE": "${userHome}/.rbenv/shims/rdebug-ide",
  "program": "${userHome}/.rbenv/versions/3.1.0/lib/ruby/gems/3.1.0/gems/rackup-1.0.0/lib/rackup.rb",
  "request": "launch",
  "showDebuggerOutput": true,
  "type": "Ruby",
  "useBundler": true,
},

VSCode Debugging With Binstubs

We saw earlier that the rackup command is a Bash script, and it launches the gem of the same name. However, Ruby debuggers are unable to work with Bash scripts.

Binstubs to the rescue! ... well, actually, manually created binstubs to the rescue.

I do not know why the following attempt to automagically create a binstub fails — as we have seen, rackup does indeed have an associated bash script.

Shell
$ bundle binstubs rackup
rackup has no executables, but you may want one from a gem it depends on.
  rack has: rackup 

However, there is a solution: manually creating a binstub for the rackup gem. I wrote a binstub for rackup; save it as bin/rackup in the directory of your Jekyll project.

./bin/rackup
#!/usr/bin/env ruby
# Prepares the $LOAD_PATH by adding to it lib directories of all gems in the # project's bundle: require 'bundler/setup'
load Gem.bin_path('rack', 'rackup')

The last line above could be modified to load other gems.

The following is a Visual Studio Code launch configuration for Rackup-compatible webapps using the manually created binstub. It assumes that rbenv was used to install Ruby. Because bundler is used to load rackup, all the dependencies mentioned in the Gemfile are also loaded before debugging.

.vscode/launch.json
{
  "debugPort": "0",
  "name": "Debug rackup application",
  "request": "launch",
  "script": "${workspaceRoot}/bin/rackup",
  "type": "rdbg",
},
😁

Easy!

To learn more about Ruby LSP and rdebug, please read Essential Visual Studio Code Extensions for Ruby.

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.