Maybe an engineer you respect just told you to go learn about Rack — you won’t get better at Ruby web stuff until you know what’s underneath.
Maybe you have to write a config.ru to use Heroku and you’re thinking, “what’s config.ru?”.
Maybe you have a deep, nasty Sinatra bug and you’re having to dig into the Sinatra stack traces and stuff that might be failing is named “Rack”. Why “Rack”?
Now it’s time to learn about Rack.
Quick Answers to “What’s Rack?”
Here are a few things Rack is.
If these don’t help, scroll down a bit. There’s far more detail below.
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. Or pancakes, why not?
Less Hand-Wavey, Please!
Unicorn 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 unicorn. Or mongrel. Or thin. Or lighttpd. Or any of the many other wacky app servers out there. That’s why it’s like CGI for Ruby.
But What Good Is It?
That’s fine. But it helps the guys 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 a whole bunch of Rack pancakes (same as middleware, but I’m calling them pancakes from here on out). Here’s what that stack looks like, lightly edited:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
Each of those things is a Rack middleware that’s doing a little piece of the processing.
Like Rack::Lock, up near the top? That keeps more than one thread from running in your Rails server at once so that you don’t have to make all your code thread-safe.
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 pancake can be as simple as Rack::Config where you hand it a block of code and it runs it on each request. 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 pancakes 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’m writing a book with all those details.
For the first two chapters, sign up below: