C'è un modo per chiamare un metodo Class privato da un'istanza in Ruby?
-
09-06-2019 - |
Domanda
Altro che self.class.send :method, args...
, Ovviamente.Mi piacerebbe rendere disponibile un metodo piuttosto complesso sia a livello di classe che di istanza senza duplicare il codice.
AGGIORNAMENTO:
@Jonathan Branam:questa era la mia supposizione, ma volevo assicurarmi che nessun altro avesse trovato un modo per aggirare il problema.La visibilità in Ruby è molto diversa da quella in Java.Hai ragione anche su questo private
non funziona sui metodi di classe, anche se questo dichiarerà un metodo di classe privata:
class Foo
class <<self
private
def bar
puts 'bar'
end
end
end
Foo.bar
# => NoMethodError: private method 'bar' called for Foo:Class
Soluzione
Ecco uno snippet di codice per accompagnare la domanda.L'utilizzo di "privato" in una definizione di classe non si applica ai metodi di classe.È necessario utilizzare "private_class_method" come nell'esempio seguente.
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
Non vedo un modo per aggirare questo problema.La documentazione dice che non è possibile specificare la ricezione di un metodo privato.Inoltre puoi accedere solo a un metodo privato dalla stessa istanza.La classe Foo è un oggetto diverso da una determinata istanza di Foo.
Non prendere la mia risposta come definitiva.Non sono certamente un esperto, ma volevo fornire uno snippet di codice in modo che altri che tentano di rispondere abbiano metodi di classe adeguatamente privati.
Altri suggerimenti
Contribuisco a questo elenco di soluzioni e non soluzioni più o meno strane:
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 il tuo metodo è semplicemente a funzione utile (ovvero, non si basa su alcuna variabile di istanza), potresti inserire il metodo in un modulo e include
E extend
la classe in modo che sia disponibile sia come metodo di classe privata che come metodo di istanza privata.
Al giorno d'oggi non hai più bisogno dei metodi di supporto.Puoi semplicemente integrarli con la definizione del tuo metodo.Questo dovrebbe sembrare molto familiare alla gente di 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 no, non puoi chiamare un metodo di classe privata da un metodo di istanza.Tuttavia, potresti invece implementare the privato metodo di classe come pubblico metodo di classe in a privato classe annidata invece, utilizzando la classe private_constant
metodo di aiuto.Vedere questo post sul blog per maggiori dettagli.
Questo è il modo di giocare con i metodi "reali" delle classi private.
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"
saluti
Questo è probabilmente il modo più "nativo di Ruby Vanilla":
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>
Con un po' di metaprogrammazione Ruby, potresti anche farlo sembrare:
class Foo
def self.foo
'foo'
end
extend PrivateStatic
private_static :foo
end
La metaprogrammazione di Ruby è piuttosto potente, quindi potresti tecnicamente implementare qualsiasi regola di ambito che potresti desiderare.Detto questo, preferirei comunque la chiarezza e sorpresa minima della prima variante.
A meno che non abbia capito male, non ti serve semplicemente qualcosa del genere:
class Foo
private
def Foo.bar
# Complex logic goes here
puts "hi"
end
public
def bar
Foo.bar
end
end
Ovviamente potresti cambiare la seconda definizione per usare il tuo approccio self.class.send se volessi evitare di codificare il nome della classe...