Я попытаюсь объяснить это немного глубже:
когда вы include
добавляете модуль в некоторый класс, Ruby создает специальный внутренний включающий класс и добавляет его в иерархию (обратите внимание, что в принципе вам не разрешено видеть включающий класс из программы Ruby, это скрытый класс):
Given A inherits B
And we have a module C
When A includes C
Then A inherits includeC inherits B
Если во включенном модуле есть другие включенные модули, то для этих модулей также будет создан includeModules:
Given A inherits B
And we have a module C
And C includes module D
When A includes C
Then A inherits includeC inherits includeD inherits B
Включить класс Таблица методов C представляет собой ссылку на таблицу методов исходного класса C.
Если вы extend
какой-то объект с модулем, то этот модуль входит в синглтон класса этого объекта, таким образом:
class << self; include C; end
# is the same as
extend C
Переходя к вашему примеру:
module Kernel
extend Talk
end
Здесь вы включаете модуль Talk
в одноэлементный класс Kernel
(Kernel
является объектом класса Module
). Вот почему вы можете вызывать метод hello
только для объекта Kernel: Kernel.hello
.
Если мы напишем это:
module Kernel
include Talk
end
Тогда Kernel
будет внутренне наследоваться включать класс includeTalk (класс со ссылкой на Talk
методы).
Но модуль ядра уже включен в Object
- Object
наследует свой собственный класс includeKernel, а класс includeKernel имеет ссылку на таблицу методов Kernel
и не видит методы новых классов include Kernel
.
Но теперь, если вы снова включите Ядро в Object, все Объекты увидят методы Talk:
> module Talk
> def hi
> puts 'hi'
> end
> end
=> nil
> module Kernel
> include Talk
> end
=> Kernel
> hi
NameError: undefined local variable or method `hi` for main:Object
from (irb):9
from /usr/share/ruby-rvm/rubies/ruby-1.9.2-p290/bin/irb:16:in `<main>`
> class Object
> include Kernel
> end
=> Object
> hi
hi
=> nil
Решением для вас может быть расширение основного объекта с вашим новым модулем:
extend Talk
Надеюсь, это прояснится немного поведения, которое вы наблюдаете:)
UPDATE
Постараюсь уточнить ваши вопросы:
Я все еще немного сбит с толку, почему мне нужно заново включить ядро в Object. В
случаи, которые не связаны с основным объектом, я могу создать экземпляр объекта
на основе класса, а затем снова открыть этот класс и включить
модуль, и этот объект будет видеть методы в моем модуле. Есть
что-то другое о том, как основной объект включает в себя ядро? я
также не уверен, что вы подразумеваете под "Объект наследует свой собственный includeKernel
class и includeKernel class ... "Почему он не видит новое включенное
модуль в ядре?
Расскажите о случае с прямым включением модуля в класс объекта:
module M
def hi
puts 'hi'
end
end
class C
end
c = C.new
c.hi # => UndefinedMethod
class C
include M
end
c.hi # => hi
в этом случае у вас будет объект c
класса C
. Класс C
наследует Object
(потому что это экземпляр класса Class
. C ищет его методы экземпляра в своем одноэлементном классе -> затем в своем классе C -> затем в родителях класса C (в данном случае Object
методы экземпляра). Когда мы включаем модуль M
в класс C
, includeM
будет суперклассом C
, и если c
не найдет его метод экземпляра в своем классе. класс singleton и класс C
, он будет искать методы экземпляра в includeM
. includeM
имеет ссылку на таблицу методов класса M
(экземпляр класса Module
). Таким образом, когда c
ищет экземпляр метод hi
находит его в модуле M
.
Но это отличается от случая, когда вы включаете модуль M
в Kernel
модуль.
При запуске программы Object
класс включает в себя модуль Kernel
: class Object; include Kernel; end
. Вот почему я говорю, что Object
наследуется от includeKernel
. includeKernel
имеет ссылку на таблицу методов Kernel
и при изменении таблицы методов ядра , includeKernel
также увидит эти изменения:
module Kernel
def hi # add hi method to method table of Kernel
puts 'hi'
end
end
hi # => hi # any Object now see method hi
Но когда вы включаете модуль M в ядро, таблица методов ядра не изменяется. Вместо этого ядро теперь будет наследовать includeM
include class . includeKernel
не видит методы includeM
, потому что он не знает о цепочке наследования Kernel
и includeM
, он знает только таблицу методов Kernel
.
Но когда вы снова включите Kernel
в Object
, механизм включения увидит, что Kernel
включает в себя M
, а также создаст includeM для Object
. Теперь Object
унаследует includeKernel
унаследует includeM
унаследует BasicObject
.