Есть ли способ вызвать частный метод класса из экземпляра в Ruby?

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


ОБНОВЛЕНИЕ:

@Jonathan Branam: это было мое предположение, но я хотел убедиться, что никто другой не нашел пути. Видимость в 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

Ответы

Ответ 1

Вот фрагмент кода, чтобы обсудить этот вопрос. Использование "private" в определении класса не применяется к методам класса. Вам нужно использовать "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.

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

Ответ 2

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

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

Ответ 3

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

Ответ 4

Это способ играть с "реальными" методами частного класса.

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"

веселит

Ответ 5

В настоящее время вам больше не нужны вспомогательные методы. Вы можете просто встраивать их в определение метода. Это должно быть хорошо знакомо людям 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_constant helper, Подробнее см. этот blogpost.

Ответ 6

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

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

    public
    def bar
        Foo.bar
    end
end

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