Почему "сам" метод модуля не может стать одноточечным методом класса?
module Test
def self.model_method
puts "this is a module method"
end
end
class A
include Test
end
A.model_method
это будет ошибка:
undefined метод `model_method 'для A: Class (NoMethodError)
Но когда я использую метакласс A, он работает:
module Test
def model_method
puts "this is a module method"
end
end
class A
class << self
include Test
end
end
A.model_method
Может кто-нибудь объяснить это?
Ответы
Ответ 1
Если вы хотите, чтобы оба метода класса и методы экземпляра смешивались с классом при включении модуля, вы можете следовать шаблону:
module YourModule
module ClassMethods
def a_class_method
puts "I'm a class method"
end
end
def an_instance_method
puts "I'm an instance method"
end
def self.included(base)
base.extend ClassMethods
end
end
class Whatever
include YourModule
end
Whatever.a_class_method
# => I'm a class method
Whatever.new.an_instance_method
# => I'm an instance method
Чтобы упростить его, вы extend
добавили методы класса, а include
- добавить методы экземпляра. Когда модуль включен, вызывается метод #included
, с фактическим классом, в который он был включен. Отсюда вы можете extend
класс с некоторыми методами класса из другого модуля. Это довольно распространенная картина.
Смотрите также: http://api.rubyonrails.org/classes/ActiveSupport/Concern.html
Ответ 2
Включение модуля аналогично копированию его методов экземпляра.
В вашем примере нет экземпляров методов для копирования в A
. model_method
- фактически метод экземпляра класса Test
singleton.
Дано:
module A
def method
end
end
Это:
module B
include A
end
Аналогично этому:
module B
def method
end
end
Когда вы так думаете об этом, это имеет смысл:
module B
class << self
include A
end
end
B.method
Здесь методы копируются в модуль singleton class B
, что делает их "методами класса" B
.
Обратите внимание, что это точно так же, как:
module B
extend A
end
В действительности методы не копируются; нет дублирования. Модуль просто включен в список поиска методов.