The Role of Rack in The Ruby Ecosystem
Software Development
Nicolas Nisoria
2022-08-25

The Role of Rack in The Ruby Ecosystem

Learn more about the role of rack in the ecosystem of Ruby from our our expert and up skill your ruby game.

While working with Ruby web frameworks it’s common to take things for granted. We know the framework will handle the HTTP requests and execute the middleware logic for us. As we get more curious we start wondering what is behind the scenes, there’s where we start hearing about Rack.

What is Rack?

The project is described as “A modular Ruby web server interface”. Rack is the interface that let us create web applications unifying the API for web servers, web frameworks, and middleware.

rack ruby scheme

As described in the above picture, Rack acts as a middleman between our Web Application and the Application Server, it wraps the HTTP requests in the simplest way possible.

Rack Application

A Rack application is a Ruby object (not a class) that responds to call. It takes exactly one argument, the environment and returns a non-frozen Array of exactly three values:

  • The status,

  • the headers,

  • and the body.

    You can find the detailed specification of a Rack Application here.

require 'rack'

class RackApp
  def call(env)
        status = 200
        headers = { 'Content-Type' => 'text/html' }
        body = ['<h1>My Rack App<h1>']

    [status, headers, body]
  end
end

Rack::Handler

Handlers connect web servers with Rack. Rack includes Handlers for Thin, WEBrick,FastCGI, CGI, SCGI and LiteSpeed. Each application server that supports Rack should provide a handler to create the connection (Puma has its own handler).Handlers usually are activated by calling MyHandler.run(myapp). A second optional hash can be passed to include server-specific configuration.

Using Thin application server

Rack::Handler::Thin.run(app)

The default file to add the configuration is config.ru and you can execute itusing rackup command in your console.

Rack Middleware

Rack allows us to create middleware applications (applications between our main web application and the application server). These middleware applications are chained together and executed sequentially.

Rack Middleware must implement all the specifications of a Rack Application and meet the following points:

  • It must be a class,
  • have an initializer that receives only one parameter (the main application),
  • and call the next middleware or the application.
class RackMiddleware
def initialize(app)
@app = app
end
def call(env)
@app.call(env)
end
end

Rack into Practice

Now that we know the basics, we are ready to create our first Rack Application with Rack Middleware and run it using Puma (Application Server).

Install the dependencies

Make sure you have the rack gem and the puma gem installed.

gem install rack
gem install puma

Create the configuration file

First, we have to create a file called config.ru and this file will make use of the Rack::Builder DSL to run the application and add the middleware.

Add the Rack Application

Within the config.ru file, we will add the simple Rack Application we defined in the previous sections.

# config.ru

class RackApp
  def call(env)
    status = 200
    headers = { 'Content-Type' => 'text/html' }
    body = ['<h1>My Rack App<h1>']

    [status, headers, body]
  end
end

Add the Rack Middleware

Here we will make a small modification to our simple middleware and now it will add the server software to our HTML body after being executed.

# config.ru

class RackMiddleware
  def initialize(app)
    @app = app
  end

  def call(env)
    status, headers, body = @app.call(env)

    body << env['SERVER_SOFTWARE']

    [status, headers, body]
  end
end

Run the Application Server

As a last step, we will run the server and see our application running. Our config.ru file will look as follows:

# config.ru

class RackApp
  def call(env)
    status = 200
    headers = { 'Content-Type' => 'text/html' }
    body = ['<h1>My Rack App<h1>']

    [status, headers, body]
  end
end

class RackMiddleware
  def initialize(app)
    @app = app
  end

  def call(env)
    status, headers, body = @app.call(env)

    body << env['SERVER_SOFTWARE']

    [status, headers, body]
  end
end

use RackMiddleware
run RackApp.new

In the last lines, we specify the middleware using use and we run the application using run.We are ready to execute rackup in our console and see the server running. We can check the port where our application is running and we should see something like this after accessing it:

rack server text

Conclusions

Sometimes is good to go back to the basics and learn about the insights into the technology we work with. Learning Rack give us a clear overview of the architecture and reveals the “magic” behind the Ruby Web Frameworks.

Ruby Developer Offer

Read More

GraphQL Ruby. What about performance?

Rails and Other Means of Transport

Rails Development with TMUX, Vim, Fzf + Ripgrep