Metaprograming Ruby -- C10 Active Support’s Concern Module

Summary

  1. ruby can include instance methods and extend class methods, but can't do those two things together.
    Rails just want do though this together(powerful language with powerful framework ...).
  2. at first, rails use included hook,
    when a class includes the instance methods, it extend class methods by the includes hook
    so rails do those two things together.
  3. but when a module includes a module, problem happens.
    the second level module extended the first level module,
    yeah, the first level's ClassMethod's methods have became the second level's class method.
    but, the second level module's ClassMethods' methods do not have the first level's ClassMethods' methods,
    so when include the second level module in a class, the class can not get the first level's ClassMethods' methods. Because the first level module's class methods do not in the second level module's ClassMethods module
  4. rails solve this by overwriting the apppend_features
    it flatten all nest included modules with right order.
    and let the class include all those included module in the right order.

put include and extend together

includes and extend

included is a hook, when a class includes a module, this hook will be called
def self.included(base)
base.extend ClassMethods # ...
end

nested include

 Problem happens when BaseClass includes the FirstLevelModule, so the please read the comment in it and then read comment in FirstLevelModule's `included`
  module SecondLevelModule
    def self.included(base)
      base.extend ClassMethods
    end

    def second_level_instance_method; 'ok'; end

    module ClassMethods
      def second_level_class_method; 'ok'; end
    end
  end

  module FirstLevelModule
    def self.included(base)
      # called because the BaseClass me(`FirstLevelModule`)
      # I let the BaseClass extend all the methods in my ClassMethods module
      # but wait a minute, I include the SecondLevelModule,
      # and I have second_level_class_method as my class method
      # but it is not in my ClassMethods module,
      # so the BassClass dose not have second_level_class_method as its class method :(
      # include trick broken ...
      base.extend ClassMethods
    end

    def first_level_instance_method; 'ok'; end

    module ClassMethods
      def first_level_class_method; 'ok'; end
    end

    include SecondLevelModule
  end

  class BaseClass
    # the FirstLevelModule's included hook will be called
    include FirstLevelModule
  end

append_features - solving module includes module problem

the append_features is overwrote by rails

goal: never include a concern in another concern,
so just let the includer(class or module) 'includes' all concerns in the right order.

it has two part

  1. a concern includes a concern,
    checked by base.instance_variable_defined?("@_dependencies")
    it add the included concern as a dependent and add it to its @_dependencies

  2. a module includes a concern
    def append_features(base)
    # recursive
    # no need include and extend
    return false if base < self
    @_dependencies.each { |dep| base.send(:include, dep) }

        # to include class method by call original append_features
        super
        # extend class methods
        base.extend const_get("ClassMethods") if const_defined?("ClassMethods")
        base.class_eval(&@_included_block) if instance_variable_defined?("@_included_block")
      end
    
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容