如何扩展'unloadable'Rails插件?

时间:2010-03-07 20:03:12

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

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

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

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

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

第一次尝试是在我的插件的init.rb中执行此操作:

InheritedResources::Base.send :include, MyModule

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

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

Class SomeController < InheritedResource::Base

但是没有代码可以“拉入”我的扩展模块,因为它除了init.rb之外没有被引用,每个请求都 重新加载

所以现在我只是手动将模块包含在需要它的每个控制器中。 我甚至无法在ApplicationController中包含它一次,因为InheritedResources继承它,因此它将覆盖任何更改。

更新

我不是在寻找关于如何'猴子补丁'的建议。该扩展正在生产中非常好。我的问题是如何在加载InheritedResources之后准确捕捉时刻以将我的扩展插入其中:)

UPDATE2

另一种澄清的尝试:

事件的顺序是

  • a)导轨加载插件。我的插件在inherited_resources之后加载并修补它。
  • b)提供开发模式请求并且正常工作
  • c)rails卸载包含所有应用程序代码的所有“可卸载”代码 inherited_resources
  • d)另一个请求进入
  • e)rails load controller,它继承自继承的资源
  • f)rails加载继承自application_controller
  • 的继承资源
  • g)rails加载application_contrller(或者可能是它已经在此阶段加载,不确定)
  • g)请求失败,因为没有人加载我的插件来修补inherited_resources。插件init.rb文件未重新加载

我需要抓住g和h之间的时间点

2 个答案:

答案 0 :(得分:4)

环境文件中的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并以正确的方式注册回调。

答案 1 :(得分:-1)

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

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

关于“放置文件的位置”的问题:它通常是lib /目录。这可能意味着rails应用程序的lib,或者gem或插件中的lib目录,如果您遇到这种情况。

例如,如果要更改的文件是继承资源的lib/generators/rails/templates/controller.rb,则首先要做的是在lib /文件夹中复制该目录结构('lib / generators / rails / templates / controller.rb')

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

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 /插件,那么您将在该插件的“root”文件夹中有一个init.rb文件。把'需要'放在那里。

我对这个unloadable的东西不太熟悉;也许我问了一些明显的东西 - 但你是否尝试过让你的扩展模块无法加载? (如果您对模块进行了monkeypatched而不是创建新模块,则不应该需要这样做)