The existence of Rack::Response is an enigma. After all, aren't our responses just an array? What good would a response object do when we have to return an array? The secret is that we only have to return something that acts like an array. If we provide a to_a method we can return an object instead of an array.

We can construct a valid response object like this:

class Response
  def initialize status, headers, body
    @status, @headers, @body = status, headers, body

  def to_a
    [@status, @headers, @body]

Then use it like this:

class App
  def call(env)"200", {"Content-Type" => "text/plain"}, ["Wait a minute. I'm the leader!."])



Rack::Response takes advantage of this pattern to provide response helpers. It has helpers for; setting status codes, setting headers and writing to the body.

The most useful thing Rack::Response does is wrap the body in a proxy object, which means we can listen to it for writes. If we listen to the body for writes, we can update the response live -- i.e streaming.

We can set status codes using the status accessor:

@response ="Cat Created!")
@respsonse.status = 201

And we can write to the body using an accessor:

@response =
@respsonse.body << "Cat Created!"

However, this is not the best way to write to the body. The client expects our response to have data about the size of the body. Without a Content-Length header clients tend to throw a fit.

We could manually set the Content-Length header:

@response =
body = "Cat Created!"
@respsonse.body << body
@response['Content-Length'] = body.length

But the easiest way to write to the body is through the write method. write() will figure out the size of the string, calculate the Content-Length for us, and then set the header:

@response =
@respsonse.write 'Cat Created!'

We can set any response header through the request instance by using []=:

@response ='Go on!', 302)
@response['Location'] = '/cats'

Rack::Request also helps us set cookies:

@response ='Wow!')
@response.set_cookie 'cats', 'cats are great!'

This hides a secret; cookies are just headers and we could do the same thing with:

@response ='Wow!')
@response['Set-Cookie'] = 'Properly Formatted Coookie Here'

Chances are though that we would have formatted our cookie improperly; most of us have no idea how a cookie looks, have heard of RFC 822 or even know what RFC is. set_cookie makes everything look and feel like a hash -- and we work with hashes everyday. The real world is a mess -- thank your Rack you don't have to deal with it.

Everything in our response is just headers. When we use those fancy methods they are just making it easier to set headers. Take redirect() for example, it simply sets a 302 status code and a Location header -- all of it, just headers.

At their core, requests and responses are just wrapper objects. They give us a nice API to work with instead of forcing us to manipulate things manually. They are easy to extend and build upon just like Rack apps. If we wanted to we could use these simple patterns to build an entire MVC app without any framework.