Metodi del modulo privato in Ruby


108

Ho una domanda in due parti

La migliore pratica

  • Ho un algoritmo che esegue alcune operazioni su una struttura dati utilizzando l'interfaccia pubblica
  • Attualmente è un modulo con numerosi metodi statici, tutti privati ​​tranne l'unico metodo di interfaccia pubblica.
  • C'è una variabile di istanza che deve essere condivisa tra tutti i metodi.

Queste sono le opzioni che posso vedere, qual è la migliore ?:

  • Modulo con metodi statici ('module' in ruby)
  • Classe con metodi statici
  • Modulo Mixin per l'inclusione nella struttura dati
  • Rifattorizza la parte dell'algoritmo che modifica quella struttura dati (molto piccola) e rendila un mixin che chiama i metodi statici del modulo algoritmo

Parte tecnica

C'è un modo per creare un metodo Modulo privato ?

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

Il privatelà dentro non sembra avere alcun effetto , posso ancora chiamare Thing.privsenza alcun problema.


5
Cordiali saluti, non esiste un metodo 'statico' in ruby, sono chiamati metodi di istanza di classe
Brad

31
Un vecchio commento, ma poiché ha quattro voti positivi, devo sottolineare che non esiste un "metodo di istanza di classe". "Metodo di classe" è il termine corretto.
micapam

5
privateinteressa solo i metodi di istanza, non i metodi di classe. usa private_class_methodinvece:module Thing; def self.pub; end; private_class_method :pub; end
apeiros

1
I metodi di istanza della classe @micapam esistono in Ruby e sono diversi dai metodi di classe.
Marnen Laibow-Koser

Risposte:


88

Penso che il modo migliore (e principalmente come vengono scritte le librerie esistenti) per farlo sia creare una classe all'interno del modulo che si occupa di tutta la logica, e il modulo fornisce solo un metodo conveniente, ad es.

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
Ruby newb qui. In questo esempio, la classe Translator è esposta come parte dell'interfaccia pubblica del modulo? Il metodo "perform" può avere accesso limitato a GTranslate?
rshepherd

2
@rshepherd The performnon è il metodo che dovrebbe essere privato qui, il metodo privato è il metodo privato nella Translatorclasse (l'esempio di @ ucron non ne ha, il che è molto sfortunato). GTranslate.translateè solo un metodo conveniente per GTranslate::Translator#perform, non c'è alcun guadagno reale che lo nasconda, se fosse possibile.
michelpm

28
Non sono sicuro di cosa si ottenga tenendo una lezione qui. Se l'obiettivo è avere un metodo del modulo privato, questo non soddisfa l'obiettivo. Perché puoi accedere al metodo "perform" dall'esterno del modulo chiamando GTranslate :: Translator.new.perform. In altre parole, non è privato.
Zack Xu

1
@jschorr Penso che Op e questa risposta intendono creare un metodo di classe o modulo privato, non un metodo di istanza. Inoltre, ciò non renderà privato alcun metodo di istanza poiché self.translatedichiara un metodo di classe / modulo.
konsolebox

5
GTranslate::Translator.new.perform(text)- contorto, ma non privato!
abhillman

80

C'è anche Module.private_class_method, che probabilmente esprime più intento.

module Foo
  def self.included(base)
    base.instance_eval do
      def method_name
        # ...
      end
      private_class_method :method_name
    end
  end
end

Per il codice nella domanda:

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

Ruby 2.1 o più recente:

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

Non ne ero consapevole. Funzionerà anche prima della definizione del metodo, come private?
Marnen Laibow-Koser

7
Questa risposta insieme alla risposta di @ JCooper è la vera soluzione. @ MarnenLaibow-Koser Non è così. Puoi considerare l'altra risposta al prezzo di più raggruppamenti e rientri. Potrebbe effettivamente essere la soluzione preferita per alcuni. (Risposta solo per amor di riferimento.)
konsolebox

58
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)

4
Questa dovrebbe essere la risposta accettata. Pulito e idiomatico secondo me.
Martin Nyaga

@MartinNyaga questo ha lo svantaggio di non avere include Writeropzioni!
Ulysse BN

28

Puoi usare il metodo "incluso" per fare cose fantasiose quando un modulo è mischiato. Questo fa quello che vuoi, penso:

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
È intelligente. Quindi è possibile, ma probabilmente non ne vale la pena.
Daniel Beardsley,

funziona bene. Ho usato included do |base| [...] endinvece di def
Crystark

5
@Crystark: quella sintassi esiste solo sui moduli che estendono ActiveSupport :: Concern se non mi sbaglio. cioè è una cosa dei binari.
Vaz

11

Sfortunatamente, privatesi applica solo ai metodi di istanza. Il modo generale per ottenere metodi "statici" privati ​​in una classe è fare qualcosa come:

class << self
  private

  def foo()
   ....
  end
end

Devo ammettere che non ho giocato a farlo nei moduli.


7
Questo non è vero. Puoi avere metodi di classi private e metodi di moduli privati.
mikeycgto

Puoi avere metodi di classe privata, ma solo facendo questo non .foocreerai un metodo di classe privata: "private; def self.foo ()"
Ari

@mikeycgto Ti interessa elaborare la differenza tra metodi di classe privata e metodi di modulo privato? Perché penso che siano la stessa cosa. Nota che entrambi privatee private_class_methodsono di proprietà di Modulenot Class. Questo codice funziona tra l'altro ed è l'alternativa all'uso private_class_method.
konsolebox

3

Un bel modo è così

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

1

Che dire della memorizzazione di metodi come lambda all'interno di variabili / costanti di classe?

module MyModule
  @@my_secret_method = lambda {
    # ...
  }
  # ...
end

Per test:
UPD: un enorme aggiornamento di questo codice dopo 6 anni mostra un modo più pulito per dichiarare il metodo privatod

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

Qui vediamo che:

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) @@Lnon può essere accessibile dall'esterno ma è accessibile da quasi ovunque
2) class << self ; private ; defrende con successo il metodo dinaccessibile dall'esterno e dall'interno con self.ma non senza di esso - questo è strano
3) private ; self.e private ; class << selfnon rendere i metodi privati ​​- sono accessibili entrambi con e senzaself.


lambda non sono affatto la stessa cosa dei metodi. lambda sono di tipo Proc, mentre i metodi sono di tipo Method.
Michael Dorst

1
le variabili globali sono pessime
achempion

@achempion, dove li vedi?
Nakilon

@Nakilon le mie scuse, modifica la tua risposta se vuoi che annulli il mio voto
achempion

0

Crea un modulo o una lezione privata

Le costanti non sono mai private. Tuttavia, è possibile creare un modulo o una classe senza assegnarlo a una costante.

Quindi un'alternativa a :private_class_methodè creare un modulo o una classe privata e definire metodi pubblici su di essa.

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

Uso:

PublicModule.do_stuff("whatever") # => "WHATEVER"

Vedere i documenti per Module.new e Class.new .


Mi piace molto questo metodo. Ma non sembra possibile rimuovere il .selfnelle definizioni del metodo, includerlo in un'altra classe e usarli come metodi_istanza della classe inclusa. Sai se c'è un modo per farlo funzionare?
Shiyason

0

Questo metodo non consentirà la condivisione dei dati con i metodi privati ​​a meno che non si passino esplicitamente i dati tramite i parametri del metodo.

module Thing
  extend self

  def pub
    puts priv(123)
  end

  private
  
  def priv(value)
    puts "Private method with value #{value}"
  end
end

Thing.pub
# "Private method with value 123"

Thing.priv
# NoMethodError (private method `priv' called for Thing:Module)
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.