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.