Почему модуль `ClassMethods` определен и расширен в одном пространстве имен?
Я пытаюсь понять код из github repo. Это основной модуль драгоценного камня для настройки клиента.
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
?
- Существует метод класса
def included(base)
. Кажется, это добавляет методы класса в конкретный объект. Почему так? Это может относиться к функциональности класса, но я этого не понимаю.
Ответы
Ответ 1
module MyModule
class << self
def included(base)
base.extend ClassMethods # what would this be for?
end
<...>
end
<...>
end
На самом деле это довольно распространенная практика в Ruby. В основном, что он говорит: когда какой-то объект выполняет include MyModule
, сделайте его также extend MyModule::ClassMethods
. Такой подвиг полезен, если вы хотите, чтобы 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}"
# Calling the class method from the module here!
p2 = P.factory(10)
puts "#{p2.sum} #{p2.mul}"
Ответ 2
Глядя на repo, есть еще один класс Github::API
. Этот класс, по-видимому, требует функциональности модуля Github::ClassMethods
.
module Github
# Core class responsible for api interface operations
class API
extend Github::ClassMethods
Таким образом, имеет смысл, что он имеет собственный модуль. Это дает возможность импортировать только те методы. Если бы были включены методы из class << self
, они стали бы доступными, которые, вероятно, не нужны.
Возможно, было лучше иметь модуль в своем классе или назвать что-то еще. Но я думаю, это просто личный выбор.