Talks
Rack For Web Developers

Rack For Web Developers

by Michael J. I. Jackson

In the video titled 'Rack For Web Developers,' presented by Michael Jackson at the MountainWest RubyConf 2010, the main focus is on the Rack library, a significant component in Ruby web development. The presentation begins with Michael's background as a Ruby contractor and his transition from PHP to Ruby, highlighting his initial confusion about the Rack library in the Ruby ecosystem. The presentation aims to clarify what Rack is, why it is important, and how it facilitates web development in Ruby by defining a standard interface between web applications and servers.

Key points discussed include:
- Introduction to Rack: Rack is introduced as an interface that allows different web frameworks, like Rails and Sinatra, to interact with various web servers without needing to change their core functionality.
- Decoupling: Rack simplifies the development and deployment of web apps by decoupling applications from the underlying servers. This flexibility stands in contrast to other languages, like PHP, where developers are often tied to specific frameworks and servers.
- Specification Overview: The Rack specification defines two main components: an application and a middleware. An application must respond to a specific method (with a standardized environment hash as input), while middleware layers provide additional features processing the request before or after reaching the main application.
- Implementation Examples: Examples are provided on how to create a simple Rack app and how middleware functions within the Rack ecosystem. Michael discusses the Rack::Builder class, which helps set up Rack applications and run them on various servers using an easy setup.
- Middleware Importance: The middleware concept is highlighted as crucial in the Rack ecosystem, enabling common tasks like session handling, logging, and response management to be handled modularly, improving the efficiency of web development.
- Development Tools: Tools like Rackup, Shotgun, Rack::Lint, and Rack::ShowExceptions are introduced as methods to simplify the development and debugging of Rack applications.

In conclusion, the talk emphasizes the importance of Rack in enhancing web development workflows in Ruby by providing a common middleware interface that fosters modularity and ease of use. Michael encourages viewers to familiarize themselves with the Rack specification, asserting that understanding Rack is essential for any Ruby web developer's efficiency in building web applications.

