Методи приватного модуля в Ruby


108

У мене питання з двох частин

Найкраща практика

  • У мене є алгоритм, який виконує певну операцію над структурою даних за допомогою публічного інтерфейсу
  • В даний час це модуль з численними статичними методами, всі приватні, за винятком одного методу публічного інтерфейсу.
  • Є одна змінна екземпляра, яку потрібно поділити між усіма методами.

Це варіанти, які я бачу, який найкращий ?:

  • Модуль зі статичними методами («модуль» в рубіні)
  • Клас статичними методами
  • Модуль Mixin для включення в структуру даних
  • Refactor виділяє частину алгоритму, яка модифікує структуру даних (дуже мала) і робить міксин, який викликає статичні методи модуля алгоритму

Технічна частина

Чи є спосіб зробити приватний метод модуля ?

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

privateТам , здається, не мають ніякого ефекту , я все ще можу назвати Thing.privбез проблем.


5
FYI немає такого поняття, як "статичний" метод в рубіні, їх називають методами класових екземплярів
— brad

31
Старий коментар, але оскільки він має чотири оновлення, я мушу зазначити, що не існує такого поняття, як "метод екземпляра класу". "Метод класу" - це правильний термін.
— micapam

5
privateвпливає лише на методи екземпляра, а не на методи класу. використовувати private_class_methodзамість цього:module Thing; def self.pub; end; private_class_method :pub; end
— apeiros

1
Методи екземплярів класу @micapam існують у Ruby, і вони відрізняються від методів класу.
— Marnen Laibow-Koser

Відповіді:


88

Я думаю, що найкращий спосіб (і в основному, як записані існуючі лібси) це зробити, створивши клас в модулі, який займається всією логікою, і модуль просто забезпечує зручний метод, наприклад

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
Рубі новачків тут. У цьому прикладі, чи піддається експорту клас Translator як публічний інтерфейс модуля? Чи може метод "виконувати" обмежувати його доступ до GTranslate?
— rshepherd

2
@rshepherd Це performне метод, який повинен бути приватним тут, приватний метод - це приватний метод у Translatorкласі (у прикладі @ ucron немає жодного, що дуже прикро). GTranslate.translateце лише зручний метод, для GTranslate::Translator#performцього немає реального виграшу, який би його приховував, якщо це взагалі було можливо.
— Michelpm

28
Я не впевнений, що досягти, якщо тут пройшов клас. Якщо метою є використання методу приватного модуля, то це не відповідає цілі. Оскільки ви можете отримати доступ до методу "виконувати" поза модулем, зателефонувавши в GTranslate :: Translator.new.perform. Іншими словами, це не є приватним.
— Зак Сю

1
@jschorr Я думаю, що Op і ця відповідь мають на меті зробити приватний клас або метод модуля, а не метод екземпляра. Крім того, це не зробить жоден метод екземпляра приватним, оскільки self.translateоголошує метод класу / модуля.
— konsolebox

5
GTranslate::Translator.new.perform(text)- перекручений, але не приватний!
— abhillman

80

Є також Module.private_class_method, що, певно, виражає більше намірів.

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

Для коду у питанні:

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

Ruby 2.1 або новіші:

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

Я цього не знав. Чи буде це працювати і до визначення методу, як private?
— Marnen Laibow-Koser

7
Ця відповідь разом із відповіддю @ JCooper - справжнє рішення. @ MarnenLaibow-Koser Це не так. Ви можете розглянути іншу відповідь ціною більше групування та відступів. Насправді це може бути кращим рішенням для деяких. (Відповідаю просто заради посилання.)
— 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
Це має бути прийнятою відповіддю. Чистий і ідіоматичний на мій погляд.
— Мартін Няга

@MartinNyaga у цього недоліку відсутність include Writerможливості!
— Уліссе БН

28

Ви можете використовувати "включений" метод для фантазійних речей, коли модуль змішаний. Це стосується того, що ви хочете, я думаю:

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
Це розумно. Так це можливо, але, мабуть, не варто.
— Даніель Бердслі

працює добре. Я використовував included do |base| [...] endзамість def
— Crystark

5
@Crystark: Цей синтаксис існує лише в модулях, які розширюють ActiveSupport :: Побоювання, якщо я не помиляюся. тобто це річ.
— Ваз

11

На жаль, privateстосується лише методів екземплярів. Загальний спосіб отримати приватні "статичні" методи в класі - це зробити щось на кшталт:

class << self
  private

  def foo()
   ....
  end
end

Правда, я не грав з цим в модулях.


7
Це не правда. Ви можете мати методи приватного класу та приватні модульні методи.
— mikeycgto

Ви можете мати методи приватного класу, але це зробити не призведе .fooдо методу приватного класу: "private; def self.foo ()"
— Арі

@mikeycgto Care розробити різницю між методами приватного класу та методами приватного модуля? Тому що я думаю, що вони просто однакові. Слід зазначити , що як privateі private_class_methodналежать ModuleНЕ Class. Цей код працює до речі, і він є альтернативою для використання private_class_method.
— konsolebox

3

Гарний спосіб такий

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

Що щодо зберігання методів як лямбда в межах змінних / констант класу?

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

Для тесту:
UPD: величезне оновлення цього коду через 6 років показує більш чіткий спосіб заявити про приватний методd

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

Тут ми бачимо це:

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) @@Lне може бути доступ ззовні, але доступний майже скрізь
2) class << self ; private ; defуспішно робить метод dнедоступним ззовні та зсередини, self.але без нього - це дивно
3) private ; self.і private ; class << selfне робити методи приватними - вони доступні і з і безself.


лямбда - це зовсім не те саме, що методи. лямбда є типу Proc, тоді як методи - типу Method.
— Майкл Дорст

1
глобальні змінні погані
— achempion

@achempion, де ти їх бачиш?
— Накілон

@Nakilon мої вибачення, відредагуйте свою відповідь, якщо ви хочете, щоб я скасував свій голос
— achempion

0

Складіть приватний модуль або клас

Константи ніколи не є приватними. Однак можна створити модуль або клас, не призначаючи його постійному.

Тож альтернативою :private_class_methodє створення приватного модуля або класу та визначення публічних методів на ньому.

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

Використання:

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

Дивіться документи для Module.new та Class.new .


Мені дуже подобається цей метод. Але видається визначення .selfу методі неможливо , включити його до іншого класу та використовувати їх як instance_methods класу включення. Чи знаєте ви, чи є якийсь спосіб змусити це працювати?
— Шиясон

0

Цей спосіб не дозволить обмінюватися даними з приватними методами, якщо ви явно не передаєте дані за параметрами методу.

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)
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.