Повторно включить модуль - PullRequest
2 голосов
/ 19 апреля 2010

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

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» Мне нужен простой, простой способ для повторного включения моего модуля.

Есть предложения?

Спасибо!

Ответы [ 4 ]

2 голосов
/ 19 апреля 2010
 class Module
     def include_again(mod)
         mod.instance_methods.each { |m|
             self.send(:define_method, m) { |*args|
                 mod.instance_method(m).bind(self).call(*args)
             }
         }
     end
 end

module One
    def test(a); puts "test one #{a}"; end
end

module Two
    def test; puts "test two"; end
end

class Foo
    include One
    include Two
end

Foo.new.test #=> "test two"

class Foo
    include_again One
end

Foo.new.test(1) #=> "test one 1"
0 голосов
/ 20 мая 2016

@ 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
0 голосов
/ 19 апреля 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
0 голосов
/ 19 апреля 2010

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

Проверка Foo ancestors на каждом этапе создания 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.

...