00:00:15.080 Okay, so just a little introduction because I've got a lot to get through, and I don't want to spend very much time on myself. My name is Michael Jackson, and I'm still alive. Yeah, everybody was telling me, "Dude, I thought you died!" I'm originally from California but I live here now in Utah. I work as an independent Ruby contractor and web developer. I do a lot of web development work, just JavaScript, CSS, HTML, whatever. Today, I want to talk about a library called Rack. Now, I've only been using Ruby for about two years, and that’s kind of around the same time that Rack came onto the scene. People were like, "Whoa!" and I was like, "Whoa!" because I came from a PHP background. I was like, "Wow, what is this Rack?" What was crazy was that everybody started putting their libraries on Rack. The Rails folks were like, "Hey, we should use Rack," and the Sinatra folks were like, "We're using Rack," and the Thin developer was like, "I'm Thin as Rack!" I was like, "What the heck is Rack?" I was actually talking to a developer friend of mine last year, and he was developing an app in Rails. He said, "Oh, Rails 2.3 is on Rack," and he felt like he had to learn Rack as well as Rails.
00:01:07.640 I told him, "You don't have to learn Rack; it just works for you." His comment kind of typified, unfortunately, the feeling that I’ve heard from a few other people too. They ask, "What is this Rack thing? What is this library? A lot of people are talking about it, but what does it do? Why is it necessary? What are we using it for?" So my talk today is important because Rack has seriously changed the way we write for the web in Ruby. Let me see, by a show of hands really quick—I promise it's the only time I'll make you raise your hand—how many people are using Ruby to do web servers, web frameworks, or web services? That's like everybody in here! There are like five people not raising their hands. Everybody is using Ruby for web development.
00:02:05.760 What's so cool about the Ruby web development community, and you realize this if you come from another background, is that you can write stuff in Ruby. You can just take all of these different servers, frameworks, libraries, and middleware, and you can stick them all together in some big hodgepodge. It’s kind of weird because in other languages, if you’re a PHP developer, for example, what are you going to do if you're going to write a web app? You might say, "Okay, we're going to use Drupal, or we're going to use CakePHP or something," and so you’ll use that framework. You do not go outside the framework; you just run it on Apache with mod PHP. That's it. You know, some developers might say, "Hey, let’s run it on FastCGI," but typically the mindset is no, because it might be different on FastCGI. You might have some different environment variables.
00:02:47.719 So you’re just like, "We're running it on mod PHP, we're running it on Apache, and that’s that!" It's a very strict way to develop and deploy your apps. Then you come to the Ruby world, and it’s like, "Hey, what do you want to write your app in?" I don’t know, we could write it in Rails, we could write it in Sinatra, or we could write it in Merb. Well, not anymore because Merb got the axe. But then how are we going to deploy it? I don’t know. We might run it on Mongrel, or we could run it on Passenger, or we could run it on Thin. Now you all know how many characters my password has!
00:03:32.080 Okay, so basically I’m going to take this down—is that okay? Because I want to... oh snap, there it is. I'm kind of a wired guy, but I'll try this wireless thing. Hello, hello. Oh yeah, it’s good! I'm live. Nice! So anyway, this is what I'm saying. We’ve got lots of ways to write web apps, right? We can write them however we want. We’ve got lots of ways to serve web apps. Does that ever strike you as kind of like, "Wow, how do they all work together?" I mean, how is it possible that I can take a Rails app and deploy it on any number of application servers? I can take a Sinatra app and run it on all those servers as well. I can basically write for any framework and run it on any server that I want. It’s actually pretty amazing.
00:04:44.760 Here's how I picture it: Along the top here, you've got all your different ways to serve web apps. You've got CGI, FCGI; then you've got things like Passengers, which is an Nginx or Apache module, and then you've got your different app servers, which are pretty popular. Obviously, this is not a comprehensive list; there are lots more. Along the bottom, you have all your different frameworks—these are all the different ways you can write an app in Rails, Sinatra, Camping, or Merb. Back in the early days, the people who were writing Rails asked, "How are we going to run our Rails app?" They thought, "Let's run it over CGI!" So if you were writing a Rails app before 2.3, you had a little file in your public directory called 'dispatch.cgi.' Then, along came Mongrel and it was like, "Okay, there’s a Mongrel command and a Mongrel Rails command." You see the coupling that was happening; you had a specific adapter for Rails and all these different adapters: an adapter for Rails in CGI, an adapter for Rails in FCGI, a specific layer of software for Rails on Mongrel. Thin has a Rails adapter, so it can detect if it’s being run from inside a Rails directory.
00:07:36.199 You've got these couplings going on. That’s represented by these arrows. Anytime you want to run Rails on any one of these different servers, you’ve got a little coupling there. It might be small; it’s a small thing, but there is still a little bit of coupling going on. If you kind of do the math, you follow the pattern: anytime you want to write a web app or a framework, if you want to run it on all the different servers, you have to figure out how you’re going to run it on all those different servers. This is something you never think about if you code PHP because it’s just like, "Yeah, we’ll run on Apache and mod PHP. Duh! Why would we do anything else?" But it changes when you’ve got a language like Ruby; it wasn’t written for the web—people are using it for the web.
00:10:53.400 So it’s a mess. You need a common kind of language, and that’s Rack. Rack basically specifies an interface for servers and frameworks, meaning this is how they talk to each other. You don't have to worry about Rails anymore or what app server you’re running it on. You are a Rack app. The same goes for Sinatra and all other frameworks—you are all Rack apps, and then you can run on any framework you want. I can write some middleware and say, "Hey, I just wrote some really cool middleware!" Well, how am I going to use it? You can use it with anything that’s Rack compatible. Everybody talks to Rack, and Rack handles the translation. That’s a pretty huge thing. How do they know? There’s a spec. It’s like a spec! Oh my gosh, it’s revolutionary. It takes you like five minutes to read this spec. It is so small. If you are developing for the web, you need to go read the spec. Do yourself a favor and go read the spec, because no matter how you’re writing stuff for the web, you are using Rack nowadays, unless you’re just writing XML files, pumping them through rake, and sprinkling XSLT on them to make a little website.
00:12:53.520 If you have anything that has server application logic, you're running on Rack. So follow the spec. Let’s talk about it: what is the spec? In a nutshell, the spec specifies three things. You’ve got an environment—what is an environment? It’s a hash with keys and values. The keys are HTTP or CGI-like headers, such as HTTP host, path info, query string—these types of things that you get in your CGI environment. Then you’ve got a couple of variables in that environment that Rack throws in there as well; it’s all in the spec. Like I said, it’s straightforward. You’ve got apps. What is an app? It’s an object that responds to one method. You can’t get any simpler than that, right? It responds to one method call, takes one parameter—the aforementioned hash. What does it return? It returns an array with three elements: status, headers, body.
00:14:12.919 The genius of Rack is that it is so simple to implement. It’s not like, you know, it's not some complex thing. Everybody's like, "Yeah, we could do that. That's easy! We could totally be Rack compatible!" And actually, we’re going to look at some of the Rails source code later. In Rails 3—which, by the way, I know some of the authors are here, and they’ve done a fantastic job—you check it out, and it's all just middleware. Because all you need to have is a method in there that takes a hash. I might not be making much sense, but this is kind of what that hash looks like. This is kind of what your environment looks like if you have developed in other environments and you’re coming to Ruby. You will realize the significance of this hash. It’s awesome that you can always count on those values being there and being formatted in the same way, no matter what environment you are running in.
00:16:13.080 This is your environment hash. This is like an app; it's like super simple. In the top right, a Lambda is a valid Rack app. You could fire up this Rack app. I'm going to show you how we run it. You could fire that thing up and serve "Hello, World!" all day; people would love you. Or you could instantiate a little object here. We’ve got a little class with a method called 'call.' Boom! We instantiate it, and all of a sudden it’s a valid Rack app. These are just kind of real simple examples. I keep talking about middleware. What is middleware? Middleware is the lifeblood of the Rack ecosystem. Go to GitHub, search for Rack—you’ll find almost 500 repositories.
00:17:42.800 The manual stuff you have to do every time you write a web app, like how are we going to do sessions? How are we going to do cookies? How are we going to do JSON to the client? How are we going to detect if it's a mobile client? Monotonous stuff—it’s all just middleware. I'm going to show you how it works. Middleware is like a fancy app; it’s got one more method. It’s got an initialize method. When middleware is invoked, it’s inserted into the pipeline. I’ll show you how that happens. It takes an instance of the app in its initialize method. This is kind of a generic template for a middleware: it stores the instance of the app internally and later on, when you call it, all it’s got to do is say, "App, I'm calling you!" It just passes the environment right through.
00:19:10.079 You could have a chain; they wrap one another. So your app is at the core, and then you’ve got all these middlewares wrapping it. When one of them gets called, they pass the environment all the way down to your app that’s at the very core. It will return the array just like the spec says—status, headers, and body. So if I'm using these middlewares and I've got this app that I'm running, this app is the smart part; this is my logic. I don’t always want to have to specify details, for example, common logger. If you’re running like a PHP app on Apache, you’ve got Apache writing out these log files. Nginx will do the same thing. But sometimes you want to get a log of just the stuff that actually hits your app—not all the requests for static files.
00:21:53.320 So you insert common logger into the pipeline. What does it do? As the request comes in, common logger doesn’t do anything with it, until the response comes back out. When the response comes back out, common logger checks the status, the time, and the user agent that was used to create an Apache-style log. Same thing with other middlewares. I’m going to talk about Rack Lock, for example. It is super simple. You’ve got an environment variable called rack.multithreaded or multi-threaded. I can’t remember. Anyway, all it does is it’s got a mutex, indicating maybe my app isn't safe for a multi-threaded environment. Have you ever thought about that? If my app isn’t safe for a multi-threaded environment, I have to figure out some single point of execution where I can say, "Stop the world! You run!" Then you run, and then you run.
00:24:02.360 So a mutex or a lock is perfect for that because I can synchronize calls to my app and say, "Okay, it’s your turn now!" I’m not sure about the Rails library, but I thought Rails 3 was using Rack Lock to ensure thread safety—somebody else could probably address that better than I can. So there are also content-type and content-length middlewares. The Rack spec states that when your request comes back out, it should have a content-type and a content-length. That’s just HTTP specification stuff. You might think, "Okay, if I’m too lazy to put that in, why not automatically determine it?" Rack will check for that and if it doesn’t find a content-type, it will automatically set one.
00:27:28.799 You see a lot of middlewares that are very simple like this, but there are also more complex ones that do impressive functions. So, how are we going to run this? It’s easy to run a Rack app! This is a verbose way to do it. If you want to run this app, take this code and stick it in a file called myapp.rb or whatever you want, then invoke it with the Ruby interpreter. What’s it going to do? It requires the Rack library, sets up an instance of Rack::Builder, and Rack::Builder takes that chunk in the middle and evaluates it, creating an instance of Rack URlMap. It just creates a real simple way to build an app. You say, "Run my handler!" In this case, I’m using WEBrick.
00:30:07.320 The beautiful thing is that Rack does the work for us, abstracting how to pass this app to WEBrick, Thin, Mongrel, Passenger, or any other server. We just say, "Run my app on port 9292, and you can stick a host in there if you want!" Rack ships with a tool called Rackup. What is Rackup? It’s just basically a tool that creates an instance of the Builder and evaluates the code in your config.ru file, and off you go! So notice how I’ve shortened it by removing some verbosity. You’d stick this in a config.ru file and run it with Rackup. Rackup will give you some options: run it in different environments, on different ports, and so on. It defaults to WEBrick, which is the server that ships with Ruby.
00:32:52.159 There’s a cool little library called Shotgun. What happens when you fire up your app and it’s running in memory? You tweak it because something’s broken or whatever while you’re developing, and then you think, "Crap! I have to go kill the process, fire it up again, refresh!" It’s kind of a pain. Ryan Tomo, one of the GitHub guys, wrote a nice library called Shotgun. What it does is load your app in a separate process every single time. So every time your app is hit, it loads in a separate process, so you don’t have your old application code. Nod your head if you understand what I'm saying! All right, five people are following me. I think the rest of you are too lazy to nod.
00:35:00.199 So, once again, you can mount all kinds of different apps at different places. I can mount an app at the root. When somebody requests the root of my website, some app runs, and when someone requests /downloads or something, a different app may run—a file server, perhaps. When someone requests different post URLs, a different app runs. How do we route? The Sinatra DSL is often imitated but never equaled. People are always trying to imitate the Sinatra DSL. If you haven’t written a library that imitates Sinatra, you need to get on it because your version could be cooler than Sinatra.
00:38:15.560 Rack::Mount—like I said, I’m going to upload a lot of this stuff. These are just basic examples. Here’s what you do. Here’s how you run a Sinatra app. Here’s how you use the Rack::Mount library. I want you guys to download this stuff; I just have a few minutes left, so I'm rushing through the end here. There are all kinds of debugging libraries! Take note of Rack::Lint and Rack::ShowExceptions. You’ve got a Rack::Shell! Imagine you’re running your app, you crack open a shell, and you’re in your app, making requests and doing anything right from the shell. You’ll see the responses coming back from your server. It’s awesome.
00:39:44.320 Let’s see... Rack::Bug! Check that out! If you do Rails, make a note of Rack::Bug. It is awesome! I forget the name of the guy who created it, oh yes, Brian Helmkamp. That's right! Brian Helmkamp did that. Awesome library! Rack::Debug—uh, not ready for 1.9 yet. So, there’s a lot of middleware out there. Whenever you think about writing something, just check first. I promise someone already wrote it. Head over to GitHub, check it out. Check out Rack::Contrib—the Rack library has some useful middlewares. You can do streaming, chunked HTTP responses, automatic headers, sessions, cookie handling. You get all this stuff with Rack; you get all of it.
00:41:09.040 I actually have a sample app in the repository. When you download the code from the repository, there’s a little app there. Just run it with the Ruby interpreter. It’s a Twitter cache limiter app, ensuring that you don’t hit the Twitter API more than it likes to be hit. That’s all it does, but it’s a cool example of a raw Rack app complete with unit tests, so you can run the tests and everything. This is an app you can download; you might say, "What’s all this Rack stuff about?" It’s very simple, I think it’s about a hundred lines of code, and you can check it out. You can run it and say, "Okay, I get Rack now." Since I’m done, I want to know if there are any questions anyone has about Rack.
00:44:52.360 Yeah, the setting default call—the newty? Yeah. How do you... So if you’re talking about using the `#edit` method in Rack::Builder DSL, remember I showed that app with Rack::Builder?
00:45:07.799 You can call three methods inside: use, map, and run. When I say, "Use this class"—like say `use Rack::ContentLength`—I can pass it a list of arguments that will be passed to the constructor when it gets initialized. Does that answer the question? Yes? Anyone else?
00:48:37.920 What about AC? You know, honestly, I think if someone has an answer for that, I’d suggest forking a separate process or using something like Delayed Job and examples. It has all been done; you do not have to do anything. Just... oh thanks dude! Thanks for your help! Let me let you know where you can download the code. Here’s where you can find me. It’s a pretty lame blog, but I don’t know, go there if you don’t have anything to read. I'm on Twitter. Thank you very much!