<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>EricWJP (Eric)</title>
    <link>https://ruby-china.org/EricWJP</link>
    <description/>
    <language>en-us</language>
    <item>
      <title>rails 之 concern 常见用法解读 和 源码原理解读</title>
      <description>&lt;h2 id="concern 只是一个extend ActiveSupport::Concern 的 module。"&gt;concern 只是一个 extend ActiveSupport::Concern 的 module。&lt;/h2&gt;
&lt;p&gt;既可以在 model 中使用，也可以在 controller 中使用。&lt;br&gt;
使用方式：
  include ConcernName&lt;/p&gt;
&lt;h2 id="concern 用途"&gt;concern 用途&lt;/h2&gt;
&lt;p&gt;一、用于把特定功能代码集合封装成一个 concern，提高代码可读性和方便代码重构；&lt;br&gt;
二、把多个 model 或者 多个 controller 重复的功能集合代码封装成一个 concern。&lt;/p&gt;
&lt;h2 id="concern 定义"&gt;concern 定义&lt;/h2&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#定义一
module MyConcern
  extend ActiveSupport::Concern
  ...
end

#定义二
concern MyConcern do 
  ...
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;以上两种定义 concern 的方式&lt;strong&gt;&lt;em&gt;完全等同&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="常见concern 内部定义"&gt;常见 concern 内部定义&lt;/h2&gt;
&lt;p&gt;ActiveSupport::Concern 提供了一种约定配置：&lt;br&gt;
&amp;nbsp;&amp;nbsp;约定 ClassMethods  用来给类添加类方法（可选）&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; module ClassMethods&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; end&lt;br&gt;
&amp;nbsp;&amp;nbsp; 定义 included 作为当当前 module(也就是子 concern) 被 include 的回调方法（可选）&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; def self.included&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; end   &lt;/p&gt;
&lt;h5 id="如果不使用ClassMethods，concern 与普通module是一样的"&gt;如果不使用 ClassMethods，concern 与普通 module 是一样的&lt;/h5&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;module FirstConcern
  #把ActiveSupport::Concern中的方法 加入到 当前module模块方法（与class类方法原理相同）中
  extend ActiveSupport::Concern

  def self.included(base) 
    base.instance_eval do
      scope :jief, -&amp;gt;{...}
      has_many :table
    end
    # 或者
    #base.class_eval do
    #  ...  
    #end
    ...
  end
  #included 也可以这样
  #included do 
  #  这里作用域是base
  #  
  #  ...
  #end

  module ClassMethods
    #定义类方法
    def my_method
    end
    ...
  end
  #或者
  # class_methods do
  #   ...
  # end

end
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="ActiveSupport::Concern  代码详解"&gt;ActiveSupport::Concern  代码详解&lt;/h2&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;module Concern
    # 负责当定义多个 included 抛出异常
    class MultipleIncludedBlocks &amp;lt; StandardError #:nodoc:
      def initialize
        super "Cannot define multiple 'included' blocks for a Concern"
      end
    end

    # extended 是回调方法，当 当前module（这里指的是ActiveSupport::Concern） 被 extend 的时候执行
    # 参数--base是extend当前模块的module或者class
    # 用途，在base的作用域初始化@dependencies为空数组 []
    #@_dependencies是base的类实例变量（类实例变量只有类可以访问，他的实例无法访问），
      #用于记录base中include哪些module，顺序---从上到下，深度--1
    def self.extended(base) #:nodoc:
      base.instance_variable_set(:@_dependencies, [])
    end

    #这里的 append_features 是 对 Module.append_features 的扩展
    # Module#include 方法最终是 通过  Module.append_features 实现的（参数顺序是相反的）
    # append_features 只有在多个concern，include嵌套（即一个concern include 若干个其他的concern）时
      #使用此时扩展后的append_features。
    # 当子concern被include时 调用append_features(可理解成 include) 和 included,
        #append_features 在 included 之前调用
    # 参数--base是include当前模块的module或者class
    def append_features(base)
      if base.instance_variable_defined?(:@_dependencies)
        # instance_variable_defined? 用于判断实例变量是否存在。
        #如果 base是module，并且 extend ActiveSupport::Concern，
          # 也就是当 base是concern（这里称其为父concern）时，
          # @_dependencies 就会在上面extended回调方法中初始化
          #否则 这里不会执行。
        #把当前concern 加入到 base（父concern）的 类实例变量 @_dependencies中
        base.instance_variable_get(:@_dependencies) &amp;lt;&amp;lt; self
        return false
      else
        # 当base不是concern(base 是 class 或者 是普通 module)，才会执行

        #如果base 已经继承了self(子concern) 就不再执行，
        #这也就是 多次include 同一个module时，实际上只执行了第一次的原因
        return false if base &amp;lt; self      

        #遍历子concern 类实例变量 @_dependencies，base include 每一个子concern include的module
        @_dependencies.each { |dep| base.include(dep) }    
        super  #执行祖先链后面按序逐个查找并执行 append_features 方法

        #如果子concern 定义了 module ClassMethods，
        #  base 会 extend 该module-ClassMethods
        base.extend const_get(:ClassMethods) if const_defined?(:ClassMethods) 

        #在base作用域中执行 子concern中 included 方法的内容(Proc实例)
        base.class_eval(&amp;amp;@_included_block) if instance_variable_defined?(:@_included_block)  
      end
    end

    #这里扩展了 Module#included 方法
    def included(base = nil, &amp;amp;block)
      if base.nil?
        #这里的处理 实现了 included do  ...  end
        raise MultipleIncludedBlocks if instance_variable_defined?(:@_included_block)

        # 通过实例变量@_included_block，实现了  included 传入的块(这里变成了Proc对象) 共享，
            # 即append_features能够访问到
        @_included_block = block
      else
        super
      end
    end

    #这里实现了 class_methods do ... end
    def class_methods(&amp;amp;class_methods_module_definition)
      # mod 判断子concern中是否定义了 module ClassMethods，没有就用Module.new 初始化
      mod = const_defined?(:ClassMethods, false) ?
        const_get(:ClassMethods) :
        const_set(:ClassMethods, Module.new)

      # mod 把 传入的块(这里变成了Proc对象) 添加到 mod 中
      mod.module_eval(&amp;amp;class_methods_module_definition)
    end
  end
&lt;/code&gt;&lt;/pre&gt;</description>
      <author>EricWJP</author>
      <pubDate>Mon, 17 Feb 2020 15:27:42 +0800</pubDate>
      <link>https://ruby-china.org/topics/39509</link>
      <guid>https://ruby-china.org/topics/39509</guid>
    </item>
  </channel>
</rss>
