Ruby 如何将config.ru文件转换为单机架应用程序?

Ruby 如何将config.ru文件转换为单机架应用程序?,ruby,rack,Ruby,Rack,我有一个config.ru文件,它开始有重复的代码: map '/route1' do run SampleApp.new end map '/route2' do run SampleApp.new end 我想将这个config.ru文件转换为它自己的机架应用程序,这样我所要做的就是: map '/' do run MyApp.new end 创建您自己的机架应用程序的正确方法是什么?具体来说,如何创建一个类,以便在类中使用map方法来定义一组路由 解决方案: 以下是一个

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

map '/route1' do
  run SampleApp.new
end

map '/route2' do
  run SampleApp.new
end
我想将这个config.ru文件转换为它自己的机架应用程序,这样我所要做的就是:

map '/' do
  run MyApp.new
end
创建您自己的机架应用程序的正确方法是什么?具体来说,如何创建一个类,以便在类中使用
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
的块正在被删除

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

def initialize
  sample_app = SampleApp.new
  @app = Rack::Builder.new do
    map '/route1' do
      run sample_app   # will work
    end
  end
end
这是一个非常基本的例子。您可能应该研究
Rack::Response
来处理响应,而不是自己构建响应,但它可以让您很好地了解基本机架中间件的工作原理:

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

编辑:

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

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
我这样做:

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

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

例如,您可以获取现有
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
您需要调用
方法
,以便您的类是一个框架应用程序,但您可以将请求转发到使用
Builder
创建的应用程序上。然后,您可以创建使用新应用程序的新
config.ru

require './my_app'

run MyApp.new

使用URLMap怎么样

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

run app

只需在
#call
方法中检查请求路径并添加条件statements@injekt你能提供一个这样的例子吗?在我的答案中检查我的例子,如果它有帮助的话,我可以添加更多,但这是最基本的…这是有帮助的。但是,我需要在定义的每个路径上运行Rack应用程序。所以我认为这个解决方案行不通。你知道我怎么能做到吗?这是一个越来越近的问题。然而,马特的答案有点接近我想要的。在他的回答中,我建议
Rack::Request
而不是自己分割信息。“它能承受所有的负荷,看起来更漂亮。”“嗯,我只是仔细看了一下,不知道这在这里会起什么作用。”。当我需要处理查询字符串或POST表单数据时,我将它用于
params()
方法,但我没有看到任何有助于解析和路由路径的方法……这正是我正在尝试做的,事实上,我已经尝试过这件事。我遇到的问题是,我想将实例变量从类传递到映射块中,但这不起作用,因为它们是在一个新的
Rack::Builder
实例的上下文中进行计算的。你知道有什么方法可以将
Rack::Builder
子类化,这样我就不必实例化
Rack::Builder
实例了吗?@Andrew我不确定子类化是一种方法,因为
Rack::Builder
是递归创建的,我认为您不想妨碍它。@Andrew避免在
Builder
上下文中计算块的一种可行方法是首先创建Builder对象,然后显式调用该对象上的DSL方法,例如,
builder=Rack::builder.new
后跟
builder.map'/route1'do…
等。即使您希望保留“普通”DSL方法,您也可以这样做。