Методи приватного модуля в 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.