Prywatne metody modułów w Rubim

108

Mam pytanie na dwie części

Najlepsze praktyki

  • Mam algorytm, który wykonuje pewne operacje na strukturze danych przy użyciu interfejsu publicznego
  • Obecnie jest to moduł z wieloma metodami statycznymi, wszystkie prywatne, z wyjątkiem jednej metody interfejsu publicznego.
  • Istnieje jedna zmienna instancji, która musi być współużytkowana przez wszystkie metody.

Oto opcje, które widzę, które są najlepsze ?:

  • Moduł z metodami statycznymi („moduł” w języku ruby)
  • Zajęcia z metodami statycznymi
  • Moduł Mixin do włączenia do struktury danych
  • Zrefaktoryzuj część algorytmu, która modyfikuje tę strukturę danych (bardzo małą) i stwórz z niej mixin, który wywołuje statyczne metody modułu algorytmu

Część techniczna

Czy istnieje sposób na utworzenie prywatnej metody modułu ?

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

Wydaje się, privateże tam nie ma żadnego efektu , nadal mogę dzwonić Thing.privbez problemu.

Daniel Beardsley
źródło
5
FYI nie ma czegoś takiego jak metoda
``
31
Stary komentarz, ale ponieważ ma cztery głosy za, muszę zaznaczyć, że nie ma czegoś takiego jak „metoda instancji klasy”. „Metoda klasowa” to termin właściwy.
micapam
5
privatewpływa tylko na metody instancji, nie na metody klas. użyj private_class_methodzamiast tego:module Thing; def self.pub; end; private_class_method :pub; end
apeiros
1
@micapam Metody instancji klasy istnieją w Rubim i różnią się od metod klasowych.
Marnen Laibow-Koser

Odpowiedzi:

88

Myślę, że najlepszym sposobem (a przede wszystkim sposobem pisania istniejących bibliotek) jest utworzenie w module klasy, która zajmuje się całą logiką, a moduł zapewnia tylko wygodną metodę, np.

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
ucron
źródło
14
Tutaj Ruby Newb. Czy w tym przykładzie klasa Translator jest uwidoczniona jako część publicznego interfejsu modułu? Czy dostęp do metody „perform” może być ograniczony do GTranslate?
rshepherd
2
@rshepherd Nie performjest to metoda, która ma być tutaj prywatna, metoda prywatna jest metodą prywatną w Translatorklasie (przykład @ ucron nie ma żadnej, co jest bardzo niefortunne). GTranslate.translatejest tylko wygodną metodą GTranslate::Translator#perform, nie ma żadnej realnej korzyści, by to ukryć, jeśli było to w ogóle możliwe.
michelpm
28
Nie jestem pewien, co osiągnięto, mając tutaj zajęcia. Jeśli celem jest posiadanie metody modułu prywatnego, to nie spełnia celu. Ponieważ możesz uzyskać dostęp do metody „perform” spoza modułu, wywołując GTranslate :: Translator.new.perform. Innymi słowy, nie jest to prywatne.
Zack Xu
1
@jschorr Myślę, że Op i ta odpowiedź mają na celu utworzenie prywatnej metody klasy lub modułu, a nie metody instancji. Ponadto nie spowoduje to, że żadna metoda instancji będzie prywatna, ponieważ self.translatedeklaruje metodę klasy / modułu.
konsolebox
5
GTranslate::Translator.new.perform(text)- zawiłe, ale nie prywatne!
abhillman
80

Jest też coś Module.private_class_method, co prawdopodobnie wyraża więcej zamiarów.

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

Dla kodu w pytaniu:

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

Ruby 2.1 lub nowszy:

module Thing
  def self.pub; puts "Public method"; end
  private_class_method def self.priv; puts "Private method"; end
