Я попытаюсь объяснить это немного глубже:
когда вы include
модуля в какой-то класс, 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.hello
.
Если мы пишем это:
module Kernel
include Talk
end
Тогда Kernel
будет внутренне наследовать включают класс includeTalk (класс с ссылкой на Talk
методы).
Но модуль ядра уже включен в Object
- Object
наследует свой собственный includeKernel класс и includeKernel класс имеет ссылку на метод таблицы Kernel
и не видит новых методов включают классы Kernel
.
Но теперь, если вы будете повторно включать ядра в объект, все объекты будут видеть методы работы в режиме разговора:
> 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
Раствор для вас probjem может быть, чтобы расширить главный объект с новым модулем:
extend Talk
Надежда это разъяснено немного поведение вы наблюдаете :)
UPDATE
Постараюсь уточнить ваши вопросы:
Я еще немного запутался, почему я должен повторно включать ядра в Object. В случае случаев, которые не связаны с основным объектом, я могу создать объект на основе класса, а затем повторно открыть этот класс и включить модуль , и этот объект увидит методы в моем модуле. Есть ли что-то другое о том, как основной объект включает в себя ядро? Я также не уверен, что вы подразумеваете под «Object наследует свой собственный includeKernel класс и включает класс ядра ...» Почему он не видит новый включенный модуль в ядре?
Вы говорите о случае с прямым включением модуля в класс объекта:
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
не найдет свой метод экземпляра в его одноплодном классе и C
класса, он будет искать методы, например, в includeM
. includeM
имеет ссылку на метод таблицу из M
класс (пример Module
класс). Таким образом, когда c
поиск метода экземпляра hi
он находит его в th e 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
Но когда вы включаете модуль М в ядро, то метод таблица ядра не изменилась , Вместо этого ядро теперь наследует includeM
включает класс.includeKernel
не видят методы includeM
, потому что он не знает о цепочке наследования Kernel
и includeM
, он знает только таблицу методов Kernel
.
Но когда вы повторно включить Kernel
в Object
, механизм включения будет видеть, что Kernel
включает M
и создаст includeM для Object
, а также. Теперь Object
наследует includeKernel
наследует includeM
наследует BasicObject
.
Джек, @AlexKliuchnikau прибил его; Примите его ответ! И проверьте «Рубиновый хакерский гид», на котором он разместил ссылку! –