Как предоставить методы package-private видимости в Ruby или Python - PullRequest
0 голосов
/ 06 июня 2018

Java имеет видимость с именем package-private , что означает, что к чему-то можно получить доступ только внутри пакета, в котором оно определено, а не снаружи.Мне интересно, есть ли способ добавить эту функцию в Ruby (с модулями) или Python.

Вот пример того, что я ищу:

module Foo
  class FooClass
    def print_message_package
      # Some line to test that the last caller is in module Foo
      puts 'Hello'
    end

    def print_message_public
      print_message_package
    end
  end
end

test = Foo::FooClass.new
# I would like that following line raises an error, because it is not called inside module Foo
test.print_message_package
# This line should not raise an error:
test.print_message_public

1 Ответ

0 голосов
/ 07 июня 2018

Это не красиво, но я смог заставить это работать, используя драгоценный камень binding_of_caller.Просто сделайте gem install binding_of_caller, и приведенный ниже код должен работать для того, что вам нужно.

Обратите внимание, что это сильно скопировано с https://stackoverflow.com/a/10535890/2230115, и вам определенно следует проголосовать за него:)

require 'binding_of_caller'
class Object
  @@module_scoped_methods = []

  def module_visibility(method_name, module_name)
    method_name = method_name.to_sym
    module_name = module_name.to_sym
    new_method_name = "module_only_#{method_name}".to_sym
    self.send(:alias_method, new_method_name, method_name)
    self.send(:undef_method, method_name)
    self.send(:private, new_method_name)
    @@module_scoped_methods << [method_name, module_name]
  end

  alias_method :old_method_missing, :method_missing
  undef_method :method_missing
  def method_missing(method_name, *arg, &block)
    method_ob = @@module_scoped_methods.find { |m| m[0] == method_name }
    if method_ob
      if binding.of_caller(1).eval('self.class').ancestors.find {|m| m.name.split('::').map{|c|c.to_sym}.include? method_ob[1]}
        self.send("module_only_#{method_name}".to_sym,*arg,&block)
      else
        raise "Method #{method_name} is called outside the set module."
      end
    else
      old_method_missing(method_name, arg, block)
    end
  end
end

Использование:

module A
  class Test
    def test
      5
    end
    #Method name, and module wanted to be scoped to.
    #Required since you can have nested modules.
    module_visibility :test, :A 
  end
  class B
    def test
      A::Test.new.test
    end
  end
end
# raises runtime error "method test is called outside the set module
A::Test.new.test
A::B.new.test # => 5

Как это работает:

Мы изменяем класс Object, чтобы отслеживать список методов, которые были ограничены видимостью модуля.Здесь хранится двухэлементный массив с именем метода и модулем, к которому он был применен.Затем он меняет имя метода на module_only_<originalname> и делает его закрытым.

Затем, чтобы мы могли его вызвать, мы переопределяем method_missing, чтобы он проверял этот метод в списке методов.мы присвоили области видимости модуля.Он проверяет предков класса вызывающего для модуля, к которому он был применен.Если какой-либо из предков является модулем, к которому он был применен, он вызывает метод.Если нет, возникает ошибка, которая не вызывается в модуле, к которому вы его добавили.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...