web-dev-qa-db-ja.com

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

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

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
171
Backo

============================================= ===================================更新: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=を呼び出しているときに無限ループを引き起こすため、失敗します。

276
rubyprince

superキーワードを使用します。

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

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

def attribute_name
  super.some_custom_decode
end
42
Robert Kajic

In Rails 4

テーブルにage属性があるとしましょう

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メソッドを使用して、コントローラーレベルで属性をホワイトリストに登録する必要があります。

16

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

has_many :specialties

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

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

3
Robin Daugherty

attr_writerを使用してセッターattr_writer:attribute_nameを上書きする

  def attribute_name=(value)
    # manipulate value
    # then send result to the default setter
    super(result)
  end
0
bananaappletw