Rack Middlewares

Ramaze is a Rack based framework and thus allows you to create so called Rack middlewares. Middlewares are basically objects that are stacked together in order to intercept and process sequentially each incoming request and outgoing response between Rack and Ramaze. You can think of a collection of middlewares as a stack at whose bottom lies your Ramaze app.

The flow of a Rack request (including middlewares) looks as following:

Request --> Server (Thin, Unicorn, etc) --> Rack --> Middleware(s) -->
Ramaze  --> Controller

Say we want to ban a number of users by IP, there are two ways of doing this. The first way of doing this would be to validate the user's IP in all controllers (or in a base controller). However, this approach will eventually require quite a bit of code. The easier method, as you may have guessed, is using a Rack middleware. Since middlewares are executed for each request this means we'll only have to add our code once and we're good to go.

Building the Middleware

Let's begin building our IP blacklist. For the sake of simplicity we'll hardcode the blocked IPs in an array stored inside our middleware. Go ahead and create a file called "banlist.rb" and save it somewhere in your application (and require it!). We'll begin with our basic skeleton that looks like the following:

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

  def call(env)

  end
end

First we declare a new class called "Banlist". Followed by this is our construct method that takes a single argument: an object containing the details of our Ramaze application. Next up is the call() method which also takes a single argument but this time it's an object containing our environment details such as the POST and GET data.

Let's add a list of blocked IPs to our middleware. Modify the initialize() method so that it looks like the following:

def initialize(app)
  @app    = app
  @banned = ['189.3.0.116', '193.159.244.70', '193.46.236.*']
end

We now have 3 blocked IPs. Time to actually implement the blocking mechanism in our call() method. Modify it as following:

def call(env)
  if @banned.include?(env['REMOTE_ADDR'])
    return "You have been banned!"
  else
    @app.call(env)
  end
end

So what did we do? Quite simple actually, we extracted the user's IP by calling env['REMOTE_ADDR'] and checked if it's stored in the @banned instance variable. If it is we'll block the user and show a message "You have been banned". Our final middleware looks like the following:

class Banlist
  def initialize(app)
    @app    = app
    @banned = ['189.3.0.116', '193.159.244.70', '193.46.236.10']
  end

  def call(env)
    if @banned.include?(env['REMOTE_ADDR'])
      return "You have been banned!"
    else
      @app.call(env)
    end
  end
end

Using Middlewares

Now it's time to tell Ramaze to actually use the middleware. This can be done by calling Ramaze#middleware. This method accepts a block in which one defines which middlewares to use for a specific mode and the name for this Ramaze mode (Ramaze comes with "live" and "dev").

In the block you can call two Innate#MiddlewareCompiler methods use() and run(). The use() method is used in order to add and configure a middleware, while run() is used to determine what class is used to run our Ramaze application. Unless you're using a custom class this should always be set to Ramaze.core.

Assuming we're running in "dev" mode our call will look like the following:

Ramaze.middleware :dev do
  use Banlist
  run Ramaze.core
end

The method Ramaze.middleware can no longer be used to retrieve the used middleware, instead it can only be used to set these. The method Ramaze.middleware! also no longer exists.