Элегантный способ подготовиться к модулю, который уже включен? - PullRequest
7 голосов
/ 10 марта 2019

Я хочу обезопасить самоцвет, и целевой код находится в модуле. К сожалению, в то время, когда я готовлю свой патч, модуль уже включен в различные классы, и новый код не действует.

Пример:

module Feature
  def action
    puts "Feature"
  end
end

module Patch
  def action
    puts "Patch"
  end
end

class Base1
  include Feature
end

Feature.prepend Patch

class Base2
  include Feature
end


Base1.new.action # Returns "Feature", I want it to be "Patch" instead.
Base2.new.action # Returns "Patch"

Когда я добавляю Feature до его включения в Base2, патч работает, но с настоящим драгоценным камнем я не могу изменить порядок.

Есть ли элегантный способ решить эту проблему, или мне нужно пройти по ObjectSpace, чтобы найти, какие классы уже включают модуль Feature?

Ответы [ 2 ]

3 голосов
/ 11 марта 2019

TL; DR - вы вообще не можете, но Base1.include Patch может быть достаточно хорошим.


Для вашего примера кода ancestors из Base1 и Base2: (выровнено для ясности)

Base1.ancestors #=> [Base1,        Feature, Object, Kernel, BasicObject]
Base2.ancestors #=> [Base2, Patch, Feature, Object, Kernel, BasicObject]

Base2 имеет дополнительного предка Patch перед Feature - результат Feature.prepend Patch.

Rubyне позволяет нам свободно изменять цепочку предков модуля, поэтому мы не можем просто добавить Patch к Feature задним числом.

Но, к счастью, Patch является первым модулем после Baseкласс, поэтому мы можем прибегнуть к include, чтобы добавить Patch к Base1 вместо:

Base1.include Patch
Base1.ancestors #=> [Base1, Patch, Feature, Object, Kernel, BasicObject]

Очевидно, это работает только для очень конкретных случаев, а не в целом.

Вот контрпример:

module Feature
  def action ; 'Feature' ; end
end

module Foo
  def action ; "#{super} overridden" ; end
end

module Patch
  def action ; 'Patch' ; end
end

class Base1
  include Feature
  include Foo
end

Feature.prepend(Patch)

class Base2
  include Feature
  include Foo
end

Base1.new.action #=> "Feature overridden"
Base2.new.action #=> "Patch overridden"

Base1.include Patch

Base1.new.action #=> "Patch"

Глядя на предков, выявляется проблема:

Base1.ancestors #=> [Base1, Foo,        Feature, Object, Kernel, BasicObject]
Base2.ancestors #=> [Base2, Foo, Patch, Feature, Object, Kernel, BasicObject]
Base1.include Patch
Base1.ancestors #=> [Base1, Patch, Foo, Feature, Object, Kernel, BasicObject]

Patch и Foo не в порядке.

0 голосов
/ 11 марта 2019
module Feature
  def action
    puts "Feature"
  end
end

class Base
  include Feature
end 

После include Feature вам остается только переопределить методы экземпляра Feature, которые должны быть изменены, в том месте кода, где это необходимо.

Feature.define_method(:action) { "Patch" }
# other methods follow

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