118 votes

Méthodes de modules privés en Ruby

J'ai une question en deux parties

Meilleures pratiques

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

Ce sont les options que je vois, laquelle est la meilleure ?

  • Module avec des méthodes statiques ('module' en ruby)
  • Classe avec des méthodes statiques
  • Mixin pour l'inclure dans la structure de données
  • Refactor sortir la partie de l'algorithme qui modifie cette structure de données (très petite) et en faire un mixin qui appelle les méthodes statiques du module de l'algorithme.

Partie technique

Y a-t-il un moyen de faire un Méthode Module privé ?

module Thing
  def self.pub; puts "Public method"; end
  private
  def self.priv; puts "Private method"; end
end

El private ne semble pas avoir d'effet. Je peux toujours appeler Thing.priv sans problème.

5 votes

Pour votre information, il n'existe pas de méthode "statique" en ruby, ce sont des méthodes d'instance de classe.

36 votes

Un vieux commentaire, mais comme il a quatre votes positifs, je dois signaler qu'il n'existe pas de "méthode d'instance de classe". Le terme correct est "méthode de classe".

5 votes

private n'affecte que les méthodes d'instance, pas les méthodes de classe. utilisez private_class_method à la place : module Thing; def self.pub; end; private_class_method :pub; end

94voto

ucron Points 1884

Je pense que la meilleure façon de faire (et surtout la façon dont les librairies existantes sont écrites) est de créer une classe au sein du module qui s'occupe de toute la logique, et le module fournit juste une méthode pratique, par ex.

module GTranslate
  class Translator
    def perform(text)
      translate(text)
    end

    private

    def translate(text)
      # do some private stuff here
    end
  end

  def self.translate(text)
    t = Translator.new
    t.perform(text)
  end
end

14 votes

Je suis un novice en matière de rubis. Dans cet exemple, la classe Translator est-elle exposée dans le cadre de l'interface publique du module ? L'accès à la méthode "perform" peut-il être limité à GTranslate ?

2 votes

@rshepherd Le perform n'est pas la méthode qui est censée être privée ici, la méthode privée est celle qui se trouve dans le fichier Translator (l'exemple d'@ucron n'en a pas, ce qui est très regrettable). GTranslate.translate est seulement une méthode pratique pour GTranslate::Translator#perform il n'y a aucun intérêt à le dissimuler, si tant est que cela soit possible.

30 votes

Je ne suis pas sûr de ce qu'on obtient en ayant une classe ici. Si l'objectif est d'avoir une méthode de module privée, alors ceci ne répond pas à l'objectif. Parce que vous pouvez accéder à la méthode "perform" depuis l'extérieur du module en appelant GTranslate::Translator.new.perform. En d'autres termes, elle n'est pas privée.

83voto

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 de 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

67voto

CDR LDN Points 102
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)

28voto

Cameron Price Points 869

Vous pouvez utiliser la méthode "included" pour faire des choses fantaisistes lorsqu'un module est intégré. Cela fait à peu près 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

5 votes

C'est malin. Donc c'est possible, mais ça ne vaut probablement pas le coup.

0 votes

Fonctionne bien. J'ai utilisé included do |base| [...] end au lieu de def

5 votes

@Crystark : Cette syntaxe n'existe que sur les modules qui étendent ActiveSupport::Concern si je ne me trompe pas. c'est-à-dire que c'est un truc de rails.

11voto

J Cooper Points 7695

Malheureusement, private ne s'applique qu'aux méthodes d'instance. La manière générale d'obtenir des méthodes privées "statiques" dans une classe est de faire quelque chose comme :

class << self
  private

  def foo()
   ....
  end
end

Il est vrai que je n'ai pas joué à faire cela dans les modules.

7 votes

Ce n'est pas vrai. Vous pouvez avoir des méthodes de classe privées et des méthodes de module privées.

0 votes

Vous pouvez avoir des méthodes de classe privées, mais faire cela ne rendra pas .foo une méthode de classe privée : "private ; def self.foo()"

0 votes

@mikeycgto Pourriez-vous préciser la différence entre les méthodes de classe privées et les méthodes de module privées ? Parce que je pense que c'est la même chose. Notez que les deux private y private_class_method sont détenus par Module pas Class . Ce code fonctionne d'ailleurs et c'est l'alternative à l'utilisation de private_class_method .

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X