Rack::Deflater 和 Rack::URLMap

Rack::Deflater and Rack::URLMap

我想根据响应的大小 body 有条件地启用 Rack::Deflater,如下所示:

use Rack::Deflater, :if => lambda { |*, body| body.map(&:bytesize).reduce(0, :+) > 512 }

如果我在我的一个 Rack 应用程序中插入 Rack::Deflater 中间件,这个 lambda 可以正常工作; body 是一个字符串数组。但是,如果我在 Rack::URLMap 之前(在 config.ru 中)插入 Rack::Deflater 中间件,它不会;因为 body 现在是 Rack::BodyProxy object.

我查看了 Rack::BodyProxy 的定义,它似乎有点不透明。没有明确的方法从这个 object 中解析实际的 body; body.bodyreturnsnil。在这种情况下,easiest/best 确定响应 body 大小的方法是什么,而不是将 Rack::Deflater 中间件向下移动到我的每个 Rack 应用程序中?

tl;dr:在给定 Rack::BodyProxy object 的情况下,如何确定响应 body 的大小?

看起来 Rack::BodyProxy 确实提供了 #each 方法作为问题 rack/rack#434 的解决方法。此方法 returns 对私有 @body ivar 的元素进行枚举。在调用链中添加一个 .each 可以解决 Rack::BodyProxy 对象的这个问题,并且对于更普通的基于数组的主体来说这是一个空操作,所以现在这让我到达了我需要去的地方。但是,我仍然想了解为什么我会得到这些对象,以及是否有更好的方法来处理它们。

这是修改后的有效解决方案:

use Rack::Deflater, :if => lambda { |*, body| 
  body.each.map(&:bytesize).reduce(0, :+) > 512 
}

更新: 哎呀,有时 body 是一个 Rack::BodyProxy 对象,而 body.body 是一个 Rack::Response 对象。这有点失控了……!这是我现在使用的解决方案:

use Rack::Deflater, :if => lambda { |*, body|
  body.map(&:bytesize).reduce(0, :+) > 512 \
    if body.respond_to?(:map) \
    or body.respond_to?(:each) and (body = body.each).respond_to?(:map)
}

远非优雅...