ruby – 为什么包含此模块不会覆盖动态生成的方法?

前端之家收集整理的这篇文章主要介绍了ruby – 为什么包含此模块不会覆盖动态生成的方法?前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我试图通过包含一个模块来覆盖动态生成方法.

在下面的示例中,Ripple关联向表添加rows =方法.我想调用那个方法,但之后还要做一些额外的事情.

我创建了一个模块来覆盖该方法,认为模块的row =能够调用super来使用现有方法.

  1. class Table
  2.  
  3. # Ripple association - creates rows= method
  4. many :rows,:class_name => Table::Row
  5.  
  6. # Hacky first attempt to use the dynamically-created
  7. # method and also do additional stuff - I would actually
  8. # move this code elsewhere if it worked
  9. module RowNormalizer
  10. def rows=(*args)
  11. rows = super
  12. rows.map!(&:normalize_prior_year)
  13. end
  14. end
  15. include RowNormalizer
  16.  
  17. end

但是,我的新行=永远不会被调用,事实证明,如果我在其中引发异常,则没有任何反应.

我知道该模块已被包含在内,因为如果我把它放入其中,我的异常会被提升.

  1. included do
  2. raise 'I got included,woo!'
  3. end

此外,如果不是rows =,则模块定义somethingelse =,该方法是可调用的.

为什么我的模块方法不会覆盖动态生成的模块?

解决方法

我们来做一个实验:
  1. class A; def x; 'hi' end end
  2. module B; def x; super + ' john' end end
  3. A.class_eval { include B }
  4.  
  5. A.new.x
  6. => "hi" # oops

这是为什么?答案很简单:

  1. A.ancestors
  2. => [A,B,Object,Kernel,BasicObject]

B在祖先链中的A之前(您可以将其视为B在A内).因此A.x总是优先于B.x.

但是,这可以解决

  1. class A
  2. def x
  3. 'hi'
  4. end
  5. end
  6.  
  7. module B
  8. # Define a method with a different name
  9. def x_after
  10. x_before + ' john'
  11. end
  12.  
  13. # And set up aliases on the inclusion :)
  14. # We can use `alias new_name old_name`
  15. def self.included(klass)
  16. klass.class_eval {
  17. alias :x_before :x
  18. alias :x :x_after
  19. }
  20. end
  21. end
  22.  
  23. A.class_eval { include B }
  24.  
  25. A.new.x #=> "hi john"

使用ActiveSupport(以及Rails),您可以将此模式实现为alias_method_chain(target,feature)http://apidock.com/rails/Module/alias_method_chain

  1. module B
  2. def self.included(base)
  3. base.alias_method_chain :x,:feature
  4. end
  5.  
  6. def x_with_feature
  7. x_without_feature + " John"
  8. end
  9. end

更新Ruby 2附带了Module#prepend,它确实覆盖了A的方法,使得这个别名hack对大多数用例来说都是不必要的.

猜你在找的Ruby相关文章