Ruby on Railsでセッターメソッドをオーバーライドする正しい方法は何ですか?


184

Ruby on Rails 3.2.2を使用していますが、次の方法がmyクラス属性のセッターメソッドをオーバーライドする「適切な」/「正しい」/「確実な」方法であるかどうかを知りたいです。

attr_accessible :attribute_name

def attribute_name=(value)
  ... # Some custom operation.

  self[:attribute_name] = value
end

上記のコードは期待どおりに動作するようです。しかし、上記のコードを使用することで、将来的に問題が発生するかどうか、少なくとも、Ruby on Railsで「予想されるはずの問題」/「発生する可能性のある問題」について知りたいと思います。それがセッターメソッドをオーバーライドする正しい方法でない場合、正しい方法は何ですか?


:コードを使用する場合

attr_accessible :attribute_name

def attribute_name=(value)
  ... # Some custom operation.

  self.attribute_name = value
end

次のエラーが発生します。

SystemStackError (stack level too deep):
  actionpack (3.2.2) lib/action_dispatch/middleware/reloader.rb:70

4
私は「 "適切" / "正しい" / "確か"」という用語を適用するのが好きです。あなたがそれを3つの方法で与えるとき、それは本当に誤解がないことを確実にします。よくやった!
ジェイ

5
@ジェイ-「ファインイタリアン」-)
Backo

2
明確にするために、「スタックレベルが深すぎる」​​とは、その再帰呼び出しがその呼び出し自体であるという事実を指します。
ニッピサウルス2014年

回答:


295

================================================== ========================= 更新:2017年7月19日

現在、Railsのドキュメントは次のsuperように使用することも推奨しています。

class Model < ActiveRecord::Base

  def attribute_name=(value)
    # custom actions
    ###
    super(value)
  end

end

================================================== =========================

元の回答

モデルを介してアクセスするときにテーブルの列のセッターメソッドをオーバーライドする場合は、これがその方法です。

class Model < ActiveRecord::Base
  attr_accessible :attribute_name

  def attribute_name=(value)
    # custom actions
    ###
    write_attribute(:attribute_name, value)
    # this is same as self[:attribute_name] = value
  end

end

Railsのドキュメントの「デフォルトアクセサーのオーバーライド」を参照してください。

したがって、最初の方法は、Ruby on Railsのモデルで列セッターをオーバーライドする正しい方法です。これらのアクセサーは、モデルの属性としてテーブルの列にアクセスするためにRailsによってすでに提供されています。これは、ActiveRecord ORMマッピングと呼ばれるものです。

またattr_accessible、モデルの上部にあるはアクセサとは関係がないことに注意してください。それは完全に異なる機能を持っています(この質問を見てください)

しかし、純粋なRubyでは、クラスのアクセサを定義していて、セッターをオーバーライドしたい場合は、次のようにインスタンス変数を使用する必要があります。

class Person
  attr_accessor :name
end

class NewPerson < Person
  def name=(value)
    # do something
    @name = value
  end
end

これは、何attr_accessorができるかを理解すると理解しやすくなります。コードattr_accessor :nameは、これらの2つのメソッド(getterおよびsetter)と同等です。

def name # getter
  @name
end

def name=(value) #  setter
  @name = value
end

また、2番目のメソッドは、attribute_name=そのメソッド内で同じメソッドを呼び出すときに無限ループが発生するため、失敗します。


9
Rails 4 attr_accessibleの場合は、もう存在しないのでスキップし、機能するはずです
zigomir

11
電話してみませんsuperか?
Nathan Lilienthal 14年

1
アクセサーとライターは動的に作成されるため、機能しsuperない可能性があるという印象を受けました。しかし、そうではないようです。確認したところ、うまくいきました。また、この質問は同じように尋ねます
rubyprince

4
との大きな落とし穴がありwrite_attributeます。変換はスキップされます。write_attributeが日付を含むタイムゾーン変換をスキップすることに注意してください。これはほとんどの場合望ましくありません。
ティムスコット

2
superも機能しますが、使用したくない理由がいくつかあります。たとえば、mongoid gemには、getterメソッドをスーパーにすると配列にプッシュできないというバグがあります。メモリ内でアレイを管理する方法があるため、これはバグです。また、@ nameは、上書きするメソッドを呼び出すのではなく、値セットも返します。ただし、上記のソリューションではどちらも問題なく機能します。
newdark-it 2015

44

superキーワードを使用:

def attribute_name=(value)
  super(value.some_custom_encode)
end

逆に、リーダーをオーバーライドするには:

def attribute_name
  super.some_custom_decode
end

1
メソッド呼び出しを同じ名前に制限し続けるため、受け入れられたIMOよりも良い答えです。これにより、継承されたオーバーライドされた動作がattribute_name =に保持されます
Andrew Schwartz

この変更により、Rails 4.2ではgetterメソッドのオーバーライドが危険になりました。github.com / rails / rails / commit / 以前は、フォームヘルパーはフィールドのuntypecast値を呼び出し、カスタムゲッターを呼び出していませんでした。これでメソッドが呼び出されるため、値をオーバーライドする方法によっては、フォームに混乱する結果が生成されます。
Brendon Muir

16

レール4

あなたのテーブルに年齢属性があるとしましょう

def age=(dob)   
    now = Time.now.utc.to_date
    age = now.year - dob.year - ((now.month > dob.month || (now.month == dob.month && now.day >= dob.day)) ? 0 : 1)
    super(age) #must add this otherwise you need to add this thing and place the value which you want to save. 
  end

注:rails 4の新しいコーナーでは、モデルでattr_accessibleを指定する必要はありません。代わりに、permitメソッドを使用して、コントローラレベルで属性をホワイトリストに登録する必要があります。


3

(少なくともActiveRecord関係コレクションの場合)次のパターンが機能することがわかりました。

has_many :specialties

def specialty_ids=(values)
  super values.uniq.first(3)
end

(これにより、渡された配列の最初の3つの重複しないエントリが取得されます。)


0

attr_writersetter attr_writer:attribute_nameを上書きするために使用

  def attribute_name=(value)
    # manipulate value
    # then send result to the default setter
    super(result)
  end
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.