如何将config.ru文件转换为单个Rack应用程序?

时间:2012-07-16 21:12:26

标签: ruby rack

我有一个开始有重复代码的config.ru文件:

map '/route1' do
  run SampleApp.new
end

map '/route2' do
  run SampleApp.new
end

我想将此config.ru文件转换为自己的Rack应用程序,以便我所要做的就是:

map '/' do
  run MyApp.new
end

创建自己的Rack应用程序的正确方法是什么?具体来说,我如何创建一个类,以便我可以在我的类中使用map方法来定义一堆路由?


解决方案:

这是一个有效的解决方案:

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

我以前试过这个,但是无法让它工作,因为我试图将实例变量传递给map块。例如:

def initialize
  @sample_app = SampleApp.new
  @app = Rack::Builder.new do
    map '/route1' do
      run @sample_app   # will not work
    end
  end
end

这不起作用的原因是因为传递给map的阻止是evaluated in the context of a Rack::Builder instance

但是,如果我传递一个局部变量,它将起作用:

def initialize
  sample_app = SampleApp.new
  @app = Rack::Builder.new do
    map '/route1' do
      run sample_app   # will work
    end
  end
end

4 个答案:

答案 0 :(得分:10)

config.ru中使用的DSL在Rack::Builder中定义。使用config.ru时,文件的内容将传递给Builder的实例以创建Rack应用程序。您可以直接在代码中直接执行此操作。

例如,您可以获取现有config.ru的内容,并从中创建一个新类:

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

您需要使用call方法,以便您的班级成为Rack应用,但您只需将请求转发到使用Builder创建的应用。然后,您可以创建使用新应用的新config.ru

require './my_app'

run MyApp.new

答案 1 :(得分:4)

这是真正的基本示例。您应该考虑Rack::Response来处理响应而不是自己构建它,但它可以让您对基本的Rack中间件的工作原理有所了解:

class MyApp
  def call(env)
    request = Rack::Request.new(env)
    headers = { 'Content-Type' => 'text/html' }

    case request.path
    when '/'
      [200, headers, ["You're at the root url!"]]
    when '/foo'
      [200, headers, ["You're at /foo!"]]
    else
      [404, headers, ["Uh oh, path not found!"]]
    end
  end
end

编辑:

将多个Rack应用程序映射为一个:

class RootApp
  def call(env)
    [200, {'Content-Type' => 'text/html' }, ['Main root url']]
  end
end

class FooApp
  def call(env)
    [200, {'Content-Type' => 'text/html' }, ['Foo app url!']]
  end
end

class MyApp
  def initialize
    @apps = {}
  end

  def map(route, app)
    @apps[route] = app
  end

  def call(env)
    request = Rack::Request.new(env)

    if @apps[request.path]
      @apps[request.path].call(env)
    else
      [404, {'Content-Type' => 'text/html' }, ['404 not found']]
    end
  end
end

app = MyApp.new
app.map '/', RootApp.new
app.map '/foo', FooApp.new

run app

答案 2 :(得分:3)

如何使用URLMap?

app = Rack::URLMap.new(
  "/path1" => Path1App.new,
  "/path2" => Path2App.new
)

run app

答案 3 :(得分:0)

我这样做:

class MyApp 
  def call(env)
    @env = env

    # REQUEST_URI is still encoded; split before decoding to allow encoded slashes
    @path = env['REQUEST_URI'].split('/')

    # REQUEST_URI starts with a slash, so delete blank first element
    @path.delete_at(0)

    @path.each_index do |i|
      @path[i]= CGI.unescape(@path[i])
    end

    route()
  end
end

然后route()可以做任何想要路由请求的事情,例如:

class MyApp 
  def route
    m = @env['REQUEST_METHOD']
    @section = @path.shift

    if not @section
      home()
    elsif @section == 'route1' and m == 'GET'
      route1()
    # else ...
    end
  end
end