检查include了IdentityCache后的AR得到的fetch方法来自哪里

[2] pry(main)> Human.include IdentityCache
=> Human (call 'Human.connection' to establish a connection)
[5] pry(main)> Human.method(:fetch).source_location
=> ["/home/z/.rbenv/versions/2.4.0/lib/ruby/gems/2.4.0/gems/identity_cache-0.5.1/lib/identity_cache/query_api.rb", 44]


但在identity_cache-0.5.1/lib/identity_cache.rb中,发现这里也有一个fetch方法,那为什么AR的fetch方法不是这个而是identity_cache-0.5.1/lib/identity_cache/query_api.rb那个?

module IdentityCache
  extend ActiveSupport::Concern

  include ArTransactionChanges
  include IdentityCache::BelongsToCaching
  include IdentityCache::CacheKeyGeneration
  include IdentityCache::ConfigurationDSL
  include IdentityCache::QueryAPI
  include IdentityCache::CacheInvalidation
  include IdentityCache::ShouldUseCache
  include IdentityCache::ParentModelExpiration

  class << self

    def fetch(key)
      if should_use_cache?
        unmap_cached_nil_for(cache.fetch(key) { map_cached_nil_for yield })
      else
        yield
      end
    end


原因在于,定义在module M中的类方法是无法通过include让class C/module N继承到的,因为module M的类方法其实是该module M的单例类的实例方法,而include只是让某个class C/module N的实例查找实例方法时能到被include的module M中查找,调用class C/module N类方法时,完全不会查找到module M的单例类

而identity_cache-0.5.1/lib/identity_cache/query_api.rb的fetch方法之所以能去到AR那里,是因为IdentityCache和IdentityCache::QueryAPI都是ActiveSupport::Concern,而IdentityCache.include IdentityCache::QueryAPI,因此IdentityCache::QueryAPI的fetch被带到AR上了

module IdentityCache
  module QueryAPI
    extend ActiveSupport::Concern

    module ClassMethods

      def fetch(id, options={})
        fetch_by_id(id, options) or raise(ActiveRecord::RecordNotFound, "Couldn't find #{self.name} with ID=#{id}")
      end