为什么在同一个命名空间中定义和扩展模块“ClassMethods”?

Why the module `ClassMethods` defined and extended in the same namespace?

我正在尝试理解来自 github repo 的代码。它是 gem 的主要模块,用于设置客户端。

module Github
  # more code
  class << self
    def included(base)
      base.extend ClassMethods # what would this be for?
    end
    def new(options = {}, &block)
      Client.new(options, &block)
    end
    def method_missing(method_name, *args, &block)
      if new.respond_to?(method_name)
        new.send(method_name, *args, &block)
      elsif configuration.respond_to?(method_name)
        Github.configuration.send(method_name, *args, &block)
      else
        super
      end
    end
    def respond_to?(method_name, include_private = false)
      new.respond_to?(method_name, include_private) ||
      configuration.respond_to?(method_name) ||
      super(method_name, include_private)
    end
  end

  module ClassMethods
    def require_all(prefix, *libs)
      libs.each do |lib|
        require "#{File.join(prefix, lib)}"
      end
    end
    # more methods ...
  end

  extend ClassMethods
  require_all LIBDIR,
    'authorization',
    'validations',
    'normalizer',
    'parameter_filter',
    'api',
    'client',
    'pagination',
    'request',
    'response',
    'response_wrapper',
    'error',
    'mime_type',
    'page_links',
    'paged_request',
    'page_iterator',
    'params_hash'

end
  1. 为什么使用class << selfmodule ClassMethods,然后扩展而不是包含在class << self部分?
  2. 有一个class方法def included(base)。这似乎将 class 方法添加到特定对象中。为什么会这样?它可能与class的功能有关,但我不明白。

多看 repo 还有一个 class Github::API。 class 似乎需要 Github::ClassMethods 模块的功能。

module Github
  # Core class responsible for api interface operations
  class API
    extend Github::ClassMethods

所以它有自己的模块是有道理的。它提供了只导入那些方法的能力。如果包含 class << self 中的方法,它们将变得可用,这可能是不需要的。

将模块放在自己的 class 中或命名为其他名称可能更好。但我想这只是个人选择。

module MyModule
  class << self
    def included(base)
      base.extend ClassMethods # what would this be for?
    end
    <...>
  end
  <...>
end

这实际上是 Ruby 中很常见的做法。基本上,它的意思是:当某个对象执行 include MyModule 时,使它也执行 extend MyModule::ClassMethods。如果您想要一个不仅向 class 的实例添加一些方法,而且向 class 本身添加一些方法的 mixin,这样的壮举很有用。

一个简短的例子:

module M
  # A normal instance method
  def mul
    @x * @y
  end
 
  module ClassMethods
    # A class method
    def factory(x)
      new(x, 2 * x)
    end
  end
 
  def self.included(base)
    base.extend ClassMethods
  end
end
 
class P
  include M
  def initialize(x, y)
    @x = x
    @y = y
  end
 
  def sum
    @x + @y
  end
end
 
p1 = P.new(5, 15)
puts "#{p1.sum} #{p1.mul}" # <= 20 75

# Calling the class method from the module here!
p2 = P.factory(10)
puts "#{p2.sum} #{p2.mul}" # <= 30 200