为什么在同一个命名空间中定义和扩展模块“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
- 为什么使用
class << self
和module ClassMethods
,然后扩展而不是包含在class << self
部分?
- 有一个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
我正在尝试理解来自 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
- 为什么使用
class << self
和module ClassMethods
,然后扩展而不是包含在class << self
部分? - 有一个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