ActiveSupport::Concern代码阅读

由于平时写类的代码比较多,这里再复习一下模块的知识点:

  1. 模块不能拥有实例
  2. 模块不能被继承

模块的主要使用方式:

  1. 利用 Mix-in 扩展功能
  2. 提供命名空间

模块的方法:
和类一样,我们也可以在 module 语句中定义方法。
然而,如果只定义了方法,虽然在模块内部与包含此模块的语句中都可以直接调用,但却不 能以“模块名 . 方法名”的形式调用。如果希望把方法作为模块函数公开给外部使用,就需要用 到 module_function 方法。module_function 的参数是表示方法名的符号。

还有一点就是,模块也是有类方法和示例方法,而无论是include还是extend都只会扩展module的示例方法而类方法只能模块内使用或者以module_function的方式才能调用。

module ActiveSupport
  module Concern
    class MultipleIncludedBlocks < StandardError #:nodoc:
      def initialize
        super "Cannot define multiple 'included' blocks for a Concern"
      end
    end

    def self.extended(base) #:nodoc:
      base.instance_variable_set(:@_dependencies, [])
    end

    def append_features(base)
      if base.instance_variable_defined?(:@_dependencies)
        base.instance_variable_get(:@_dependencies) << self
        false
      else
        return false if base < self
        @_dependencies.each { |dep| base.include(dep) }
        super
        base.extend const_get(:ClassMethods) if const_defined?(:ClassMethods)
        base.class_eval(&@_included_block) if instance_variable_defined?(:@_included_block)
      end
    end

    def included(base = nil, &block)
      if base.nil?
        raise MultipleIncludedBlocks if instance_variable_defined?(:@_included_block)

        @_included_block = block
      else
        super
      end
    end

    def class_methods(&class_methods_module_definition)
      mod = const_defined?(:ClassMethods, false) ?
        const_get(:ClassMethods) :
        const_set(:ClassMethods, Module.new)

      mod.module_eval(&class_methods_module_definition)
    end
  end
end

1.extended方法:
当模块扩展concern时,会调用extended方法,定义@_dependencies类示例变量。

  1. append_features方法:
    该方法是ruby的一个内核方法。在模块被include的时候被调用,与included方法的区别是,included方法没有默认实现,而append_features方法是检查该模块是否在祖先链上如果不在,则将该模块加入其祖先链。
    而我们改写的append_features方法主要是检查如果被concern包含那么将module加入到@_dependencies里,如果被不是concern的包含那么,把所有依赖都入住到类里。并且调用module的append_features将自身加入到祖先链里。
    每一个concern被include的时候也会将ClassMethods模块加入到base里并且扩充.


    image.png

    还有一个颠覆我的认知的知识点,为什么会这样呢后来笔者仔细思考,因为调用方法的self是classA的实例所以在执行方法的时候,会根据classA依次根据祖先链向上找。

# frozen_string_literal: true


module ModuleA
  def instance_method_1
    puts 'this is instance method 1'
  end

  module ClassMethods
    def class_method_1
      puts 'this is class method 1'
    end
  end
end

module ModuleB
  def instance_method_2
    instance_method_1
    puts 'this is instance method 2'
  end

  module ClassMethods
    def class_method_2
      puts 'this is class method 2'
    end
  end
end

class ClassA
  include ModuleA
  include ModuleB
end

ClassA.new.instance_method_2
# => "this is instance method 1"
# => "this is instance method 2"
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • mean to add the formatted="false" attribute?.[ 46% 47325/...
    ProZoom阅读 7,998评论 0 3
  • Introduction This document gives coding conventions for t...
    wuutiing阅读 10,164评论 0 9
  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 13,913评论 1 32
  • 原文 原文下载之后的格式略有点不友好,利用简述的markdown,编辑一下.版权归原作者 PEP Index > ...
    大飞哥阅读 7,317评论 0 0
  • 早起临摹的,但是感觉好像过去了好久的样子! 细节以及自然规律,这都是自己忽略掉的。 观察不够就下笔了,重复的图案其...
    bobo啊bobo阅读 1,621评论 0 2

友情链接更多精彩内容