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

Кроме 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
Пошаговое руководство по созданию собственного Slackbot: От установки до развертывания
Пошаговое руководство по созданию собственного Slackbot: От установки до развертывания
Шаг 1: Создание приложения Slack Чтобы создать Slackbot, вам необходимо создать приложение Slack. Войдите в свою учетную запись Slack и перейдите на...
17
0
15 318
7
Перейти к ответу Данный вопрос помечен как решенный

Ответы 7

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

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

    public
    def bar
        Foo.bar
    end
end

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

Ответ принят как подходящий

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

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

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

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

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"

ваше здоровье

Получение ошибки для первого примера: 1.9.3p327 :078 > f=Foo.new => #<Foo:0x0000000293baa0> 1.9.3p327 :079 > f.class.send :calling_private_method NoMethodError: undefined method `calling_private_method' for Foo:Class from (irb):79 from ~/.rvm/rubies/ruby-1.9.3-p327/bin/irb:16:in `<main>'

TrinitronX 23.02.2013 01:57

Да, это не работает (или, возможно, больше не работает).

Jacob Crofts 01.09.2017 03:44

@metakungfu часть, с которой я (все еще) не согласен, вызывает метод частного класса из любого метода экземпляра. Изнутри метода экземпляра self.class.some_private_method выдаст ошибку. Меня беспокоит, что, если мы не используем отражение, методы частного класса могут использоваться только другими методами класса.

Jacob Crofts 06.09.2017 00:13

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

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

Как и исходные почтовые запросы, он должен вызов метод класса. Это нехорошо, поскольку он копирует метод в набор методов экземпляра класса. Если, например, обезьяна исправит метод класса C :: foo, у вас внезапно возникнет несоответствие между уровнем класса :: foo и уровнем экземпляра #foo.

Chad M 24.11.2016 13:07

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

Это, наверное, самый «родной ванильный Ruby» способ:

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

Другие вопросы по теме