Повторно включить модуль

Мне нужны вот такие:

module One
  def test; puts 'Test One'; end
end

module Two
  def test; puts 'Test Two'; end
end

class Foo
  include One
  include Two
  include One
end

В этом случае мне нужен результат «Test One», но, очевидно, он возвращает «Test Two». Мне нужен чистый простой способ повторно включить мой модуль.

Любое предложение?

Спасибо!


person Nino55    schedule 18.04.2010    source источник


Ответы (4)


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

Проверка ancestors Foo на каждом этапе создания Foo дает нам большую подсказку:

module One
  def test; puts 'Test One'; end
end

module Two
  def test; puts 'Test Two'; end
end

class Foo
  include One
  p ancestors
  include Two
  p ancestors
  include One
  p ancestors
end

Вывод:

[Foo, One, Object, Kernel]
[Foo, Two, One, Object, Kernel]
[Foo, Two, One, Object, Kernel]

Если модуль уже находится в родословной класса, Ruby не позволяет вам повторно включать его снова. Таким образом, когда Two включен после One, Two появляется в таблице поиска Foo до того, как у One появится шанс, независимо от того, сколько раз вы повторно включаете One.

person Mark Rushakoff    schedule 18.04.2010
comment
remove_method не будет работать, потому что метод определен не в Foo, а в модуле, включенном Foo (и я предполагаю, что вы не хотите удалять метод из модуля;)) - person horseyguy; 19.04.2010

Вы можете настроить поведение include:

module One
  def test; puts 'Test One'; end
end

module Two
  def test; puts 'Test Two'; end
end

class Foo
  @mods = []
  def self.include(mod)
    @mods.delet mod
    @mods << mod
  end

  def self.new(*args)
    super.tap { |o| @mods.each { |m| o.extend m } } 
  end

  include One
  include Two
  include One
end
person Konstantin Haase    schedule 19.04.2010
comment
Ага, чтобы написать это в одну строчку. это как «себя» в smalltalk. Каков альтернативный (не злоупотребляющий) вариант использования тапа? - person Konstantin Haase; 19.04.2010
comment
Я сам не очень много использовал #tap, но ребята, с которыми я разговаривал, сказали, что его единственный реальный вариант использования - отладка (они сказали, что не будут использовать его в производственном коде). - person horseyguy; 19.04.2010

@banister, большое спасибо за этот ответ, так как я не могу комментировать, я добавлю сюда код, чтобы он работал с блоками, а также с аргументами:

class Module
  def include_again(mod)
    mod.instance_methods.each{ |m| 
      self.send(:define_method, m) { |*args, &block|
        mod.instance_method(m).bind(self).call(*args, &block)
      }
    }
  end
end
person Lumean    schedule 20.05.2016

person    schedule
comment
Что произойдет, если у моего теста метода есть аргументы? - person Nino55; 19.04.2010