Есть ли способ вызвать закрытый метод класса из экземпляра в Ruby? - PullRequest
15 голосов
/ 21 августа 2008

Кроме self.class.send :method, args..., конечно. Я хотел бы сделать довольно сложный метод доступным как на уровне класса, так и на уровне экземпляра без дублирования кода.


ОБНОВЛЕНИЕ:

@ Джонатан Бранам: это было мое предположение, но я хотел убедиться, что никто другой не нашел обходного пути. Видимость в Ruby сильно отличается от видимости в Java. Вы также совершенно правы, что private не работает с методами класса, хотя при этом будет объявлен метод закрытого класса:

class Foo
  class <<self
    private
    def bar
      puts 'bar'
    end
  end
end

Foo.bar
# => NoMethodError: private method 'bar' called for Foo:Class

Ответы [ 7 ]

12 голосов
/ 21 августа 2008

Вот фрагмент кода, чтобы ответить на вопрос. Использование «частного» в определении класса не относится к методам класса. Вам нужно использовать "private_class_method", как в следующем примере.

class Foo
  def self.private_bar
    # Complex logic goes here
    puts "hi"
  end
  private_class_method :private_bar
  class <<self
    private
    def another_private_bar
      puts "bar"
    end
  end
  public
  def instance_bar
    self.class.private_bar
  end
  def instance_bar2
    self.class.another_private_bar
  end
end

f=Foo.new
f=instance_bar # NoMethodError: private method `private_bar' called for Foo:Class
f=instance_bar2 # NoMethodError: private method `another_private_bar' called for Foo:Class

Я не вижу способа обойти это. В документации сказано, что вы не можете указать получение частного метода. Также вы можете получить доступ только к приватному методу из того же экземпляра. Класс Foo является объектом, отличным от данного экземпляра Foo.

Не воспринимайте мой ответ как окончательный. Я, конечно, не эксперт, но я хотел предоставить фрагмент кода, чтобы у других, кто попытается ответить, были методы частного класса.

3 голосов
/ 15 июня 2014

Позвольте мне внести свой вклад в этот список более или менее странных решений и не решений:

puts RUBY_VERSION # => 2.1.2

class C
  class << self
    private def foo
      'Je suis foo'
    end
  end

  private define_method :foo, &method(:foo)

  def bar
    foo
  end
end

puts C.new.bar # => Je suis foo
puts C.new.foo # => NoMethodError
0 голосов
/ 03 апреля 2019

Это, пожалуй, самый "родной ванильный рубин":

class Foo
  module PrivateStatic # like Java
    private def foo
      'foo'
    end
  end
  extend PrivateStatic
  include PrivateStatic

  def self.static_public_call
    "static public #{foo}"
  end

  def public_call
    "instance public #{foo}"
  end
end

Foo.static_public_call # 'static public foo'
Foo.new.public_call # 'instance public foo'
Foo.foo # NoMethodError: private method `foo' called for Foo:Class
Foo.new.foo # NoMethodError: private method `foo' called for #<Foo:0x00007fa154d13f10>

С помощью некоторого метапрограммирования на Ruby вы можете даже сделать его похожим на:

class Foo
  def self.foo
    'foo'
  end

  extend PrivateStatic
  private_static :foo
end

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

0 голосов
/ 10 марта 2016

В настоящее время вам больше не нужны вспомогательные методы. Вы можете просто вставить их в определение вашего метода. Это должно быть очень знакомо людям Java:

class MyClass

  private_class_method def self.my_private_method
    puts "private class method"
  end

  private def my_private_method
    puts "private instance method"
  end

end

И нет, вы не можете вызвать метод закрытого класса из метода экземпляра. Однако вместо этого вы можете реализовать метод класса private как метод класса public во вложенном классе private , используя вспомогательный метод private_constant. См. этот пост для получения более подробной информации.

0 голосов
/ 04 июля 2012

Это способ играть с «настоящими» методами частного класса.

class Foo
  def self.private_bar
    # Complex logic goes here
    puts "hi"
  end
  private_class_method :private_bar
  class <<self
    private
    def another_private_bar
      puts "bar"
    end
  end
  public
  def instance_bar
    self.class.private_bar
  end
  def instance_bar2
    self.class.another_private_bar
  end
  def calling_private_method
    Foo.send :another_private_bar
    self.class.send :private_bar
  end
end
f=Foo.new
f.send :calling_private_method 
 # "bar"
 # "hi"
Foo.send :another_private_bar
# "bar"

ура

0 голосов
/ 17 декабря 2011

Если ваш метод - просто служебная функция (то есть он не зависит от переменных экземпляра), вы можете поместить метод в модуль и include и extend класс так что он доступен как метод частного класса, так и метод частного экземпляра.

0 голосов
/ 21 августа 2008

Если я не понимаю, разве вам не нужно что-то вроде этого:

class Foo
    private
    def Foo.bar
        # Complex logic goes here
        puts "hi"
    end

    public
    def bar
        Foo.bar
    end
end

Конечно, вы можете изменить второе определение, чтобы использовать подход self.class.send, если вы хотите избежать жесткого кодирования имени класса ...

...