如何覆盖rails模型中的class_method问题

时间:2016-07-21 13:52:41

标签: ruby-on-rails ruby ruby-on-rails-4 activesupport-concern

如何覆盖模型关注中定义的类方法?

这有点棘手,因为你没有真正重写类方法吗?因为它使用了class_methods块中定义类方法的关注api。

所以说我有一个看起来像这样的问题:

module MyConcern
  extend ActiveSupport::Concern

  class_methods do
    def do_something
       #some code
    end
  end

end

在模型中..我将如何覆盖该方法,以便我可以像使用继承时使用super一样调用它?所以在我的模型中,我想去:

def self.do_something
  #call module do_something
end

3 个答案:

答案 0 :(得分:2)

如果您在定义include的模型中MyConcern d self.do_something,那么您应该能够使用super

module MyConcern
  extend ActiveSupport::Concern

  class_methods do
    def do_something
      puts "I'm do_something in the concern"
    end
  end
end

class UsesMyConcern < ActiveRecord::Base
  include MyConcern

  def self.do_something
    super
  end
end

UsesMyConcern.do_something
# => "I'm do_something in the concern"

如果您没有或不想在模型中include MyConcern而想要在模块上调用do_something而不创建任何中间对象,则可以将模型更改为:

class UsesMyConcern < ActiveRecord::Base
  def self.do_something                
    MyConcern::ClassMethods.instance_method(:do_something).bind(self).call
  end
end

UsesMyConcern.do_something
# => "I'm do_something in the concern"

ActiveSupport::Concern.class_methods在关注点中定义ClassMethods模块,如果还没有,那么我们可以找到do_something方法。

答案 1 :(得分:1)

为什么不简单地调用模块的方法:MyConcern.do_something

我不确定模块是否容易super(虽然我可以看到为什么这可能有用)。

下一个最佳解决方案可能是调用#included_modules并手动迭代#responds_to?

def self.do_something
  self.super_module(__method__)
end

def self.super_module(method)
  self.included_modules.find { |m| m.responds_to? method }.public_send(method)
end

答案 2 :(得分:0)

使用alias_method_chain的旧方式:https://ernie.io/2011/02/03/when-to-use-alias_method_chain/

新方法(需要&gt; ruby​​ 2.0.0)你真的应该使用它,因为在rails 5.0中使用它时会有DEPRECATION WARNING: http://paweljaniak.co.za/2014/09/30/understanding-ruby-module-prepend-and-include/