Ruby의 비공개 모듈 메서드


108

두 부분으로 된 질문이 있습니다

모범 사례

  • 공용 인터페이스를 사용하여 데이터 구조에서 일부 작업을 수행하는 알고리즘이 있습니다.
  • 현재 하나의 공용 인터페이스 메서드를 제외하고 모두 전용 인 수많은 정적 메서드가있는 모듈입니다.
  • 모든 메소드간에 공유해야하는 인스턴스 변수가 하나 있습니다.

다음은 내가 볼 수있는 옵션이며 가장 좋은 것은 무엇입니까? :

  • 모듈 정적 (루비 '모듈')와 방법
  • 정적 메서드가있는 클래스
  • 데이터 구조에 포함하기위한 Mixin 모듈
  • 해당 데이터 구조 (매우 작음)를 수정하는 알고리즘 부분을 리팩터링 하고 알고리즘 모듈의 정적 메서드를 호출하는 믹스 인으로 만듭니다.

기술 부분

private Module 메서드 를 만드는 방법이 있습니까?

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

private거기에는 어떤 영향을 미칠 것 같지 않습니다 난 여전히 호출 할 수 있습니다, Thing.priv문제없이.


5
참고로 루비에는 '정적'메소드가 없습니다. 클래스 인스턴스 메소드라고합니다
brad

31
오래된 의견이지만 4 개의 찬성표가 있기 때문에 '클래스 인스턴스 메서드'와 같은 것이 없다는 점을 지적해야합니다. '클래스 방법'은 올바른 용어입니다.
micapam 2013 년

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
여기 루비 newb. 이 예에서 Translator 클래스가 모듈의 공용 인터페이스의 일부로 노출됩니까? 'perform'메소드가 GTranslate에 대한 액세스를 제한 할 수 있습니까?
rshepherd 2011-07-23

2
@rshepherd perform여기서는 private 메서드가 Translator아니며 private 메서드는 클래스 의 private 메서드입니다 (@ucron의 예제에는 매우 안타까운 일이 없습니다). GTranslate.translate에 대한 편리한 방법 일 뿐이며 GTranslate::Translator#perform가능하다면 은폐하는 실제 이득이 없습니다.
michelpm 2013

28
여기서 수업을함으로써 무엇을 얻을 수 있는지 잘 모르겠습니다. 목표가 비공개 모듈 메서드를 갖는 것이라면 목표를 달성하지 못합니다. GTranslate :: Translator.new.perform을 호출하여 모듈 외부에서 "perform"메소드에 액세스 할 수 있기 때문입니다. 즉, 개인이 아닙니다.
Zack Xu

1
@jschorr 나는 Op 와이 대답이 인스턴스 메서드가 아닌 개인 클래스 또는 모듈 메서드를 만들려고한다고 생각합니다. 또한 self.translate클래스 / 모듈 메서드 를 선언 하므로 인스턴스 메서드를 비공개로 만들지 않습니다 .
konsolebox 2014 년

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 2011 년

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
이것은 받아 들여진 대답이어야합니다. 제 생각에는 깨끗하고 관용적입니다.
Martin Nyaga

@MartinNyaga 이것은 include Writer옵션 이 없다는 단점이 있습니다!
Ulysse BN

28

"included"메서드를 사용하여 모듈이 혼합 될 때 멋진 작업을 수행 할 수 있습니다. 이것은 원하는대로 수행합니다.

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
영리합니다. 따라서 가능하지만 그만한 가치는 없습니다.
Daniel Beardsley

잘 작동합니다. 나는 included do |base| [...] enddef 대신 사용
Crystark

5
@Crystark :이 구문은 내가 착각하지 않았다면 ActiveSupport :: Concern을 확장하는 모듈에만 존재합니다. 즉 그것은 레일의 것입니다.
Vaz

11

불행히도 private인스턴스 메서드에만 적용됩니다. 클래스에서 비공개 "정적"메서드를 얻는 일반적인 방법은 다음과 같은 작업을 수행하는 것입니다.

class << self
  private

  def foo()
   ....
  end
end

나는 모듈에서 이것을 해본 적이 없다.


7
이것은 사실이 아닙니다. 개인 클래스 메서드와 개인 모듈 메서드를 가질 수 있습니다.
mikeycgto

개인 클래스 메서드를 가질 수 있지만 이렇게하면 .foo개인 클래스 메서드가 만들어지지 않습니다 . "private; def self.foo ()"
Ari

@mikeycgto private 클래스 메서드와 private 모듈 메서드의 차이점을 자세히 설명 하시겠습니까? 나는 그들이 똑같다고 생각하기 때문입니다. private및 둘 다 not private_class_method가 소유합니다 . 이 코드는 어쨌든 작동하며 . ModuleClassprivate_class_method
konsolebox 2014 년

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 년 후이 코드를 대대적으로 업데이트하면 private 메서드를보다 깔끔하게 선언 할 수 있습니다.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외부에서 액세스 할 수 있지만, 거의 모든 곳에서 액세스 할 수 없습니다
)이 class << self ; private ; def성공적으로 방법을 만드는 d외부에서 액세스 할 수와와 내부에서 self.이 이상해 -하지만 그것없이
3) private ; self.private ; class << self방법은 비공개로하지 않습니다 - 그들은 액세스 할 수있는 두 유무에 관계없이self.


람다는 메소드와 전혀 다릅니다. 람다는 형식 Proc이고 메서드는 형식 Method입니다.
Michael Dorst 2013 년

1
전역 변수는 나쁜
achempion

@achempion, 어디에서 볼 수 있습니까?
Nakilon 2010 년

내 사과 @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.newClass.new 문서를 참조하십시오 .


이 방법이 정말 마음에 듭니다. 그러나 .self메서드 정의에서 를 제거하고 다른 클래스에 포함하고 포함하는 클래스의 instance_methods로 사용하는 것은 불가능 해 보입니다 . 작동시킬 방법이 있는지 알고 있습니까?
Shiyason

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)
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.