わかった気になるRack Middleware
使いたい機能のみを有効にできるmiddlewareという仕組みがrackにはある。
たとえば、ロギングはmiddlewareで実装するためアプリケーションと疎結合な状態になる。
rackアプリケーションにmiddlewareを追加する方法は、config.ru などに有効にしたい機能をuseというキーワードで登録にしていく。
ちなみに、config.ruはRack::Builderのインスタンスコンテキストで実行する。
# config.ru
use LoggerMiddleware
run lambda { |env|
[200, {'Content-Type' => 'text/plain'}, ['OK']]
}
middlewareはミルフィーユみたいなもので、リクエストを受け取るとレスポンスを生成するために、middlewareを通過して最終的なレスポンスを評価する。
middlewareとして使うクラスにはcallというメソッドがあれば動作し、
middlewareを使った最小限なrackアプリケーションは下記な感じで表現できる。
class Middleware
def initialize(app)
@app = app
end
def call(env)
pp 'hello'
@app.call(env)
end
end
app = Rack::Builder.app do
use Middleware
run lambda { |env|
[200, {'Content-Type' => 'text/plain'}, ['OK']]
}
end
server = Rack::Server.new(app: app)
server.start
curl 'http://localhost:8080' # => OK
middlewareの動かし方がわかったのでミルフィーユに深入りしてみる。
useキーワードによって登録されたmiddlewareクラスがどんな感じでミルフィーユ化するのかを調べたところ、Procでラップしたオブジェクトを配列に追加して、最後にinjectで数珠繋ぎにしている、ということがわかった。
下記は、ミルフィーユ化処理を簡単に書き直したコードである。
class Middle
def initialize(app)
@app = app
end
def call(env)
puts 'hell middleware'
@app.call(env)
end
end
class Builder
def initialize(&block)
@use = []
instance_eval(&block) if block
end
def use(klass, &block)
@use << Proc.new { |app| klass.new(app) }
end
def run
app = ->(x){ '200' }
puts @use.inject(app) { |a, e| e.call(a) }.call('')
end
end
Builder.new {
use Middle
use Middle
}.run
hell middleware
hell middleware
200
以上。
-
category:
- ruby