1 of 22

Rack basics

2 of 22

Hello!

I am Enol Iglesias

You can find me at:

  • @enoliglesias (github, twitter, instagram…)
  • undefinedmethod.com

3 of 22

Rack, a modular Ruby webserver interface

Rack provides a minimal, modular, and adaptable interface for developing web applications in Ruby.

By wrapping HTTP requests and responses in the simplest way possible, it unifies and distills the API for web servers, web frameworks, and software in between (the so-called middleware) into a single method call.

4 of 22

What Rack does

Unicorn

Passenger

Puma

WEBrick

Rack

Rails

5 of 22

How it works

It must respond to call

The call method must accept a single argument

This argument is typically called env or environment.

The call method must return an array of three elements

That three elements, the response, are:

  • HTTP status
  • Headers
  • Body

6 of 22

The environment object

The environment object is a Hash that contains info about the request. Some of that info are:

  • PATH_INFO
  • QUERY_STRING
  • REMOTE_ADDR
  • REMOTE_HOST
  • REQUEST_METHOD
  • rack.version
  • rack.url_scheme

7 of 22

The response

Status

An HTTP status.

  • 200 (Ok)
  • 404 (Not found)
  • 403 (Forbidden)
  • Etc...

Headers

It must be a Hash (key-value pairs).

  • Content-Type
  • User-Agent
  • Authorization
  • Etc...

Body

The data returned by the server. It must respond to each, and yield string values.

8 of 22

Basic Rack app examples

Class based

require 'rack'

class Wadus

def call(env)

['200', {'X-Wadus' => 'Foo'}, ['Class based so simple rack app']]

end

end

Rack::Handler::WEBrick.run Wadus.new

9 of 22

Basic Rack app examples

Proc based

require 'rack'

app = -> (env) do

['200', {'X-Wadus' => 'foo'}, ['Proc based so simple rack app']]

end

Rack::Handler::WEBrick.run app

10 of 22

Basic Rack app examples

config.ru

run Proc.new { |env| [['200', {'X-Wadus' => 'foo'}, ['Bar']] }

This file, is a rack config file. It doesn’t need to require rack.

You can run this kind of applications with rackup

11 of 22

Rack middlewares

A middleware is a component (a single and simple rack application) between the client and the server, processing inbound requests and outbound responses.

12 of 22

Data flow

13 of 22

Basic Rack middleware

class Wadusdef initialize(app)� @app = app� end�� def call(env)� # Whatever you want to do before the rack middlewares stack execution� status, headers, body = @app.call(env)� # Whatever you want to do after the rack middlewares stack execution� [status, headers, body]� endend

14 of 22

Putting all together

class LoggerMiddleware

def initialize(app)

@app = app

end

def call(env)

before = Time.now.to_f

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

after = Time.now.to_f

puts "[LOGGER] Request time: #{after - before} sec."

[status, headers, body]

end

end

require './logger_middleware'��class Wadusdef call(env)� ['200', {'X-Wadus' => 'Foo'}, ['Class based so simple rack app']]� endend��use LoggerMiddleware�run Wadus.new

15 of 22

Rails on Rack

Rails.application is the primary Rack application object of a Rails application.

Rails server does the basic job of creating a Rack::Server object and starting the webserver.

16 of 22

Rails middleware stack

  • GET /wadus
  • Server parses the request
  • Rails::application
  • All the middleware stack

(wait for it...)

17 of 22

Rails middleware stack list

  • Rack::Sendfile
  • ActionDispatch::Static
  • Rack::Lock
  • #<ActiveSupport::Cache::Strategy...
  • Rack::Runtime
  • Rack::MethodOverride
  • ActionDispatch::RequestId
  • Rails::Rack::Logger
  • ActionDispatch::ShowExceptions
  • WebConsole::Middleware
  • ActionDispatch::DebugExceptions
  • ActionDispatch::RemoteIp
  • ActionDispatch::Reloader
  • ActionDispatch::Callbacks
  • ActiveRecord::Migration::CheckPending
  • ActiveRecord::ConnectionAdapters...
  • ActiveRecord::QueryCache
  • ActionDispatch::Cookies
  • ActionDispatch::Session::CookieStore
  • ActionDispatch::Flash
  • ActionDispatch::ParamsParser
  • Rack::Head
  • Rack::ConditionalGet
  • Rack::ETag
  • Warden::Manager
  • ActionDispatch::Static
  • OurApp::Application.routes

18 of 22

Adding middleware to Rails stack

We can add a new middleware to the middleware stack of our Rails application configuring it in config/environments/* files:

  • config.middleware.use(new_middleware, args)
    • Adds the new middleware at the bottom of the middleware stack.

  • config.middleware.insert_before(existing_middleware, new_middleware, args)
    • Adds the new middleware before the specified existing middleware in the middleware stack.

  • config.middleware.insert_after(existing_middleware, new_middleware, args)
    • Adds the new middleware after the specified existing middleware in the middleware stack.

19 of 22

Modifyng Rails middleware stack

We can modifyng the middleware stack in config/environments/* files:

  • config.middleware.swap(middleware_to_replace, middleware_to_set)
    • Swap an existing middleware in the middleware stack.

  • config.middleware.delete(middleware_to_delete)
    • Delete an existing middleware in the middleware stack.

  • config.middleware.clear
    • Clear the existing Rails middleware stack. Then, you can set your custom stack in configu.ru file.

20 of 22

Dummy example in Rails

class RequestTimeMiddlewaredef initialize(app)� @app = app� enddef call(env)� before = Time.now.to_f� status, headers, body = @app.call env� time = "#{(Time.now.to_f - before).to_s} sec"� headers['X-Request-Time'] = time�� [status, headers, body]� endend

config.middleware.insert_before "Rack::Sendfile", "RequestTimeMiddleware"

Stored in app/middleware/

Added in config/environments/*

21 of 22

Some ideas for middlewares

  • Caching
  • Authentication
  • Redirection
  • Trapping spam
  • Logging
  • Parsing the request object
  • ¿Any idea?

22 of 22

Thanks!

Any questions ?�

You can find me at:

  • @enoliglesias (github, twitter, instagram…)
  • undefinedmethod.com

Resources