21

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?

Amol Pujari
  • 2,280
  • 20
  • 42
  • 3
    `use` is *not* part of standard Ruby (and [it is *not* a keyword](http://ruby-doc.org/docs/keywords/1.9/)). Where did it come from? –  Aug 16 '12 at 07:13
  • 1
    check out [this](http://confreaks.com/videos/961-mwrc2012-rack-middleware-as-a-general-purpose-abstraction), and [this](https://github.com/postrank-labs/goliath/wiki/Middleware), I thought a keyword like thing it is – Amol Pujari Aug 16 '12 at 07:16
  • 4
    Not the most intuitive of rdocs, but "ruby rack api" (and then scrolling the methods to find `use`) resulted in [this link](http://rack.rubyforge.org/doc/classes/Rack/Builder.html#M000171). Note how it is just a method. This is probably discussed further in other Rack documentation/guides. (Updating the title/question with new information/context will likely lead to more focused -- and relevant -- responses.) –  Aug 16 '12 at 07:31

1 Answers1

35

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.
Community
  • 1
  • 1