How to turn a config.ru file into a single Rack application?

rack, ruby

Solution

The DSL used in `config.ru` is defined in `Rack::Builder`. When using a `config.ru`, contents of the file are passed to an instance of `Builder` to create the Rack app. You can do this directly yourself in code.

For example, you can take the contents of your existing `config.ru`, and create a new class from it:

require 'rack'

class MyApp

  def initialize
    @app = Rack::Builder.new do
      # copy contents of your config.ru into this block
      map '/route1' do
        run SampleApp.new
      end

      map '/route2' do
        run SampleApp.new
      end
    end
  end

  def call(env)
    @app.call(env)
  end
end

You need the `call` method so that your class is a Rack app, but you can just forward the request on to the app you create with `Builder`. Then you can create your new `config.ru` that uses your new app:

require './my_app'

run MyApp.new

Problem

I have a config.ru file that is starting to have duplicate code: ``` map '/route1' do run SampleApp.new end map '/route2' do run SampleApp.new end ``` I would like to turn this config.ru file into its own Rack application so that all I have to do is: ``` map '/' do run MyApp.new end ``` What is the correct way to create your own Rack application? Specifically, how can I create a class so that I can use the `map` method within my class to define a bunch of routes? Solution: Here is a working solution: ``` class MyApp def initialize @app = Rack::Builder.new do # copy contents of your config.ru into this block map '/route1' do run SampleApp.new end map '/route2' do run SampleApp.new end end end def call(env) @app.call(env) end end ``` I tried this before, but couldn't get it to work because I was trying to pass instance variables to the `map` blocks. For example: ``` def initialize @sample_app = SampleApp.new @app = Rack::Builder.new do map '/route1' do run @sample_app # will not work end end end ``` The reason this will not work is because the block being passed to `map` is being evaluated in the context of a `Rack::Builder` instance. However, it will work if I pass a local variable: ``` def initialize sample_app = SampleApp.new @app = Rack::Builder.new do map '/route1' do run sample_app # will work end end end ```

Original source