Есть ли способ вызвать закрытый метод Class из экземпляра в 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
8 ответов
Вот фрагмент кода, чтобы ответить на вопрос. Использование "частного" в определении класса не относится к методам класса. Вам нужно использовать "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.
Не воспринимай мой ответ как окончательный. Я, конечно, не эксперт, но я хотел предоставить фрагмент кода, чтобы у других, кто пытается ответить, были правильно закрытые методы класса.
Позвольте мне внести свой вклад в этот список более или менее странных решений и не-решений:
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
В настоящее время вам больше не нужны вспомогательные методы. Вы можете просто вставить их в определение вашего метода. Это должно быть очень знакомо людям 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
вспомогательный метод. Смотрите этот пост для более подробной информации.
Если ваш метод является просто служебной функцией (то есть он не зависит от переменных экземпляра), вы можете поместить метод в модуль и include
а также extend
класс, так что он доступен как метод частного класса, так и метод частного экземпляра.
Это, наверное, самый "родной ванильный рубин":
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 довольно мощно, так что вы можете технически реализовать любые правила области видимости, какие захотите. При этом я бы все же предпочел ясность и минимальный сюрприз первого варианта.
Предыдущие примеры (уже не?) работают. Вы не можете вызвать метод частного класса напрямую; даже из одного класса. Вместо этого вам нужно использоватьsend
.
class Foo
def self.a_method
puts "uses #{private_method} from class method"
end
def a_method
# normally this would be done with an alias or include+extend a module or calling self.class.a_method
puts "uses #{private_method} from instance method"
end
class << self
private
def private_method
"private class method"
end
end
private
def private_method
self.class.send(:private_method)
end
end
Foo.a_method => 'uses private class method from class method'
Foo.new.a_method => 'uses private class method from instance 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
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"
ура
Если я не неправильно понимаю, разве вам не нужно что-то вроде этого:
class Foo
private
def Foo.bar
# Complex logic goes here
puts "hi"
end
public
def bar
Foo.bar
end
end
Конечно, вы можете изменить второе определение, чтобы использовать подход self.class.send, если вы хотите избежать жесткого кодирования имени класса...