Вложенные модули Ruby как пространства имен

у меня есть вложенная структура модулей,которые используются только для целей пространства имен; нет смешивания в классы и т. д. Поэтому у меня есть такой код:

module Lib

  module A
    def A.foo
      puts 'Lib::A::foo'
    end
  end

  module B
    def B.bar
      puts 'Lib::B::bar'
    end
  end

end

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

module Lib

  module A
    def A.foo
      puts 'Lib::A::foo'
    end
    def A.helper
      puts 'Lib::A::helper'
    end
  end

  module B
    def B.bar
      puts 'Lib::B::bar'
      A::helper
    end
  end

end

и это, кажется, работает, но у него есть некоторый недостаток, от которого я хотел бы избавиться:
Я не хочу звонить helper его полное имя (A::helper) все время изнутри B. Я бы предпочел как-то сказать Ruby, что этот префикс пространства имен "по умолчанию" и назовите его просто helper. На C++ я мог бы просто написать using A::helper внутри пространства имен B и это решило бы проблему. Но как это сделать в Ruby?

Я пробовал добавлять include A и extend A внутри B, но ни один из них не работает. Они, похоже, работают только внутри классов, когда эти модули смешаны, но не когда они автономны, используются только для пространства имен цели.

есть ли другие способы заставить его работать как я хочу?

О, и еще одно:
Предположим другой сценарий, где я хочу A::helper для использования только внутри Aметоды, потому что это просто некоторая функция реализации, где я учитывал некоторый общий код, используемый многими функциями внутри A, но теперь я не хочу, чтобы это было видно для внешнего мира, просто для A'ы методов. Как я могу это сделать?

I пробовал с module_function + удаление A. префикс от всех других функций, которые должны быть скрыты, но они скрыты и для других методов A, потому что они являются методами экземпляра, и модули не могут быть созданы. Итак, как я могу скрыть модульный метод от внешнего мира и разрешить его внутреннее использование другими модульными методами?

редактировать Почему downvoting? Я старался быть как можно более ясным, что мне нужно умолчанию пространство имен внутри другого пространства имен избавиться от длинных полных имен в целом (не просто сглаживание их до чего-то короче), и что моя проблема не касается классов и объектов вообще, просто простые модули, используемые только для целей пространства имен. Как еще я мог это объяснить?

это не моя вина, что механизмы пространства имен, кажется, не быть полностью поддерживается в Ruby изначально как в языках например, C++, и это кажется просто побочный эффект использования модулей и классов, чтобы иметь некоторые функциональность истинных пространств имен (они крякают, как утки, у них есть утконосы, но они утконосы, а не утки, и не должны рекламироваться как пространства имен, которыми они, по-видимому, не являются, потому что это только путает людей, приходящих в Ruby с других языков с истинными пространствами имен), или что вы, по-видимому, не понимаете понятия из других языков программирования, если они нелегко возможны в Ruby (поскольку я вижу, что вы, похоже, притворяетесь, что моей проблемы не существует, и возвращаетесь к тому, что вы can легко сделать в Ruby, но это не то, что мне нужно).

и почему был удален единственный ответ, который имел отношение к моему вопросу? Не круто ;.

3 ответов


хорошо, поскольку единственный ответ, который имел отношение к моему вопросу, был удален, я попытаюсь ответить на свой вопрос сам, основываясь на этом удаленном ответе @sawa (спасибо @sawa за намек в правильном направлении). Я несколько изменил его, чтобы лучше соответствовать моим потребностям и быть более элегантным. Позже я опишу, почему оригинальный ответ @sawa не был тем, что я искал.
итак, без дальнейших церемоний, вот моя попытка решения:

module Lib

  module A
    extend self
    def foo
      puts 'Lib::A::foo'
    end
    def helper
      puts 'Lib::A::helper'
      foo
    end
  end

  module B
    extend A
    def self.bar
      puts 'Lib::B::bar'
      helper
    end
  end

end

puts 'Calling Lib::A::foo:'
Lib::A::foo        # => Lib::A::foo

