"use" keyword/word in Ruby/Rails/Rack code
Asked Answered
S

1

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?

Salyers answered 16/8, 2012 at 7:11 Comment(3)
use is not part of standard Ruby (and it is not a keyword). Where did it come from?Kwasi
check out this, and this, I thought a keyword like thing it isSalyers
Not the most intuitive of rdocs, but "ruby rack api" (and then scrolling the methods to find use) resulted in this link. 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.)Kwasi
R
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.
Rosser answered 7/3, 2014 at 8:42 Comment(1)
First link is dead. Alternate: rubylearning.com/blog/2013/04/02/whats-rackRinglet

© 2022 - 2024 — McMap. All rights reserved.