Rails4 attributeの代入をフックする


Goal

  • new, create, updateなどでattributesハッシュを渡したときに引数attributesに対して毎回特定のフィルタ処理をおこないたい
  • 通常はモデルの仕様に合わせて呼び出し側でattributesを準備するべきだと思うが、準備すべき引数attributesがモデルのビジネスロジックに依存する場合、利便性のためにモデル側にパラメータ変換ロジックを持たせておきたい

Code

assign_attributesをオーバーライドすればよい. new, create, updateで渡された引数attributesはassign_attributesメソッドを通してモデルにセットされる.

user.rb
class User < ActiveRecord::Base
  belongs_to :game

  def self.gender_role( role_name, gender_name )
    gender_roles = {
      acting: {
        male: :actor,
        female: :actress,
      },
      waiting: {
        male: :waiter,
        female: :waitress,
      }
    }
    return nil unless gender_roles[role_name]
    gender_roles[role_name][gender_name]
  end

  def assign_attributes(attributes)
    gender_local = gender
    gender_local = attributes[:gender] if attributes[:gender]
    attributes[:role] = User.gender_role(attributes[:role], gender_local)
    super attributes
  end
end
user = User.new( role: :acting, gender: :male )
user.role # :actor

user = User.new( role: :acting, gender: :female )
user.role # : actress

user = User.create( gender: :male )
user.update( role: :waiting )
user.role # :waiter

Caution

引数つきでnewしたときは毎回実行されるので、重い処理を実行するときは注意.
個別のattributeに直接代入するときは当然assign_attributesはよばれない.

Environment

$ bundle exec rails -v
Rails 4.1.9