There and Rack Again

What is Rack and Why

🐦 @zerosumjames 💌 jsrn@hey.com 🌐 jsrn.net

Objectives

  1. Understand how HTTP requests get to your app.
  2. Understand the role that rack plays.
  3. Make your own rack application.

HTTP Requests


						GET /uses HTTP/1.1
						Host: jsrn.net
					
GET The HTTP verb. Commonly GET, POST, PATCH or DELETE.
/uses The path being requested.
HTTP/1.1 The version of HTTP to be used.
Host: One of potentially several headers passed along with the request.

HTTP Responses


						HTTP/1.1 200 OK
						Date: Mon, 23 May 2005 22:38:34 GMT
						Content-Type: text/html; charset=UTF-8
						Content-Length: 155
						Last-Modified: Wed, 08 Jan 2003 23:11:55 GMT
						Server: Apache/1.3.3.7 (Unix) (Red-Hat/Linux)
						Accept-Ranges: bytes
						Connection: close

						<html>
						  <head><title>An Example Page</title></head>
						  <body><p>Hello World!</p></body>
						</html>
					

The Path

How does the request reach our Ruby application?

                 _____________________
_______________  |                   |
|             |  |     webserver     |
|  "the web"  |->|  (nginx, apache   |
|_____________|  |       etc.)       |
                 |___________________|
                 __________V__________  ______________________
                 |                   |  |                    |
                 |    app server     |  |                    |
                 |  (puma, unicorn   |->|  your application  |
                 |      etc.)        |  |                    |
                 |___________________|  |____________________|
					

The Littlest Server


						require 'socket'
						server = TCPServer.new(5678) # Bind to a socket

						while session = server.accept # Take the next connection
						  request = session.gets # Read the request

						  session.print "HTTP/1.1 200\r\n" # Respond
						  session.print "Content-Type: text/html\r\n"
						  session.print "\r\n"
						  session.print "Hello world!"

						  session.close # Close the connection
						end
					

Ruby Application Servers

What application servers are there?

unicorn, puma, passenger, thin, falcon, prax, okay, OKAY. That's enough. Jeez.

Your application server is going to want to pass requests to your application.

Do they speak the same language?

Who defines that language?

Rack

"a modular Ruby webserver interface"

Rack does the thinking, so you don't have to.

It's Everywhere

Your favourite Ruby (Rails, Hanami, Sinatra) applications are actually Rack applications.

What is a Rack application?

  • A Ruby object that responds to #call(env)
  • Accepts the Rack environment as the only argument.
  • Returns [status_code, headers, body].

Environment Goes In


						{
						  "REQUEST_METHOD" => "GET",
						  "PATH_INFO" => "/uses",
						  "SERVER_PORT" => 80,
						  "REMOTE_ADDR" => "127.0.0.1",
						  "HTTP_USER_AGENT" => "Mozilla/5.0 blah blah"
						  # lots more!
						}
					

Response Comes Out


						[
						  200,
						  { 'Content-Type' => 'text/html' },
						  ["Hello, world!"]
						]
					

Which application servers support Rack?

unicorn
puma
passenger
thin
falcon
prax

The Littlest Rack Application


            # app.rb
            class App
              def call(env)
                [
                  200,
                  { 'Content-Type' => 'text/html' },
                  ["Hello, world!"]
                ]
              end
            end
          

            # config.ru
            require './app'
            run App.new
          

rackup

rackup is a useful tool for running Rack applications


						# Automatically looks for config.ru
						# and runs the application on port 3001
						rackup -p 3001
          

config.ru

This is the configuration file used by Rack-based servers. Here's the one from a new Rails 6.1 project.


            require_relative "config/environment"
            run Rails.application
            Rails.application.load_server
          

Middleware

Add behaviour without changing your application.

Essentially also a rack application.

What's Middleware Good For?

  • Logging
  • Setting headers
  • Adding compression

Let's Make Rails

  • 78,913 commits
  • huge contributor community
  • corporate backing

Let's not.

Let's replicate a small subset of Rails-like features

Let's make...

  1. a router
  2. a controller
  3. a model
  4. a view
  5. some middleware

<insert incredible live coding sequence>

Phew!

We just made a very (very) lightweight MVC framework.

No Rails, no Hanami, no Sinatra. Scarcely any gems at all.

Why is Rails so big if Rack is so smart?

What we've just done is Rack and a little Ruby code. Rails is Rack and a lot of Ruby code.

Summary

Ruby web frameworks do a heck of a lot, but we really owe a debt of gratitude to just plain ol' rack.

Thanks

The top 100 Rack contributors as of 19th January 2021.

leahneukirchen, raggi, tenderlove, scytrin, josh, jeremyevans, ioquatix, rkh, spastorino, manveru, rtomayko, jeremy, deepj, josevalim, qerub, brainopia, oscardelben, rafaelfranca, postmodern, eileencodes, FooBarWidget, thomasklemm, yhirano55, zenspider, olleolleolle, changemewtf, krzysiek1507, jodosha, styd, kamipo, rinaldifonseca, lukaso, mtodd, tgxworld, sigmavirus24, vipulnsward, thedarkone, radar, jkowens, janko, matthewd, vais, schneems, sophiedeziel, thinkerbot, byroot, dkubb, fatkodima, SamSaffron, ender672, tonytonyjan, bestie, Drenmi, dblock, Jamie0, andrykonchin, alup, osamtimizer, lenny, felixbuenemann, evanphx, KitaitiMakoto, carlzulauf, technomancy, yeban, lanzhiheng, VBart, gjtorikian, alindeman, urielka, dayflower, eregon, davydovanton, martoche, candlerb, itnsk, esparta, AlexKVal, p8, NikolayRys, Sean0628, AlexWayfer, NickLaMuro, ChrisBr, igas, tomelm, tompng, CJKinni, ohler55, bdewater, da2x, okuramasafumi, kirs, prathamesh-sonpatki, greysteil, hyun-park, zzak, alecclarke, ziggythehamster, pvande, and many more