Existe uma maneira de chamar um método de classe privado de uma instância em Ruby?
-
09-06-2019 - |
Pergunta
Outro que não seja self.class.send :method, args...
, claro.Eu gostaria de disponibilizar um método bastante complexo tanto no nível de classe quanto de instância, sem duplicar o código.
ATUALIZAR:
@Jonathan Branam:essa foi minha suposição, mas eu queria ter certeza de que ninguém mais havia encontrado uma maneira de contornar.A visibilidade em Ruby é muito diferente daquela em Java.Você também está certo nisso private
não funciona em métodos de classe, embora declare um método de classe privado:
class Foo
class <<self
private
def bar
puts 'bar'
end
end
end
Foo.bar
# => NoMethodError: private method 'bar' called for Foo:Class
Solução
Aqui está um trecho de código para acompanhar a pergunta.Usar "private" em uma definição de classe não se aplica a métodos de classe.Você precisa usar "private_class_method" como no exemplo a seguir.
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
Não vejo uma maneira de contornar isso.A documentação diz que você não pode especificar o recebimento de um método privado.Além disso, você só pode acessar um método privado da mesma instância.A classe Foo é um objeto diferente de uma determinada instância de Foo.
Não tome minha resposta como final.Certamente não sou um especialista, mas queria fornecer um trecho de código para que outras pessoas que tentarem responder tenham métodos de classe privados adequados.
Outras dicas
Deixe-me contribuir para esta lista de soluções e não soluções mais ou menos estranhas:
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
Se o seu método é apenas um função útil (ou seja, não depende de nenhuma variável de instância), você poderia colocar o método em um módulo e include
e extend
a classe para que esteja disponível tanto como um método de classe privada quanto como um método de instância privada.
Hoje em dia você não precisa mais dos métodos auxiliares.Você pode simplesmente incorporá-los à definição do seu método.Isso deve parecer muito familiar para o pessoal de 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
E não, você não pode chamar um método de classe privada a partir de um método de instância.No entanto, você poderia implementar o privado método de classe como público método de classe em um privado classe aninhada em vez disso, usando o private_constant
método auxiliar.Ver esta postagem do blog para mais detalhes.
Esta é a maneira de brincar com métodos de classe privada "reais".
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"
saúde
Esta é provavelmente a maneira mais "nativa de Ruby 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>
Com alguma metaprogramação Ruby, você poderia até fazer com que parecesse:
class Foo
def self.foo
'foo'
end
extend PrivateStatic
private_static :foo
end
A metaprogramação do Ruby é bastante poderosa, então você pode implementar tecnicamente quaisquer regras de escopo que desejar.Dito isto, ainda prefiro a clareza e surpresa mínima da primeira variante.
A menos que eu esteja entendendo mal, você não precisa apenas de algo assim:
class Foo
private
def Foo.bar
# Complex logic goes here
puts "hi"
end
public
def bar
Foo.bar
end
end
É claro que você poderia alterar a segunda definição para usar sua abordagem self.class.send se quisesse evitar codificar o nome da classe ...