web-dev-qa-db-fra.com

Méthodes de module privé dans Ruby

J'ai une question en deux parties

Meilleures pratiques

  • J'ai un algorithme qui effectue certaines opérations sur une structure de données en utilisant l'interface publique
  • Il s'agit actuellement d'un module avec de nombreuses méthodes statiques, toutes privées à l'exception de la seule méthode d'interface publique.
  • Il existe une variable d'instance qui doit être partagée entre toutes les méthodes.

Ce sont les options que je peux voir, quelle est la meilleure?:

  • Module avec des méthodes statiques ('module' en Ruby)
  • Classe avec des méthodes statiques
  • Mixin module à inclure dans la structure de données
  • Refactor sur la partie de l'algorithme qui modifie cette structure de données (très petite) et en fait un mixin qui appelle les méthodes statiques du module d'algorithme

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.

96
Daniel Beardsley

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
85
ucron

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
72
jfrench
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)
44
cdrev

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
28
Cameron Price

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.

12
J Cooper

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
2
Tallak Tveide

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
2
Gerry Shaw

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.

1
Nakilon

Créer un module ou une classe privée

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 .

0
Nathan Long