首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >结合使用super和class_eval

结合使用super和class_eval
EN

Stack Overflow用户
提问于 2012-12-11 02:08:49
回答 4查看 6K关注 0票数 3

我有一个应用程序,其中包括模块到核心类,以添加客户端自定义。

我发现class_eval是覆盖核心类中方法的好方法,但有时我希望避免重写整个方法,而只遵循原始方法。

例如,如果我有一个名为account_balance的方法,那么在我的模块(即包含在类中的模块)中做一些类似的事情会很好:

代码语言:javascript
运行
复制
module CustomClient
  def self.included base
    base.class_eval do
      def account_balance
        send_alert_email if balance < min
        super # Then this would just defer the rest of the logic defined in the original class
      end
    end
  end
end

但是使用class_eval似乎会将super方法从查找路径中移除。

有人知道如何解决这个问题吗?

谢谢!

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2012-12-11 02:18:15

我认为有几种方法可以做你想做的事情。一种方法是打开类并为旧的实现添加别名:

代码语言:javascript
运行
复制
class MyClass
  def method1
    1
  end
end

class MyClass
  alias_method :old_method1, :method1
  def method1
    old_method1 + 1
  end
end

MyClass.new.method1
 => 2 

这是monkey patching的一种形式,所以最好适度地使用这个习惯用法。此外,有时需要的是一个单独的辅助方法,该方法包含通用功能。

编辑:有关更全面的选项集,请参阅Jörg W Mittag的答案。

票数 12
EN

Stack Overflow用户

发布于 2012-12-11 03:30:41

我发现instance_eval是覆盖核心类中方法的好方法,

您不是在重写。您正在覆盖aka monkeypatching。

,但有时我想避免重写整个方法,而只遵循原始方法。

你不能遵从原来的方法。没有原创的方法。你把它改写了。

,但使用instance_eval似乎会将super方法从查找路径中删除。

在您的示例中没有继承。super甚至都不起作用。

有关可能的解决方案和替代方案,请参阅此答案:When monkey patching a method, can you call the overridden method from the new implementation?

票数 9
EN

Stack Overflow用户

发布于 2012-12-11 20:18:25

正如您所说,必须谨慎使用alias_method。给出这个人为的例子:

代码语言:javascript
运行
复制
module CustomClient
...    
    host.class_eval do
      alias :old_account_balance :account_balance
      def account_balance ...
        old_account_balance
      end
...
class CoreClass
    def old_account_balance ... defined here or in a superclass or
                                in another included module
    def account_balance
        # some new stuff ...
        old_account_balance # some old stuff ...
    end
    include CustomClient
end

你最终会得到一个无限循环,因为在别名之后,old_account_balance是account_balance的副本,它现在称自己为:

代码语言:javascript
运行
复制
$ ruby -w t4.rb 
t4.rb:21: warning: method redefined; discarding old old_account_balance
t4.rb:2: warning: previous definition of old_account_balance was here
[ output of puts removed ]
t4.rb:6: stack level too deep (SystemStackError)

从十字架上看,这种技术alias_method的问题在于,您所依赖的并不是一个名为old_xxx的现有方法。一个更好的选择是使用方法对象,它实际上是匿名的。

话虽如此,如果你拥有源代码,一个简单的别名就足够好了。但对于更一般的情况,我将使用Jörg的方法包装技术。

代码语言:javascript
运行
复制
class CoreClass
    def account_balance
        puts 'CoreClass#account_balance, stuff deferred to the original method.'
    end
end

module CustomClient
  def self.included host
    @is_defined_account_balance = host.new.respond_to? :account_balance
    puts "is_defined_account_balance=#{@is_defined_account_balance}"
        # pass this flag from CustomClient to host :
    host.instance_variable_set(:@is_defined_account_balance,
                                @is_defined_account_balance)
    host.class_eval do
      old_account_balance = instance_method(:account_balance) if
                @is_defined_account_balance
      define_method(:account_balance) do |*args|
        puts 'CustomClient#account_balance, additional stuff'
            # like super :
        old_account_balance.bind(self).call(*args) if
                self.class.instance_variable_get(:@is_defined_account_balance)
      end
    end
  end
end

class CoreClass
    include CustomClient
end

print 'CoreClass.new.account_balance : '
CoreClass.new.account_balance

输出:

代码语言:javascript
运行
复制
$ ruby -w t5.rb 
is_defined_account_balance=true
CoreClass.new.account_balance : CustomClient#account_balance, additional stuff
CoreClass#account_balance, stuff deferred to the original method.

为什么不是类变量@@is_defined_account_balance呢?从选择器中,包含include的模块或类定义可以访问它包含的模块的常量、类变量和实例方法。

它将避免将其从CustomClient传递到主机,并简化测试:

代码语言:javascript
运行
复制
    old_account_balance if @@is_defined_account_balance # = super

但有些人不喜欢类变量,也不喜欢全局变量。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/13806630

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档