Перезаписать метод экземпляра, определенный с помощью #define_singleton_method, и вызвать оригинал - PullRequest
2 голосов
/ 04 октября 2019

У меня есть класс, который динамически определяет методы в своих экземплярах, например:

obj = Object.new
obj.define_singleton_method(:foo) { "foo" }

Позже я хотел бы переопределить #foo, но иметь возможность вызвать исходную реализацию (s).

При использовании обычных классов это может быть достигнуто с помощью prepend MyModule и вызова super внутри предварительно добавленных методов. Но #prepend недоступно на уровне экземпляра.

Я пробовал с #extend, но похоже, что он вообще не перезаписывает #foo метод:

obj = Object.new
obj.define_singleton_method(:foo) { "foo" }
mod = Module.new
mod.define_method(:foo) { "module foo" }
obj.extend(mod)
obj.foo
# => "foo"

Я взглянул на гем RSpec, потому что они реализуют похожее поведение с #and_wrap_original (см. https://relishapp.com/rspec/rspec-mocks/v/3-8/docs/configuring-responses/wrapping-the-original-implementation) , но я не в тестовой среде , и похоже, чтоустановить код для достижения этого поведения (отслеживание заглушек, обратные вызовы, когда их сбрасывать и т. д.).

Итак, есть идеи, как это сделать в чистом Ruby?

1 Ответ

1 голос
/ 04 октября 2019

Посмотрите на obj.singleton_class.ancestors, чтобы увидеть, что на самом деле происходит. По умолчанию это что-то вроде

[#<Class:#<Object:0x00007febcf103160>>, Object, Kernel, BasicObject]

Если вы сделаете obj.extend(mod), вы получите это:

[#<Class:#<Object:0x00007febcf103160>>, #<Module:0x00007febcd0bb088>, Object, Kernel, BasicObject]

Так что порядок плохой, потому что модуль отстаетСинглтон-класс. Чтобы заменить его, он должен быть раньше в цепочке предков. Вы можете сделать это по obj.singleton_class.prepend(mod). Цепочка предков в этом случае:

[#<Module:0x00007febcd0bb088>, #<Class:#<Object:0x00007febcf103160>>, Object, Kernel, BasicObject]

, и выходные данные показывают, что метод переопределен:

obj.foo
# => "module foo"
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...