2

Given a Rack app that is not Rails,

builder.rb:

def app
  Rack::Builder.new{
    use Rack::Static, urls:static_paths, root:'public'

    run ThaApp
  }.to_app
end

How to inject a testing middleware using the spec_helper?

B Seven
  • 44,484
  • 66
  • 240
  • 385

2 Answers2

1

If you're using Builder (with use, run, etc.) it does not look like you can easily inject or remove middleware at runtime. Here's the code: https://github.com/rack/rack/blob/master/lib/rack/builder.rb

Notice that it builds the stack of middleware, and when you call run it instantiates the stack (called "@use") in a tree of middleware objects which each have a reference to the next one - see methods "use" and "to_app".

So: don't think Builder is designed to allow dynamically adding and subtracting middleware in the stack.

You could rebuild a new dynamic stack, or use multiple Rack apps with and without testing middleware, or do some backflips like Rails does to dynamically reconfigure the stack.

You could also add a testing middleware only in test mode, or one that can be deactivated easily so it becomes a pass-through middleware. Then your spec_helper would just set and clear the variable telling it to pass through.

Noah Gibbs
  • 765
  • 3
  • 10
  • 1
    OK, thanks. I thought since you can do it in Rails, it might be possible to do it in Rack. I am currently using a separate Rack build for the tests, but that creates duplication. I think it is better than polluting the production build with test code. – B Seven Apr 15 '15 at 15:39
  • 1
    Separate Rack builder for the tests is probably as clean a solution as you'll get here. – Noah Gibbs Apr 15 '15 at 21:48
  • 1
    Rails keeps a different non-Builder stack. If you need that for other things, great. But if you needed to build/copy that just for this problem, it would be too much code and complexity for the result. – Noah Gibbs Apr 15 '15 at 21:48
  • Is there any special reason that the middleware cannot be changed after the build? I mean, if the middlewares are stored in `@use`, can you just update `@use`? Or is there more going on? – B Seven Apr 15 '15 at 21:59
  • 1
    More going on. Each middleware is constructed before it can be used, and takes a reference to its nearest (inward) neighbor to that constructor. You need to remake the middleware stack from the `@use` stack. That's fine if you're sure you can do that, but any reference to the old stack will behave incorrectly. Normally you only make one "run" call, which would foil your changes to `@use`. – Noah Gibbs Apr 16 '15 at 15:00
0

Since I want to prepend to the middleware stack, solving for this particular use case was easy.

Given a app defined as above named "app", add the new middleware: use ...

def new_app
  Rack::Builder.new do
    use ...
    use ...

    run app
  end.to_app
end
B Seven
  • 44,484
  • 66
  • 240
  • 385