我有一个开始有重复代码的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
答案 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