【问题标题】:How to extend an 'unloadable' Rails plugin?如何扩展“可卸载”的 Rails 插件?
【发布时间】:2023-04-24 15:58:01
【问题描述】:

我正在尝试编写一个扩展 InheritedResources 的插件。

具体来说,我想重写一些默认助手。

而且我希望它在安装后“正常工作”,无需对应用程序代码进行任何更改。

功能在模块中提供,需要包含在正确的位置。问题是在哪里? :)

第一次尝试是在我插件的 init.rb 中进行:

InheritedResources::Base.send :include, MyModule

它在生产中工作,但在开发中惨遭失败,因为 InheritedResource::Base 声明为 unloadable,因此它的代码在每次请求时都会重新加载。所以我的模块是针对第一个请求的, 然后它就消失了。

InheritedResource::Base 再次被任何使用它的控制器“拉入”:

Class SomeController < InheritedResource::Base

但是没有代码“拉入”我的扩展模块,因为它没有在任何地方被引用,除了 init.rb,它没有在每个请求上重新加载

所以现在我只是将模块手动包含在每个需要它的控制器中,这很糟糕。 我什至不能在 ApplicationController 中包含它一次,因为 InheritedResources 继承自它,因此它将覆盖任何更改。

更新

我不是在寻找有关如何“猴子补丁”的建议。扩展在生产中工作得很好。我的问题是如何在 InheritedResources 加载后准确地捕捉时刻以将我的扩展插入其中:)

更新2

再次尝试澄清:

事件的顺序是

  • a) rails 加载插件。我的插件在继承资源后加载并对其进行修补。
  • b) 开发模式请求已得到处理并正常工作
  • c) rails 卸载所有“可卸载”代码,其中包括所有应用程序代码以及 继承的资源
  • d) 另一个请求进来
  • e) rails 加载控制器,它继承自继承的资源
  • f) rails 加载继承自 application_controller 的继承资源
  • g) rails 加载 application_contrller(或者可能在这个阶段已经加载,不确定)
  • g) 请求失败,因为没有人加载我的插件来修补继承资源。插件 init.rb 文件不会重新加载

我需要捕捉 g 和 h 之间的时间点

【问题讨论】:

    标签: ruby-on-rails dependencies ruby-on-rails-plugins


    【解决方案1】:

    环境文件中的 Rails::Configuration config 允许在开发模式下的每个请求之前运行的调度程序上注册一个回调,或者在生产模式下一次。

    config.to_prepare do
       # do something here
    end
    

    问题是,我认为您的插件在运行 init.rb 文件时无法访问config。这是一种直接在调度程序中注册回调的方法。只需将其放在 init.rb 文件中即可。

    require 'dispatcher'
    ::Dispatcher.to_prepare do
        puts "hi there from a plugin"
    end
    

    警告:我不知道这可能有什么副作用。如果可能,尝试访问config 并以正确的方式注册回调。

    【讨论】:

    【解决方案2】:

    您尝试做的通常称为“MonkeyPatch”——通过“覆盖”方法改变一个模块或类的工作方式。

    这是 Rails 中的一种常见做法,但这并不意味着它是做事的最佳方式 - 如果可能,最好使用公共继承(它更明确地说明您所做的更改)。

    关于“将文件放在哪里”的问题:通常是 lib/ 目录。这可能意味着 rails 应用程序的 lib,或者 gem 或插件中的 lib 目录,如果您喜欢这类东西的话。

    例如,如果您要更改的文件是继承资源的lib/generators/rails/templates/controller.rb,那么您要做的第一件事就是将该目录结构复制到您的 lib/ 文件夹('lib/generators/rails/templates/controller. rb')

    在您的新文件中(开头为空)您可以覆盖方法。但是,您还必须使用模块/类层次结构。所以如果原来的 gem 有这个:

    module foo
      module bar
        def f1
        ...
        end
        def f2
        ...
        end
      end
      def f3
      ...
      end
    end
    

    如果你想修改 f1,你必须尊重 foo-bar 模块。

    module foo
      module bar
        def f1
        ... # your code here
        end
      end
    end
    

    现在您需要做的最后一件事是确保在正确的时间执行此代码。如果您使用应用程序的 lib/ 文件夹,则需要在 initializers/ 文件夹和 require 新文件上创建一个条目。如果您正在开发 gem/plugin,您将在该插件的“根”文件夹中拥有一个 init.rb 文件。把“要求”放在那里。

    我对@9​​87654326@ 这个东西不是很熟悉;也许我在问一些明显的问题,但是-您是否也尝试过使您的扩展模块不可加载? (如果您对模块进行了猴子补丁而不是创建新模块,则不需要这个)

    【讨论】:

    • 请在回答之前阅读问题:) a) 我没有问“如何修补补丁”。我的扩展正在工作。 b)我实际上是在询问“确保在正确的时间执行此代码”,您在最后没有实际回答(见下文)c)不,我不能将代码放入初始化程序中,我' m 创建一个插件,并且“在 install.rb 上粘贴你的初始化程序不是我正在寻找的答案 d) unloadable 确保它在开发中的每个请求上卸载。e) 猴子补丁或包含你自己的模块是完全等效的关于“在重新加载中幸存”-两者都没有:)
    • 抱歉浪费了您的时间。那我帮不了你了,祝你好运。
    最近更新 更多