Metode modul privat di Ruby

108

Saya punya pertanyaan dua bagian

Praktek terbaik

  • Saya memiliki algoritme yang melakukan beberapa operasi pada struktur data menggunakan antarmuka publik
  • Saat ini merupakan modul dengan berbagai metode statis, semuanya bersifat pribadi kecuali untuk satu metode antarmuka publik.
  • Ada satu variabel contoh yang perlu dibagikan di antara semua metode.

Ini adalah opsi yang saya lihat, mana yang terbaik ?:

  • Modul dengan metode statis ('modul' dalam ruby)
  • Kelas dengan metode statis
  • Modul mixin untuk dimasukkan ke dalam struktur data
  • Refactor bagian dari algoritme yang memodifikasi struktur data tersebut (sangat kecil) dan buat campuran tersebut yang memanggil metode statis dari modul algoritme

Bagian teknis

Apakah ada cara untuk membuat metode Modul pribadi ?

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

Di privatedalam tampaknya tidak berpengaruh apa-apa , saya masih bisa menelepon Thing.privtanpa masalah.

Daniel Beardsley
sumber
5
FYI tidak ada yang namanya metode 'statis' di ruby, mereka disebut metode instance kelas
brad
31
Komentar lama, tetapi karena memiliki empat suara positif, saya harus menunjukkan bahwa tidak ada yang namanya 'metode instance kelas'. 'Metode kelas' adalah istilah yang tepat.
micapam
5
privatehanya mempengaruhi metode instance, bukan metode kelas. gunakan private_class_methodsebagai gantinya:module Thing; def self.pub; end; private_class_method :pub; end
apeiros
1
Metode instance Kelas @micapam ada di Ruby, dan mereka berbeda dari metode kelas.
Marnen Laibow-Koser

Jawaban:

88

Saya pikir cara terbaik (dan sebagian besar bagaimana libs yang ada ditulis) untuk melakukan ini adalah dengan membuat kelas dalam modul yang berhubungan dengan semua logika, dan modul hanya menyediakan metode yang mudah digunakan, misalnya

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
sumber
14
Ruby newb di sini. Dalam contoh ini, apakah kelas Translator diekspos sebagai bagian dari antarmuka publik modul? Dapatkah metode 'perform' membatasi aksesnya ke GTranslate?
rshepherd
2
@rshepherd Ini performbukanlah metode yang seharusnya menjadi privat di sini, metode privat adalah metode privat di Translatorkelas (contoh @ ucron tidak memiliki apapun, yang sangat disayangkan). GTranslate.translatehanya metode yang nyaman karena GTranslate::Translator#perform, tidak ada keuntungan nyata yang menyembunyikannya, jika memungkinkan.
michelpm
28
Saya tidak yakin apa yang dicapai dengan memiliki kelas di sini. Jika tujuannya adalah memiliki metode modul privat, maka ini tidak memenuhi tujuan. Karena Anda bisa mengakses metode "perform" dari luar modul dengan memanggil GTranslate :: Translator.new.perform. Dengan kata lain, ini tidak bersifat pribadi.
Zack Xu
1
@jschorr Saya pikir Op dan jawaban ini bermaksud untuk membuat metode kelas atau modul pribadi, bukan metode contoh. Selain itu, itu tidak akan membuat metode instance menjadi privat seperti self.translatemendeklarasikan metode kelas / modul.
konsolebox
5
GTranslate::Translator.new.perform(text)- berbelit-belit, tapi tidak pribadi!
abhillman
80

Ada juga Module.private_class_method, yang bisa dibilang lebih mengungkapkan niat.

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

Untuk kode dalam pertanyaan:

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

Ruby 2.1 atau yang lebih baru:

module Thing
  def self.pub; puts "Public method"; end
  private_class_method def self.priv; puts "Private method"; end
