Question

Recently I happened to see this word in Ruby code, use, when I was going through some code related to goliath, middleware etc. Looks like it is different from include/extend, and require.

Can somebody explain why this use keyword exists, and how it is different from include/require? How does it work, when to use it?

Was it helpful?

Solution

The Documentation

As people have pointed out, use is not a Ruby keyword, it is in fact a method of the Rack::Builder class:

use(middleware, *args, &block)

Specifies middleware to use in a stack.

This documentation (pointed out by @user166390) describes it like this:

Rack::Builder implements a small DSL to iteratively construct Rack applications.

Example:

app = Rack::Builder.new {
  use Rack::CommonLogger
  use Rack::ShowExceptions
  map "/lobster" do
    use Rack::Lint
    run Rack::Lobster.new
  end
}

Or

app = Rack::Builder.app do
  use Rack::CommonLogger
  lambda { |env| [200, {'Content-Type' => 'text/plain'}, 'OK'] }
end

use adds a middleware to the stack, run dispatches to an application.

The Source Code

I'm not too familiar with the Rack::Builder source code, but it looks like each time you call use with a new middleware module, it gets added to an array, and each module is run/injected in the reverse order in which it was added (last-in-first-out order, a.k.a. stack order). The result of running the previous middleware is passed to the next middleware in the stack via inject:

  1. Lines 53-56:

    def initialize(default_app = nil,&block)
      # @use is parallel assigned to [].
      @use, @map, @run = [], nil, default_app
      instance_eval(&block) if block_given?
    end
    
  2. Lines 81-87:

    def use(middleware, *args, &block)
      if @map
        mapping, @map = @map, nil
        @use << proc { |app| generate_map app, mapping }
      end
      # The new middleware is added to the @use array.
      @use << proc { |app| middleware.new(app, *args, &block) }
    end
    
  3. Lines 131-135:

    def to_app
      app = @map ? generate_map(@run, @map) : @run
      fail "missing run or map statement" unless app
      # The middlewares are injected in reverse order.
      @use.reverse.inject(app) { |a,e| e[a] }
    end
    

Additional resources

  1. A Quick Introduction to Rack.
  2. Ruby on Rack #2 - The Builder.
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top