puts 'Calling Lib::A::helper:'
Lib::A::helper     # => Lib::A::helper; Lib::A::foo

puts 'Calling Lib::B::bar:'
Lib::B::bar        # => Lib::B::bar; Lib::A::helper; Lib::A::foo

вот как это работает:
Во-первых, он определяет все свои методы как методы экземпляра самого конкретного класса модуля (A в данном случае). Но затем, чтобы сделать их доступными для внешнего использования без создания экземпляра (что невозможно для модулей, в конце концов), я extend на A модуль с самим собой, что делает эти методы также его методами класса. Но благодаря тому, что они также являются методами экземпляра модуля A, их можно вызвать изнутри других методов этого модуля без префикса с именем модуля. То же самое касается модуля B, который также extends сам с модулем Aметоды, делая их своими собственными. Тогда я могу позвать их внутрь Bметоды тоже без префиксов, как я и хотел.

как вы можете видеть, не только я могу вызвать методы обоих модулей извне, как если бы они были методами их класса, и я могу вызвать A::helper С B::foo без полной квалификации его название, но я также могу назвать A::foo от A::helper без квалификации. Это именно то, что мне нужно, и, похоже, работает так, как я ожидал.

единственная проблема с этим подходом может быть с тем, что их интерфейсы смешиваются вместе. Это не большая проблема внутри B, так как это то, что я действительно хотел: иметь доступ A'методы, как если бы это было Bметоды, без необходимости префикса их с полной квалификацией. Так что я получил по заслугам. Но это может вызвать проблемы со стороны, потому что это деталь реализации B что он использует Aметоды внутренне. Она не должна просачиваться во внешний мир, но просачивается. Я попытаюсь как-то исправить это с помощью контроля доступа, возможно, это будет возможно.

Edit: Да, это можно сделать, вставив следующую строку после extend A на B:

private_class_method *A.public_instance_methods

таким образом B можно назвать Aметоды внутренне, но они недоступны из внешний мир.

теперь, что было не так с оригинальным решением @sawa:

он использует третий модуль только для прокси-интерфейса через него. Для меня это было скорее грязный хак чем элегантное решение, потому что оно вводит этот дополнительный модуль, который запутает пользователей такой библиотеки. Они не будут знать, Следует ли им использовать A или C, и почему такое приспособление используется вообще. Это не очевидно как это работает просто глядя на него. Он нуждается в более тщательном анализе, чтобы понять, что он действительно делает и почему он построен таким образом. Это не чистый дизайн.

в моем решении, с другой стороны, есть только два модуля, как первоначально было разработано, и их назначение должно быть ясно для пользователей этой библиотеки. Есть такая странная extend self, но все же это более распространенная идиома в Ruby, чем распространение прокси-модулей повсюду.

так спасибо ребята за ваши попытки. В следующий раз постарайтесь быть менее арогантным (когда вы видите, что кто-то задает вопрос, это не всегда так, он нуб) и зациклен на вашем любимом истинном языке (не поймите меня неправильно, мне нравится язык Ruby, он довольно прохладный и чистый, но у него есть некоторые недостатки, как и у любого языка, и лучше искать их решение, а не хоронить свои головы и притворяться, что нет никаких проблем, так как это не то, для чего язык был разработан)- ...


module E
  module A
    def foo
      puts 'E::A::foo'
    end
    def helper
        puts 'E::A::helper'
    end    
  end

  module B
    extend A
    def B.bar
      puts 'E::B::bar'
      helper
    end
  end
end

E::B.bar #=> E::B::bar & E::A::helper

Я бы предложил переосмыслить свой фундаментальный процесс. Это предложение-красный флаг:

У меня есть вложенная структура модулей, которые используются только для целей пространства имен; нет смешивания в классы и т. д. Только для целей пространства имен; нет смешивания в классы...

Это неправильный способ использования модулей, и даже вы не согласны прямо сейчас, вы, вероятно, узнаете это с большим опытом. Я предлагаю изучать наследование Ruby больше, и модули way используются "в дикой природе". Есть тонны хороших блогов,руководств и материалов в интернете, щелчок google...