end
konsolebox
sumber
Saya tidak menyadari hal ini. Akankah itu bekerja sebelum definisi metode juga, seperti private?
Marnen Laibow-Koser
7
Jawaban ini bersama dengan jawaban @ JCooper adalah solusi sebenarnya. @ Marenaibow-Kosong Ini tidak. Anda dapat mempertimbangkan jawaban lain dengan mengorbankan lebih banyak pengelompokan dan lekukan. Ini sebenarnya mungkin solusi yang disukai beberapa orang. (Membalas hanya untuk referensi.)
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
sumber
4
Ini harus menjadi jawaban yang diterima. Bersih dan idiomatis menurut saya.
Martin Nyaga
@MartinNyaga kekurangannya adalah tidak ada include Writerpilihan!
Ulysse BN
28

Anda dapat menggunakan metode "termasuk" untuk melakukan hal-hal mewah ketika modul dicampur. Ini tentang apa yang Anda inginkan, menurut saya:

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
sumber
5
Itu pintar. Jadi itu mungkin, tapi mungkin tidak sepadan.
Daniel Beardsley
bekerja dengan baik. Saya menggunakan included do |base| [...] endsebagai ganti def
Crystark
5
@ Crystark: Sintaks itu hanya ada pada modul yang memperluas ActiveSupport :: Concern jika saya tidak salah. yaitu itu adalah hal rel.
Vaz
11

Sayangnya, privatehanya berlaku untuk metode instance. Cara umum untuk mendapatkan metode "statis" pribadi di kelas adalah dengan melakukan sesuatu seperti:

class << self
  private

  def foo()
   ....
  end
end

Memang saya belum bermain-main dengan melakukan ini di modul.

J Cooper
sumber
7
Ini tidak benar. Anda dapat memiliki metode kelas privat dan metode modul privat.
mikeycgto
Anda dapat memiliki metode kelas privat, tetapi hanya melakukan ini tidak akan membuat .foometode kelas privat: "private; def self.foo ()"
Ari
@mikeycgto Ingin menguraikan perbedaan antara metode kelas privat dan metode modul privat? Karena menurutku mereka sama saja. Perhatikan bahwa keduanya privatedan private_class_methoddimiliki oleh Modulenot Class. Kode ini bekerja dengan cara dan merupakan alternatif untuk menggunakan private_class_method.
konsolebox
3

Cara yang bagus adalah seperti ini

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
sumber
1

Bagaimana dengan menyimpan metode sebagai lambda dalam variabel / konstanta kelas?

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

Untuk pengujian:
UPD: pembaruan besar dari kode ini setelah 6 tahun menunjukkan cara yang lebih bersih untuk mendeklarasikan metode pribadid

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

Di sini kita melihat bahwa:

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) @@Ltidak dapat diakses dari luar tetapi dapat diakses dari hampir semua tempat
2) class << self ; private ; defberhasil membuat metode dtidak dapat diakses dari luar dan dari dalam dengan self.tetapi bukan tanpa itu - ini aneh
3) private ; self.dan private ; class << selfjangan menjadikan metode pribadi - keduanya dapat diakses keduanya dengan dan tanpaself.

Nakilon
sumber
lambda sama sekali tidak sama dengan metode. lambda adalah tipe Proc, sedangkan metode adalah tipe Method.
Michael Dorst
1
variabel global buruk
achempion
@achempion, di mana Anda melihat mereka?
Nakilon
@Nakilon saya minta maaf, edit jawaban Anda jika Anda ingin saya membatalkan suara saya
achempion
0

Buat modul atau kelas pribadi

Konstanta tidak pernah bersifat pribadi. Namun, dimungkinkan untuk membuat modul atau kelas tanpa menetapkannya ke konstanta.

Jadi alternatifnya :private_class_methodadalah membuat modul atau kelas privat dan mendefinisikan metode publik di dalamnya.

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

Pemakaian:

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

Lihat dokumen untuk Module.new dan Class.new .

Nathan Long
sumber
Saya sangat suka metode ini. Tetapi tampaknya tidak mungkin untuk menghapus .selfdefinisi dalam metode, memasukkannya ke dalam kelas lain, dan menggunakannya sebagai instance_methods dari kelas yang disertakan. Apakah Anda tahu jika ada cara untuk membuatnya berhasil?
Shiyason
0

Metode ini tidak akan mengizinkan berbagi data dengan metode privat kecuali Anda secara eksplisit meneruskan data dengan parameter metode.

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
sumber