12

我有一个带有位于 lib/some/middleware.rb 的中间件的 rails 4 应用程序,该应用程序当前通过如下初始化程序注入堆栈:

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

不幸的是,每当我更改某些内容时,我都需要重新启动服务器。如何在开发模式下对每个请求重新加载它?我已经看到过类似的问题,即在 to_prepare 块中使用自动加载或包装代码来重新加载 lib 代码,但我不确定如何在这种情况下应用它。

谢谢,-FJM

更新#1

如果我尝试删除中间件,然后将其重新添加到 to_prepare 块中,则会收到错误“无法修改冻结数组”。

4

4 回答 4

15

我认为在某些时候 Rails 足够聪明地在运行时替换中间件代码,但我可能错了。

这是我想出的,规避 Ruby 类加载的疯狂并利用 Rails 类重新加载。

将中间件添加到堆栈中:

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

使用自动重新加载,但要确保正在运行的 rails 实例和已经初始化的中间件对象不断“忘记”实际执行的代码:

# 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

逻辑上的变化应该通过每次请求时自动重新加载的 rails 来获取。

我认为这实际上可能会成为一个有用的宝石!

于 2014-01-24T02:41:02.700 回答
1

在@phoet 的回答的基础上,我们实际上可以用这种延迟加载来包装任何中间件,我发现它更有用:

class ReloadableMiddleware
  def initialize(app, middleware_module_name, *middleware_args)
    @app = app
    @name = middleware_module_name
    @args = middleware_args
  end

  def call(env)
    # Lazily initialize the middleware item and call it immediately
    @name.constantize.new(@app, *@args).call(env)
  end
end

然后可以将它挂接到 Rails 配置中,将任何其他中间件作为其第一个参数,以字符串形式给出:

Rails.application.config.middleware.use ReloadableMiddleware, 'YourMiddleware'

或者 - 我将它打包到一个名为 的 gemreloadable_middleware中,它可以像这样使用:

Rails.application.config.middleware.use ReloadableMiddleware.wrap(YourMiddleware)
于 2019-04-20T18:31:35.707 回答
0

你不能简单地使用霰弹枪吗?如果我理解您的问题,您希望确保环境在您对代码所做的每一次更改时重新加载。这就是霰弹枪的作用。

于 2014-01-01T23:40:08.140 回答
0

在带有新的默认 Zeitwork 代码加载器的 Rails 6 中,这对我有用:

# at the top of config/application.rb, after Bundler.require    

# Load the middleware. It will later be hot-reloaded in config.to_prepare
Dir["./app/middleware/*.rb"].each do |middleware|
  load middleware
end

在它下面配置你的部分class Application,添加热重载config.to_prepare

middleware = "#{Rails.root}/app/middleware"
Rails.autoloaders.main.ignore(middleware)

# Run before every request in development mode, or before the first request in production
config.to_prepare do
  Dir.glob("#{middleware}/*.rb").each do |middleware|
    load middleware
  end
end
于 2020-06-27T13:53:34.213 回答