【问题标题】:How do I include behavior in a subclass?如何在子类中包含行为?
【发布时间】:2020-05-13 22:09:12
【问题描述】:

我担心included 块。在这个例子中,它设置了一个类实例变量。

require 'active_support/concern'

module Mod1
  extend ActiveSupport::Concern

  included do
    p "#{self}: included Mod1"
    @foo = "foo is set"
  end

  class_methods do
    def foo
      @foo
    end
  end
end

如果我包含它,我会得到方法 foo 并设置包含的变量。

class Parent
  include Mod1
end

p Parent.foo # "Parent: included Mod1" "foo is set"

但是如果我从 Parent 继承,我继承方法 foo,但包含的块没有运行。

class Subclass < Parent
end

p Subclass.foo # nil

即使我包含模块,包含的块也不会运行。我期待因为Subclass.include?(Mod1) 是真的。

class Subclass < Parent
  include Mod1
end

p Subclass.foo # nil
p Subclass.include?(Mod1) # true

如何编写关注点,使其included 块甚至在子类上运行?

【问题讨论】:

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


    【解决方案1】:

    ActiveSupport::Concern#included 实际上只是Module#included 的语法糖,所以它只在模块包含在父类中时才会触发。您正在寻找的钩子是Class#inherited,每当创建当前类的子类时都会调用它。

    require 'active_support/concern'
    
    module Mod1
      extend ActiveSupport::Concern
    
      included do
        set_foo!
      end
    
      class_methods do
        def foo
          @foo
        end
    
        def set_foo!
          p "Setting foo"
          @foo = "foo is set"
        end
    
        def inherited(child_class)
          puts "inherited!"
          child_class.set_foo!
        end
      end
    end
    
    class Parent
      include Mod1
    end
    
    class Subclass < Parent; end
    
    require 'minitest/autorun'
    class Mod1Test < Minitest::Test
      def test_class_ivar_set_in_subclass
        assert_equal("foo is set", Subclass.foo) # passes
      end
    end
    
    "Setting foo"
    inherited!
    "Setting foo"
    Run options: --seed 35149
    
    # Running:
    
    .
    
    Finished in 0.000999s, 1000.7185 runs/s, 1000.7185 assertions/s.
    1 runs, 1 assertions, 0 failures, 0 errors, 0 skips
    

    【讨论】:

    • 这条评论对我帮助很大!只是提醒遇到此问题的其他人,如果您在父类从其他东西(如 ApplicationRecord!)继承时执行此操作,请在您父母的 inherited 方法中调用 super(child_class)
    【解决方案2】:

    这里很有趣。实际上SubClassfoo 方法,但是没有设置返回值@fooSubClass 继承自 Parent 的方法,但是当您使用 @foo 时,它是一个变量,因此它不能在 2 个对象 ParentSubClass 之间共享。 您可以通过获取方法foo在子类中的源位置来检查

    SubClass.method(:foo).source_location
    

    如果你把逻辑移到方法foo,你可以看到SubClass会返回预期的值。 例如:

    require 'active_support/concern'
    
    module Mod1
      extend ActiveSupport::Concern
    
      included do
      end
    
      class_methods do
        def foo
          p "#{self}: included Mod1"
          "foo is set"
        end
      end
    end
    

    然后从SubClass 调用foo 将返回foo is set

    active_support/concern 中的 included do 通常用于声明 Rails 功能,例如关系或验证或范围。

    https://api.rubyonrails.org/classes/ActiveSupport/Concern.html

    【讨论】:

    • 这没有回答问题。提问者并没有试图在类和它的子类之间共享一个变量——一个类变量可以做到这一点。他们试图在类和子类中初始化一个类实例变量。
    猜你喜欢
    • 2013-04-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-03-24
    • 2021-08-22
    相关资源
    最近更新 更多