Динамически определять супер метод для экземпляра класса в Ruby - PullRequest
0 голосов
/ 11 марта 2020

Скажем, у нас есть класс, который мы не можем изменить,

class C
  def foo
    super
    puts "Low!"
  end
end

Нам нужно будет динамически определять метод foo во что-то, что мы сможем внедрить в C. родословная. Поведение super должно быть указано c для данного объекта, а не для всего класса. Мы сможем заключить эту логику c в анонимный модуль (назовем его сейчас):

module M
  def foo
    puts "High!"
  end
end

Расширение экземпляра C с помощью модуля:

c = C.new
c.extend(M)
c.foo
# High!

не будет работать, так как мы поместили метод из модуля перед методом, который мы определили в классе. Глядя на предков нашего объекта

c.singleton_class.ancestors
# => [#<Class:#<C:0x00005652be630b20>>, M, C, ...]

, я нашел уродливый обходной путь, который переопределяет методы из нашего класса в нашем singleton_class, т.е.

c.define_singleton_method(:foo, c.class.instance_method(:foo))
c.foo
# High!
# Low!

Пока это работает (делает это работать? Я проверил это немного, и, кажется, но я больше не уверен), мне интересно, упускаю ли я что-то очевидное, и есть ли более простой способ динамически определить «супер» метод для экземпляра класс.

Чтобы было ясно, мы хотим иметь возможность расширять другой экземпляр C другим модулем, то есть

C.new.extend(Module.new do
  def foo
    puts "Medium!"
  end
end).foo
# Medium!
# Low!

, и чтобы его выходные данные не были испорчены другими экземплярами.

1 Ответ

0 голосов
/ 11 марта 2020

Теперь, когда я понимаю, что вы пытаетесь обойти проблему в каком-то стороннем коде, я могу предложить более разумное решение. В приведенном ниже коде я думаю, что B и C определены гемом, и вы не хотите менять их исходный код, но вы хотите вставить некоторый код в то место, куда C#foo вызывает B#foo.

class B
  def foo
    puts "Highest!"
  end
end

class C < B
  def foo
    super
    puts "Low!"
  end
end

module CE
  def foo
    super
    foo_injected
  end
end

C.include(CE)

module M
  def foo_injected
    puts "High!"
  end
end

c = C.new
c.extend(M)
p c.singleton_class.ancestors
c.foo

Вывод:

[#<Class:#<C:0x000055ce443366a8>>, M, C, CE, B, Object, Kernel, BasicObject]
Highest!
High!
Low!
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...