别名方法:堆栈层级太深。

4

我正在尝试装饰来自另一个Rails引擎的控制器。我有一个控制器方法,想要添加一行来扩展它。我不想复制整个原始控制器方法。

这是我的尝试:

  Backend::BaseContentsController.class_eval do
    def booking_update
      # do some stuff
      update
    end
    alias_method :update, :booking_update
  end

不幸的是,这会抛出异常stack level too deep。通常情况下我可以使用继承来调用super方法。在我的情况下,理想的做法是什么?


1
请参考以下问题的讨论,了解一些选项:当猴子补丁一个方法时,您是否可以从新实现中调用被覆盖的方法? - Jörg W Mittag
我认为将您的问题标记为重复问题是有意义的,但我想先听听您的意见:您是否认为那个问题的答案回答了您的问题? - Jörg W Mittag
1
@JörgWMittag:也许吧,我还没有尝试过。我不确定是否可以只实现委托示例并调用WrappedFoo.new(Backend::BaseContentsController)。在我看来,链接的答案相当通用。也许最好创建一个新答案,引用那个答案,并解释为什么不应该使用alias_method_chain - dan-klasson
4个回答

6
你应该尝试使用 alias_method_chain
def update_with_booking
  # do some stuff
  update_without_booking # that's your old update
end

alias_method_chain :update, :booking

这正是我正在寻找的。 - dan-klasson
很高兴再次见到Marek! - Richard Peck

3
module Decorator
  def update
    # do some stuff
    super
  end
end
Backend::BaseContentsController.prepend(Decorator)

这可能是更好的实现方式,它更加明确。但我想遵循惯例,所以我选择了被接受的答案。 - dan-klasson
1
@dan-klasson 这个约定是在没有 prepend 的情况下发明的。顺便说一下:alias_method_chain 会创建一个别名,这可能会导致将来其他代码尝试执行相同操作时出现奇怪的问题。上面的解决方案是万无一失的。Rails 经常太傻了,无法遵循它的惯例。 - Aleksei Matiushkin
@mudasobwa:我指的是我正在处理的这个遗留应用程序上的惯例。不过还是谢谢你提醒我。 - dan-klasson

2
你定义了一个无限递归。结果是以下代码片段。
def update
  # do some stuff
  update
end

确保你的别名不会覆盖你仍在使用的方法。

Backend::BaseContentsController.class_eval do
  alias_method :update_original, :update

  def booking_update    
    # do some stuff
    update_original
  end

  alias_method :update, :booking_update
end

是的,我也想到了 :). 但是解决方案是什么呢? - dan-klasson
@dan-klasson,你试过我在答案中发布的那个吗? - Marek Lipka
@MarekLipka:现在正在尝试。 - dan-klasson
@dan-klasson 添加了一个示例解决方案。 - sschmeck

0

解释:

当在同一方法上同时使用 alias_method 和 Module#prepend 时,会出现“stack level too deep”错误。

当同时使用两者时,它们都试图找到实际的定义,但却得到了其他定义,现在它们都试图将自己的定义放在堆栈顶部,这会导致太多的堆栈条目并引发“stack level too deep”错误。

解决方案:

避免使用 alias_method 和 alias_method_chain,因为它们已经过时。取而代之的是使用Module#prepend 。Module#prepend 添加在 Ruby 2.0中。

参考文献:

https://blog.newrelic.com/engineering/ruby-agent-module-prepend-alias-method-chains/ https://docs.newrelic.com/docs/agents/ruby-agent/troubleshooting/systemstackerror-stack-level-too-deep https://ethigeek.com/blogs/mutually-exclusive-alias-method-and-prepend


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接