If you do Ruby web programming, you’ll often hear about “Rack.” You might hear about it from a Ruby dev you respect, and that you won’t get better at Ruby web stuff until you know what’s underneath.

Or you might have to write a config.ru to use Heroku and you’re thinking, “what’s config.ru?”. It has roughly the same answer — it’s a file Rack uses to set up your app.

You’ll probably wind up debugging at some point and see “rack” repeatedly in your stack traces. Why “Rack?”

Now what?

Now it’s time to learn about Rack.

Quick Answers to “What’s Rack?”

Here are a few things Rack is. (There’s far more detail below)

Rack is a nice Ruby-flavoured replacement for CGI.

Rack sits between all the frameworks (Rails, Sinatra, Rulers) and all the app servers (thin, unicorn, Rainbows, mongrel) as an adapter.

Rack is a convenient way to build your Ruby app out of a bunch of thin layers, like a stack of pancakes. The layers are called middleware. These days, every Rails app is built out of lots of these thin layers.

Less Hand-Waving, Please!

Your app server (let’s say Puma) gets an HTTP request. It happens to be an HTTP POST to /users/login so they can create a new session and log into the server.

Puma receives the request, parses it, hands it to Rack, and then Rack gives it to your Sinatra app (or Rails, or Padrino, or your framework of choice) as a request, usually a method call. This is where Rails routing takes over, for instance.

Rack keeps Sinatra from having to know anything specific about Puma. Or Unicorn. Or Thin. Or any of the many other wacky app servers out there. That’s why it’s like CGI for Ruby - CGI keeps your web app from needing to know if it’s running with Apache or NGinX or a different web server.

But What Good Is It?

That’s fine. But it helps the folks writing app servers, not you. How does Rack help you?

Rack lets you mess with those layers – the ones above Rails or Sinatra, the ones underneath, and even the ones in the middle! That’s the whole “coding your app in thin layers, like a stack of pancakes” thing from up above.

Rails is actually implemented as an extensive stack of Rack middleware. Here’s what that stack looks like as of Rails 6.0:

$ rake middleware
use ActionDispatch::HostAuthorization
use Rack::Sendfile
use ActionDispatch::Static
use ActionDispatch::Executor
use ActiveSupport::Cache::Strategy::LocalCache::Middleware
use Rack::Runtime
use Rack::MethodOverride
use ActionDispatch::RequestId
use ActionDispatch::RemoteIp
use Sprockets::Rails::QuietAssets
use Rails::Rack::Logger
use ActionDispatch::ShowExceptions
use WebConsole::Middleware
use ActionDispatch::DebugExceptions
use ActionDispatch::ActionableExceptions
use ActionDispatch::Reloader
use ActionDispatch::Callbacks
use ActiveRecord::Migration::CheckPending
use ActionDispatch::Cookies
use ActionDispatch::Session::CookieStore
use ActionDispatch::Flash
use ActionDispatch::ContentSecurityPolicy::Middleware
use Rack::Head
use Rack::ConditionalGet
use Rack::ETag
use Rack::TempfileReaper
run SomeApp::Application.routes

Each of those things is a Rack middleware that’s doing a little piece of the processing.

Like Rack::SendFile, up near the top? It sends back static files (javascript, CSS, 404.html) without your app having to handle that with a controller action.

Or Rails::Rack::Logger? That makes sure your requests get logged to the Rails logfiles.

And what if you added one? Maybe you could fix a lot of nasty bugs with bad HTTP requests if you could just reject them immediately, before they got a chance to screw up your web framework…

Rack ships with a bunch of middlewares and Rails has a bunch of others that it adds. You can even insert your own into the stack if you mess with config.ru in your Rails app.

A rack middleware can be as simple as Rack::Lobster which prints out a little ASCII-art lobster. Or it can be as complex as RackAMole which logs every request to a MongoDB instance you have to set up. Or way, way more complex than that.

There are lots of fun middlewares out there.

How Do I Use It?

Mostly it’s done for you. Rails and Sinatra already have Rack support. You can modify config.ru in a Rails/Sinatra/whatever app if you want to add Rack pancakes to your app.

But you don’t have to do anything at all for basic Rack support if you’re already using a Rack framework.

What if you want to write a Rack-based framework and learn all the best tricks? That’s a longer answer. Luckily I’ve written a book with all those details.

For the first two chapters, sign up below (or on the sidebar to the right.)