ruby-on-railsrubyruby-on-rails-5ruby-on-rails-5.2actiondispatch

Rail 5 swap ActionDispatch::Reloader with a custom reloader


We have a use case for mounting a mock engine to process sessions when developing locally in which a custom session middleware calls the mock engine via Net::http request when a request comes through.

When there is code change, the reloader is triggered and here calls the ActiveSupport::Dependencies to start unloading. Then the request is pass down to our custom session middleware and the http request is fired. However since the http request calls to a mountable engine, it goes thought the same middlewares again and the reloader unloads all the dependencies again which cause the first reload to timeout. So the goal is to be able to skip the reload for the second request.

I added the follow code to ActionDispatch::Reloader here and it does exactly what I wanted.

class Reloader < Executor
  def initialize(app, executor)
    super(app, executor)
  end

  def call(env)
    request = ActionDispatch::Request.new(env)
    return @app.call(env) if skip_request?(request)
    super(env)
  end

  def skip_request?(request)        
    request.path_info.start_with?('/session')
  end
end

Then I want to make this cleaner figured to pull that out completely to a module and just do a swap like this from an initializer

app.config.middleware.swap(::ActionDispatch::Reloader, MyModule::CustomReloaderMiddleware)

Here is the module

require 'action_dispatch'

module MyModule
  class CustomReloaderMiddleware < ActionDispatch::Executor
    def initialize(app, executor)
      @app, @executor = app, executor
    end

    def call(env)
      request = ActionDispatch::Request.new(env)
      return @app.call(env) if skip_request?(request)
      super(env)
    end

    def skip_request?(request)
      request.path_info.start_with?('/session')
    end
  end
end

But I ran into a couple issues.

Uncaught exception: wrong number of arguments (given 1, expected 2) from for the initialize in MyModule, when I starts the server. Then I tried the following

#1

def initialize(app, executor = nil)
  @app, @executor = app, executor
end

#2

def initialize(app, executor = nil)
  @app, @executor = app, ActiveSupport::Reloader
end

Both of them starts the service correctly and I see the request going through this middleware but it does not reload the code.. So I wondered what is the correct way of swapping ActionDispatch::Reloader with a custom reloader ?


Solution

  • You need to pass your middleware's additional argument to the swap call:

    app.config.middleware.swap(::ActionDispatch::Reloader, MyModule::CustomReloaderMiddleware, app.reloader)
    

    That's the same argument given when ActionDispatch::Reloader is first added -- it's the application's reloader, which is a more specifically configured subclass of AS::Reloader (so you were on the right track).