Этот вопрос состоит из двух частей.
В книге Ruby Programming Language есть пример (раздел 8.1.1) расширения строкового объекта и класса с помощью модуля.
Первый вопрос. Почему, если вы расширяете класс с помощью нового метода, а затем создаете объект/экземпляр этого класса, вы не можете получить доступ к этому методу?
irb(main):001:0> module Greeter; def ciao; "Ciao!"; end; end
=> nil
irb(main):002:0> String.extend(Greeter)
=> String
irb(main):003:0> String.ciao
=> "Ciao!"
irb(main):004:0> x = "foo bar"
=> "foo bar"
irb(main):005:0> x.ciao
NoMethodError: undefined method `ciao' for "foo bar":String
from (irb):5
from :0
irb(main):006:0>
Вторая часть. Когда я пытаюсь расширить объект Fixnum, я получаю ошибку неопределенного метода. Может кто-нибудь объяснить, почему это работает для строки, но не для fixnum?
irb(main):045:0> module Greeter; def ciao; "Ciao!"; end; end
=> nil
irb(main):006:0> 3.extend(Greeter)
TypeError: can't define singleton
from (irb):6:in `extend_object'
from (irb):6:in `extend'
from (irb):6
2 ответа
Первый вопрос. Почему, если вы расширяете класс с помощью нового метода, а затем создаете объект/экземпляр этого класса, вы не можете получить доступ к этому методу?
Поскольку вы расширили класс String, #ciao является методом класса, а не методом экземпляра.
String.send(:include, Greeter)
x = "foo bar"
x.ciao
# => "Ciao!"
Вторая часть. Когда я пытаюсь расширить объект Fixnum, я получаю ошибку неопределенного метода. Может кто-нибудь объяснить, почему это работает для строки, но не для fixnum?
Вот краткий ответ.
«Fixnums, Symbols, true, nil и false реализованы как непосредственные значения. С непосредственными значениями переменные содержат сами объекты, а не ссылки на них.
Одноэлементные методы не могут быть определены для таких объектов. Два Fixnum с одним и тем же значением всегда представляют один и тот же экземпляр объекта, поэтому (например) переменные экземпляра для Fixnum со значением «один» совместно используются всеми «единицами» в системе. Это делает невозможным определение одноэлементного метода только для одного из них».
Конечно, вы можете включать/расширять класс Fixnum, и каждый экземпляр Fixnum будет предоставлять методы в Mixin. Это именно то, что делает Rails/ActiveSupport, чтобы позволить вам писать
3.days.ago
1.hour.from_now
obj.extend(module)
добавляет все методы из module
в obj
. При вызове как String.extend(Greeter)
вы добавляете методы из Greeter
в экземпляр Class
, который представляет String
.
Самый простой способ добавить дополнительные методы экземпляра в существующий класс — повторно открыть класс. Следующие примеры делают то же самое:
class String
include Greeter
end
class String
def ciao
"Ciao!"
end
end
Fixnums (а также символы, true, false и nil) обрабатываются иначе, чем обычные экземпляры. Два Fixnum с одним и тем же значением всегда будут представлены одним и тем же экземпляром объекта. В результате Ruby не позволяет вам расширять их.
Конечно, вы можете расширить экземпляр любого другого класса, например:
t = "Test"
t.extend(Greeter)
t.ciao
=> "Ciao!"
Похожие вопросы
Связанные вопросы
Новые вопросы
ruby
Ruby - это многоплатформенный динамический объектно-ориентированный интерпретируемый язык с открытым исходным кодом. Тег [ruby] предназначен для вопросов, связанных с языком Ruby, включая его синтаксис и его библиотеки. Вопросы Ruby on Rails должны быть помечены [ruby-on-rails].