end
konsolebox
źródło
Nie byłem tego świadomy. Czy zadziała też przed definicją metody private?
Marnen Laibow-Koser
7
Ta odpowiedź wraz z odpowiedzią @ JCooper jest prawdziwym rozwiązaniem. @ MarnenLaibow-Koser Nie. Możesz rozważyć drugą odpowiedź kosztem większej liczby grupowań i wcięć. W rzeczywistości może to być preferowane rozwiązanie dla niektórych. (Odpowiadam tylko dla odniesienia.)
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)
cdrev
źródło
4
To powinna być akceptowana odpowiedź. Moim zdaniem czysty i idiomatyczny.
Martin Nyaga,
@MartinNyaga ma to tę wadę, że nie ma include Writeropcji!
Ulysse BN
28

Możesz użyć metody „dołączonej” do robienia wymyślnych rzeczy, gdy moduł jest wmieszany. Myślę, że to robi to, co chcesz:

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
Cameron Price
źródło
5
To sprytne. Więc jest to możliwe, ale prawdopodobnie nie warto.
Daniel Beardsley
działa dobrze. Użyłem included do |base| [...] endzamiast def
Crystark
5
@Crystark: Ta składnia istnieje tylko w modułach rozszerzających ActiveSupport :: Concern, jeśli się nie mylę. tj. jest to sprawa szyn.
Vaz
11

Niestety privatedotyczy to tylko metod instancji. Ogólnym sposobem uzyskania prywatnych metod „statycznych” w klasie jest wykonanie czegoś takiego:

class << self
  private

  def foo()
   ....
  end
end

Trzeba przyznać, że nie bawiłem się tym w modułach.

J Cooper
źródło
7
To nie jest prawda. Możesz mieć metody klasy prywatnej i metody modułu prywatnego.
mikeycgto
Można mieć prywatnych metod klasy, ale po prostu to robi nie uczyni .fooprywatną metody klasy: „prywatnym; def self.foo ()”
Ari
@mikeycgto Chcesz wyjaśnić różnicę między metodami klas prywatnych a metodami modułów prywatnych? Ponieważ myślę, że są takie same. Zauważ, że oba privatei private_class_methodsą własnością Modulenie Class. Ten kod działa nawiasem mówiąc i jest alternatywą dla używania private_class_method.
konsolebox,
3

Tak wygląda fajny sposób

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
Tallak Tveide
źródło
1

O co chodzi z przechowywaniem metod jako lambd w zmiennych / stałych klasach?

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

Do testu:
UPD: ogromna aktualizacja tego kodu po 6 latach pokazuje czystszy sposób zadeklarowania metody prywatnejd

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

Tutaj widzimy, że:

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) @@Lnie można uzyskać dostępu z zewnątrz, ale jest dostępny prawie z każdego miejsca
2) class << self ; private ; defskutecznie sprawia, że ​​metoda jest dniedostępna z zewnątrz i od wewnątrz, self.ale nie bez niej - to dziwne
3) private ; self.i private ; class << selfnie czynią metod prywatnymi - są dostępne zarówno z I bezself.

Nakilon
źródło
lambdy wcale nie są tym samym, co metody. lambdy są typu Proc, podczas gdy metody są typu Method.
Michael Dorst,
1
zmienne globalne są złe
Achempion
@achempion, gdzie je widzisz?
Nakilon,
@Nakilon moje przeprosiny, edytuj swoją odpowiedź, jeśli chcesz, abym anulował mój głos
achempion
0

Utwórz moduł lub zajęcia prywatne

Stałe nigdy nie są prywatne. Możliwe jest jednak utworzenie modułu lub klasy bez przypisywania ich do stałej.

Więc alternatywą :private_class_methodjest utworzenie prywatnego modułu lub klasy i zdefiniowanie w nim publicznych metod.

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

Stosowanie:

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

Zobacz dokumentację dla Module.new i Class.new .

Nathan Long
źródło
Bardzo podoba mi się ta metoda. Wydaje się jednak, że nie jest możliwe usunięcie z .selfdefinicji metod, uwzględnienie ich w innej klasie i użycie ich jako metod instance_metod klasy włączającej. Czy wiesz, czy jest jakiś sposób, aby to zadziałało?
Shiyason,
0

Ta metoda nie zezwala na udostępnianie danych metodom prywatnym, chyba że jawnie przekażesz dane za pomocą parametrów metody.

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)
Gerry Shaw
źródło