5

Rails 3.1.6アプリに、値が存在しない場合でも属性に値を割り当てるカスタムアクセサーメソッドがあります.my_attr属性はシリアル化されたハッシュであり、空白の値が指定されていない限り、指定された値とマージする必要があります、この場合、現在の値は空白の値に設定されます。(値が正しいことを確認するためのチェックが追加されていますが、質問の一部ではないため、簡潔にするために削除されています。)私のセッターは次のように定義されています。

def my_attr=(new_val)
  cur_val = read_attribute(:my_attr)  #store current value

  #make sure we are working with a hash, and reset value if a blank value is given
  write_attribute(:my_attr, {}) if (new_val.nil? || new_val.blank? || cur_val.blank?)

  #merge value with new 
  if cur_val.blank?
    write_attribute(:my_attr, new_val)
  else
    write_attribute(:my_attr,cur_val.deep_merge(new_val))
  end
  read_attribute(:my_attr)
end

このコードはそのままでうまく機能しますが、self.write_attribute()を使用する場合は機能しません。次に、次のエラーが発生します。

NoMethodError:
       private method `write_attribute' called for #<MyModel:0x00000004f10528>

したがって、私の質問は次のとおりです。インスタンスでwrite_attributeを使用できるようにする方が論理的であるように思われるのに、インスタンスではなくクラスでのみ使用できるのはなぜですか。RubyまたはRails(またはその両方)での自己の基本的な知識に欠けているものはありますか?

4

1 に答える 1

11

インスタンスメソッドの内部には、selfそのインスタンスがあります。ただし、明示的なレシーバーを使用してメソッドを呼び出すと、rubyの可視性制御が開始され、プライベートメソッドの呼び出しが禁止されます。

class Foo
  def implicit
    self # => #<Foo:0x007fc019091060>
    private_method
  end

  def explicit
    self # => #<Foo:0x007fc019091060>
    self.private_method
  end

  private
  def private_method
    "bar"
  end
end

f = Foo.new
f.implicit # => "bar"
f.explicit # => 
# ~> -:9:in `explicit': private method `private_method' called for #<Foo:0x007fc019091060> (NoMethodError)
# ~>    from -:25:in `<main>'

プライベートメソッドを呼び出す場合は、暗黙のレシーバーまたはを使用しますsend

self.send :private_method

アップデート

ルビーのメタプログラミングの本からの抜粋。

プライベートの本当の意味

自己について知ったので、Rubyのプライベートキーワードに新しい光を当てることができます。プライベートメソッドは、単一の単純なルールによって管理されます。明示的なレシーバーを使用してプライベートメソッドを呼び出すことはできません。つまり、プライベートメソッドを呼び出すたびに、暗黙のレシーバーである自分自身に存在する必要があります。コーナーケースを見てみましょう:

class C
  def public_method
    self.private_method 
  end
  private
  def private_method; end
end
C.new.public_method

⇒ NoMethodError: private method ‘private_method' called [...]

selfキーワードを削除することで、このコードを機能させることができます。

この不自然な例は、プライベートメソッドが2つのルールが連携して機能することを示しています。まず、自分以外のオブジェクトでメソッドを呼び出すための明示的なレシーバーが必要です。次に、プライベートメソッドは暗黙のレシーバーでのみ呼び出すことができます。これらの2つのルールを組み合わせると、自分自身でのみプライベートメソッドを呼び出すことができることがわかります。これを「プライベートルール」と呼ぶことができます。</p>

特に、プライベートの動作が大きく異なるJavaまたはC#を使用している場合は、Rubyのプライベートメソッドが複雑になる可能性があります。疑問がある場合は、プライベートルールに戻るだけで、すべてが理にかなっています。2つのオブジェクトが同じクラスを共有している場合、オブジェクトxはオブジェクトyのプライベートメソッドを呼び出すことができますか?答えはノーです。どのクラスに属していても、別のオブジェクトのメソッドを呼び出すには明示的なレシーバーが必要だからです。スーパークラスから継承したプライベートメソッドを呼び出すことはできますか?答えは「はい」です。継承されたメソッドを自分で呼び出すために明示的なレシーバーは必要ないからです。

于 2012-07-12T21:57:41.103 に答える