J'ai une question en deux parties
Meilleures pratiques
Ce sont les options que je peux voir, quelle est la meilleure?:
partie technique
Existe-t-il un moyen de faire un méthode du module privé?
module Thing
def self.pub; puts "Public method"; end
private
def self.priv; puts "Private method"; end
end
Le private
là-dedans ne semble pas avoir d'effet, je peux toujours appeler Thing.priv
sans problème.
Je pense que la meilleure façon (et surtout comment les bibliothèques existantes sont écrites) de le faire en créant une classe dans le module qui traite de toute la logique, et le module fournit simplement une méthode pratique, par exemple.
module GTranslate
class Translator
def perform( text ); 'hola munda'; end
end
def self.translate( text )
t = Translator.new
t.perform( text )
end
end
Il y a aussi Module.private_class_method
, qui exprime sans doute plus d'intention.
module Foo
def self.included(base)
base.instance_eval do
def method_name
# ...
end
private_class_method :method_name
end
end
end
Pour le code dans la question:
module Thing
def self.pub; puts "Public method"; end
def self.priv; puts "Private method"; end
private_class_method :priv
end
Ruby 2.1 ou plus récent:
module Thing
def self.pub; puts "Public method"; end
private_class_method def self.priv; puts "Private method"; end
end
module Writer
class << self
def output(s)
puts upcase(s)
end
private
def upcase(s)
s.upcase
end
end
end
Writer.output "Hello World"
# -> HELLO WORLD
Writer.upcase "Hello World"
# -> so.rb:16:in `<main>': private method `upcase' called for Writer:Module (NoMethodError)
Vous pouvez utiliser la méthode "incluse" pour faire des choses fantaisistes quand un module est mélangé. Cela fait ce que vous voulez, je pense:
module Foo
def self.included(base)
class << base
def public_method
puts "public method"
end
def call_private
private_method
end
private
def private_method
puts "private"
end
end
end
end
class Bar
include Foo
end
Bar.public_method
begin
Bar.private_method
rescue
puts "couldn't call private method"
end
Bar.call_private
Malheureusement, private
ne s'applique qu'aux méthodes d'instance. La manière générale d'obtenir des méthodes "statiques" privées dans une classe est de faire quelque chose comme:
class << self
private
def foo()
....
end
end
Certes, je n'ai pas joué avec cela en modules.
Une belle façon est comme ça
module MyModule
class << self
def public_method
# you may call the private method here
tmp = private_method
:public
end
private def private_method
:private
end
end
end
# calling from outside the module
puts MyModule::public_method
Le meilleur modèle que j'ai trouvé en faisant cela dans Rails est d'abandonner les modules qui veulent avoir des méthodes privées et d'utiliser une classe Singleton à la place. Cela ne semble pas correct mais cela le fait fonctionne et semble plus propre que d'autres exemples que j'ai vus dans cette question.
J'adorerais entendre d'autres opinions à ce sujet.
Exemple:
ErrorService.notify("Something bad happened")
class ErrorService
include Singleton
class << self
delegate :notify, to: :instance
end
def notify(message, severity: :error)
send_exception_notification(message)
log_message(message, severity)
end
private
def send_exception_notification(message)
# ...
end
def log_message(message, severity)
# ...
end
end
Qu'en est-il du stockage des méthodes en tant que lambdas dans les variables/constantes de classe?
module MyModule
@@my_secret_method = lambda {
# ...
}
# ...
end
Pour test:
PD: énorme mise à jour de ce code après 6 ans montre une façon plus propre de déclarer une méthode privée d
module A
@@L = lambda{ "@@L" }
def self.a ; @@L[] ; end
def self.b ; a ; end
class << self
def c ; @@L[] ; end
private
def d ; @@L[] ; end
end
def self.e ; c ; end
def self.f ; self.c ; end
def self.g ; d ; end
def self.h ; self.d ; end
private
def self.i ; @@L[] ; end
class << self
def j ; @@L[] ; end
end
public
def self.k ; i ; end
def self.l ; self.i ; end
def self.m ; j ; end
def self.n ; self.j ; end
end
for expr in %w{ A.a A.b A.c A.d A.e A.f A.g A.h A.i A.j A.k A.l A.m A.n }
puts "#{expr} => #{begin ; eval expr ; rescue => e ; e ; end}"
end
Ici, nous voyons que:
A.a => @@L
A.b => @@L
A.c => @@L
A.d => private method `d' called for A:Module
A.e => @@L
A.f => @@L
A.g => @@L
A.h => private method `d' called for A:Module
A.i => @@L
A.j => @@L
A.k => @@L
A.l => @@L
A.m => @@L
A.n => @@L
1) @@L
ne peut pas être accessible de l'extérieur mais est accessible de presque partout
2) class << self ; private ; def
rend la méthode d
inaccessible de l'extérieur et de l'intérieur avec self.
mais pas sans - c'est bizarre
3) private ; self.
et private ; class << self
ne rend pas les méthodes privées - elles sont accessibles avec et sans self.
Les constantes ne sont jamais privées. Cependant, il est possible de créer un module ou une classe sans l'assigner à une constante.
Donc, une alternative à :private_class_method
consiste à créer un module ou une classe privé et à y définir des méthodes publiques.
module PublicModule
def self.do_stuff(input)
@private_implementation.do_stuff(input)
end
@private_implementation = Module.new do
def self.do_stuff(input)
input.upcase # or call other methods on module
end
end
end
Usage:
PublicModule.do_stuff("whatever") # => "WHATEVER"
Voir les documents pour Module.new et Class.new .