Search code examples
ruby-on-railsruby-on-rails-4rackrack-middleware

Reloading rails middleware without restarting the server in development


I have a rails 4 app with middleware located at lib/some/middleware.rb which is currently injected into the stack though an initializer like so:

MyApp::Application.configure.do |config|
    config.middleware.use 'Some::Middleware'
end

Unfortunately, any time I change something I need to restart the server. How can I reload it on each request in development mode? I've seen similar questions about reloading lib code with either autoloading or wrapping code in a to_prepare block but I'm unsure how that could be applied in this scenario.

Thanks, - FJM

Update #1

If I try to delete the middleware and then re-add it in a to_prepare block I get an error "Can't modify frozen array".


Solution

  • I thought that at some point Rails was smart enough replacing middleware code at runtime, but I may be wrong.

    Here is what I came up with, circumventing Ruby class loading craziness and leveraging Rails class reloading.

    Add the middleware to the stack:

    # config/environments/development.rb
    [...]
    config.middleware.use "SomeMiddleware", "some_additional_paramter"
    

    Make use of auto-reloading, but make sure that the running rails instance and the already initialized middleware object keep "forgetting" about the actual code that is executed:

    # app/middlewares/some_middleware.rb
    class SomeMiddleware
      def initialize(*args)
        @args = args
      end
    
      def call(env)
        "#{self.class}::Logic".constantize.new(*@args).call(env)
      end
    
      class Logic
        def initialize(app, additional)
          @app        = app
          @additional = additional
        end
    
        def call(env)
          [magic]
          @app.call(env)
        end
      end
    end
    

    Changes in Logic should be picked up by rails auto reloading on each request.

    I think that this actually might become a useful gem!