Rubyのプライベートモジュールメソッド


108

2つの質問があります

ベストプラクティス

  • 公開インターフェースを使用してデータ構造に対して何らかの操作を実行するアルゴリズムがあります
  • 現在、多数の静的メソッドを持つモジュールであり、1つのパブリックインターフェイスメソッドを除いてすべてプライベートです。
  • すべてのメソッド間で共有する必要があるインスタンス変数が1つあります。

これらは私が見ることができるオプションです、どれが最高ですか?:

  • モジュールの静的(ルビーで「モジュール」)を持つメソッド
  • 静的メソッドを持つクラス
  • データ構造に含めるMixinモジュール
  • そのデータ構造(非常に小さい)を変更するアルゴリズムの部分をリファクタリングし、アルゴリズムモジュールの静的メソッドを呼び出すミックスインを作成します。

テクニカルパーツ

プライベート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
Ruby newbはこちら。この例では、Translatorクラスはモジュールのパブリックインターフェイスの一部として公開されていますか?「実行」メソッドのアクセスをGTranslateに制限できますか?
rshepherd 2011

2
@rshepherdこれperformはここでプライベートであることになっているメソッドではありません。プライベートメソッドはTranslatorクラスのプライベートメソッドです(@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 '16

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| [...] endはdefの代わりに使用しました
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_class_methodによって所有されてModuleいませんClass。このコードはちなみに機能し、を使用する代わりになりprivate_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年後のこのコードの大幅な更新は、プライベートメソッドを宣言するためのより明確な方法を示しています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 << selfself.


ラムダはメソッドとまったく同じではありません。ラムダは型ですProcが、メソッドは型Methodです。
Michael Dorst 2013

1
グローバル変数は悪い
achempion 2013年

@achempion、どこで見ますか?
Nakilon 2013年

@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として使用することは不可能のようです。それを機能させる方法があるかどうか知っていますか?
Shiyason 2017年

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.