Как устранить конфликты пространства имен при включении модуля в модуль в Ruby? - PullRequest
3 голосов
/ 15 июля 2011

Вот как выглядит мой код:

module A
  def foo
    puts "A"
  end
end

module B
  include A
  def bar
    foo
  end
end

class MyClass
  include B
  def foo
    puts "X"
  end
  def self.test
    puts bar
  end
end

Когда я вызываю «C.test», я получаю «X» вместо «A» (что я и хочу), потому что локальное определениеfoo переопределил, что в A. я не могу изменить подпись foo.Я могу в основном редактировать только свой собственный класс;Я могу редактировать модули A и B, но многие из существующих кодов используют их, и это не так (поэтому не нужно менять foo на A.foo, например).Я думаю о

class MyClass
  module MyModules
    include B
  end
  ....
    MyModules.bar
  ....
end

Но это не работает.

Как я могу "локализовать" пространство имен при включении B?

Ответы [ 2 ]

3 голосов
/ 15 июля 2011

Из того, что я понимаю, вы хотите переопределить A#foo (который также используется B#bar) внутри MyClass.Однако вы хотите переопределить его только внутри MyClass, а не для кода в B mixin.

Здесь нужно понимать, что когда код A и B mixin запускаются, они будут частью MyClass экземпляра.Поэтому невозможно переопределить методы экземпляра MyClass, не влияя на миксины.Это как если бы методы миксинов были брошены в ведро (экземпляр MyClass), а затем запущены.У них нет отдельных областей применения.Поэтому простой ответ: нет, вы не можете этого сделать.

Хотя может быть несколько возможных решений, многие из которых пахнут пастой .Подобные проблемы могут указывать на то, что общие проектные решения требуют некоторого рефакторинга.

Рассмотрим основы модулей:

module Numbered
  DEFAULT = "1234-AWESOME"
  def serial_number
    DEFAULT
  end
  def self.awesome?
    true
  end
end

Numbered.awesome? # => true
Numbered.serial_number # whoops! NoMethodError.

o = Object.new
o.extend(Numbered)
o.serial_number #=> "1234-AWESOME"

Numbered.extend(Numbered)
Numbered.serial_number # => "1234-AWESOME"

class Dog
  extend Numbered
end

Dog.serial_number # => "1234-AWESOME"
Dog::DEFAULT # => "1234-AWESOME"
fido = Dog.new
fido.serial_number # NoMethodError!

Это потому, что метод был добавлен какметод класса.include на помощь:

class Fish
  include Numbered

  def serial_number
    super + '-FSH'
  end
end

cod = Fish.new
cod.serial_number # => "1234-AWESOME-FSH"

Итак, include объединяет все методы в один общий экземпляр, но мы все равно можем использовать super для вызова включенного метода, который мы переопределяем.Если вы выберете решение для макаронных изделий, вы можете использовать super внутри метода bar, условно вызывая метод, который вы переопределяете.

Также, в зависимости от случая, вы можете настроитьновый модуль и extend с модулем, содержащим методы, к которым вам нужен доступ, как в ответе, который вы добавили.

1 голос
/ 16 июля 2011

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

class MyClass
  class MyModules
    extend B
  end
  ....
    MyModules.bar
  ....
end

Это эффективно позволяет мне ограничить область действия B и всех остальныхвключены модули в локальный класс MyModules.В моем коде это на самом деле имеет значение, потому что все включенные модули обрабатывают манипуляции выражениями, поэтому я могу назвать локальный класс «Expr» (и они серьезно злоупотребляют именами «eval», «bind» и «free» прямо сейчас).

...