改进 hacky ruby 'method_alias' 修复冲突的 redmine 插件?
Improving on hacky ruby 'method_alias' fix for conflicting redmine plugins?
使用 redmine 3.x,我在两个插件之间存在依赖冲突 - redmine subtask list accordion and Subtask list inherited fields。尝试查看问题时同时安装两者会引发 500 个错误。
ActionView::Template::Error (undefined method `sla_has_grandson_issues?' for #<#<Class:0x000056319e27d668>:0x00007f237ad02588>):
1: <% if sla_has_grandson_issues?(@issue) %>
2: <%= content_for :header_tags do
3: stylesheet_link_tag(sla_use_css, :plugin => "redmine_subtask_list_accordion") +
4: javascript_include_tag("subtask_list_accordion" + (subtask_tree_client_processing? ? "_client" : ""), :plugin => "redmine_subtask_list_accordion")
plugins/redmine_subtask_list_accordion/app/views/issues/_subtask_list_accordion_partial.html.erb:1:in `_292e8187f64bee60c61b7b15c99630ab'
经过大量试验和错误后,我们通过将以下内容添加到第一个插件的原始源代码中解决了这个问题:
included do
alias_method :render_descendants_tree_original, :render_descendants_tree
alias_method :render_descendants_tree, :switch_render_descendants_tree
alias_method :sla_use_css, :sla_use_css
alias_method :switch_render_descendants_tree, :switch_render_descendants_tree
alias_method :render_descendants_tree_accordion, :render_descendants_tree_accordion
alias_method :expand_tree_at_first?, :expand_tree_at_first?
alias_method :sla_has_grandson_issues?, :sla_has_grandson_issues?
alias_method :subtask_tree_client_processing?, :subtask_tree_client_processing?
alias_method :subtask_list_accordion_tree_render_32?, :subtask_list_accordion_tree_render_32?
alias_method :subtask_list_accordion_tree_render_33?, :subtask_list_accordion_tree_render_33?
alias_method :subtask_list_accordion_tree_render_34?, :subtask_list_accordion_tree_render_34?
这是原代码:
其中有来自上述源代码的前两个 alias_method 调用。
通过为原始 class 中的每个方法创建一个具有相同名称的别名方法,代码可以正常工作。然而,这似乎是一个 hacky 修复,我不明白 为什么 它有效。有人可以解释为什么修复有效以及如何正确重写它吗?
alias_method
在调用它的上下文中创建命名方法的 copy。
现在,如果原始方法(alias_method
的第二个参数)在继承链的某处定义并在以后以任何方式更改,您仍然拥有未更改的副本。这或许可以解释您看到的行为。
至于重写:根据经验,丢弃所有方法别名(在两个插件中)并使用 Module#prepend
。 This SO Answer 很好地概述了(猴子)修补 Ruby 代码的好坏技术。
修补 Rails' 视图助手,因为 Rails 处理它们的方式(结果可能因代码加载顺序而异),这在您的插件中似乎是这种情况。尽可能避免它或创建新的辅助模块并使用类似
的方法将它们添加到相关控制器中
module RedmineSubtaskListAccordion
module IssuesHelper
def render_descendants_tree(issue)
if sla_has_grandson_issues?(issue) && !subtask_tree_client_processing?
render_descendants_tree_accordion(issue)
else
super # this will call the stock Redmine IssuesHelper#render_descendants_tree method
end
end
end
end
# in init.rb:
IssuesController.class_eval do
helper RedmineSubtaskListAccordion::IssuesHelper
end
我刚才碰巧写了一篇关于这件事的 blog post。希望在这里引用它是可以的。
显然,如果两个插件期望它们正在更改的某个 Redmine 方法以其在现有 Redmine 中的行为方式运行,则可能仍然存在冲突,但根据我的经验,删除方法别名并且不尝试猴子修补已经存在的帮助程序会有所帮助避免很多问题。
更新:您链接到的那个特定模块中的大多数其他方法实际上不属于那里,但例如可以混合到 Issue
模型中(如 sla_has_grandson_issues?
或声明为方法在顶级插件命名空间中(所有设置和 Redmine 版本相关的东西 - 例如 RedmineSubtaskListAccordion.tree_render_34?
)。
使用 redmine 3.x,我在两个插件之间存在依赖冲突 - redmine subtask list accordion and Subtask list inherited fields。尝试查看问题时同时安装两者会引发 500 个错误。
ActionView::Template::Error (undefined method `sla_has_grandson_issues?' for #<#<Class:0x000056319e27d668>:0x00007f237ad02588>):
1: <% if sla_has_grandson_issues?(@issue) %>
2: <%= content_for :header_tags do
3: stylesheet_link_tag(sla_use_css, :plugin => "redmine_subtask_list_accordion") +
4: javascript_include_tag("subtask_list_accordion" + (subtask_tree_client_processing? ? "_client" : ""), :plugin => "redmine_subtask_list_accordion")
plugins/redmine_subtask_list_accordion/app/views/issues/_subtask_list_accordion_partial.html.erb:1:in `_292e8187f64bee60c61b7b15c99630ab'
经过大量试验和错误后,我们通过将以下内容添加到第一个插件的原始源代码中解决了这个问题:
included do
alias_method :render_descendants_tree_original, :render_descendants_tree
alias_method :render_descendants_tree, :switch_render_descendants_tree
alias_method :sla_use_css, :sla_use_css
alias_method :switch_render_descendants_tree, :switch_render_descendants_tree
alias_method :render_descendants_tree_accordion, :render_descendants_tree_accordion
alias_method :expand_tree_at_first?, :expand_tree_at_first?
alias_method :sla_has_grandson_issues?, :sla_has_grandson_issues?
alias_method :subtask_tree_client_processing?, :subtask_tree_client_processing?
alias_method :subtask_list_accordion_tree_render_32?, :subtask_list_accordion_tree_render_32?
alias_method :subtask_list_accordion_tree_render_33?, :subtask_list_accordion_tree_render_33?
alias_method :subtask_list_accordion_tree_render_34?, :subtask_list_accordion_tree_render_34?
这是原代码:
其中有来自上述源代码的前两个 alias_method 调用。
通过为原始 class 中的每个方法创建一个具有相同名称的别名方法,代码可以正常工作。然而,这似乎是一个 hacky 修复,我不明白 为什么 它有效。有人可以解释为什么修复有效以及如何正确重写它吗?
alias_method
在调用它的上下文中创建命名方法的 copy。
现在,如果原始方法(alias_method
的第二个参数)在继承链的某处定义并在以后以任何方式更改,您仍然拥有未更改的副本。这或许可以解释您看到的行为。
至于重写:根据经验,丢弃所有方法别名(在两个插件中)并使用 Module#prepend
。 This SO Answer 很好地概述了(猴子)修补 Ruby 代码的好坏技术。
修补 Rails' 视图助手,因为 Rails 处理它们的方式(结果可能因代码加载顺序而异),这在您的插件中似乎是这种情况。尽可能避免它或创建新的辅助模块并使用类似
的方法将它们添加到相关控制器中module RedmineSubtaskListAccordion
module IssuesHelper
def render_descendants_tree(issue)
if sla_has_grandson_issues?(issue) && !subtask_tree_client_processing?
render_descendants_tree_accordion(issue)
else
super # this will call the stock Redmine IssuesHelper#render_descendants_tree method
end
end
end
end
# in init.rb:
IssuesController.class_eval do
helper RedmineSubtaskListAccordion::IssuesHelper
end
我刚才碰巧写了一篇关于这件事的 blog post。希望在这里引用它是可以的。
显然,如果两个插件期望它们正在更改的某个 Redmine 方法以其在现有 Redmine 中的行为方式运行,则可能仍然存在冲突,但根据我的经验,删除方法别名并且不尝试猴子修补已经存在的帮助程序会有所帮助避免很多问题。
更新:您链接到的那个特定模块中的大多数其他方法实际上不属于那里,但例如可以混合到 Issue
模型中(如 sla_has_grandson_issues?
或声明为方法在顶级插件命名空间中(所有设置和 Redmine 版本相关的东西 - 例如 RedmineSubtaskListAccordion.tree_render_34?
)。