Rails 3.1: assign_attributesメソッド

2011/05/14

ゴールデンウィーク中の5月5日にRails 3.1 beta1がリリースされました。

派手な点としてはJQuery, CoffeeScript, Sassなどをデフォルトして採用したことが挙げられますが、私が注目しているのはActive Recordに加えられたassign_attributesメソッドです。

その基本的な役割は、昔からあるattributes=メソッドと同じです。たとえば、Userモデルとusersコントローラがあって、users#updateアクションが次のように書かれているとします。

def update
  @user = User.find(params[:id])
  @user.attributes = params[:user]
  if @user.save
    redirect_to @user
  else
    render :edit
  end
end

Rails 3.1では、3行目を次のように書くことができます。

  @user.assign_attributes(params[:user])

私としては、類似した機能を持つupdate_attributesメソッドと書き方が共通になった点をまず喜びたいのですが、ポイントはそこではありません。

今、Userモデルにnameおよびis_adminという2つのフィールドがあるとします。そして、is_adminフィールドを守るために、app/models/user.rbのソースコードは次のようになっているとします。

class User < ActiveRecord::Base
  attr_accessible :name
end

すると、仮にparams[:user]の中身が { :name => "tsutomu", :is_admin => true } であったとしても、

  @user.attributes = params[:user]

と書くだけでは、is_adminフィールドは書き換わりません。明示的に次のように書く必要があります。

  @user.attributes = params[:user]
  @user.is_admin = true

これは、悪意を持ったユーザーが自分のアカウントのis_adminフィールドをtrueにする、という事態を避けるための予防措置です。たとえ編集フォームにis_adminフィールドを切り替えるチェックボックスがなくても、ユーザーは簡単なスクリプトを作ってis_adminフィールドを含むパラメータをPOSTすることができるからです。

さて、usersコントローラとは別にadmin/usersコントローラがあるとします。こちらのコントローラにアクセスできるのが管理者だけだとすれば、今述べたような予防措置は不要です。

ここで登場するのが、assign_attributesメソッドです。

まず、app/models/user.rbを次のように書き換えます。

class User < ActiveRecord::Base
  attr_accessible :name
  attr_accessible :name, :is_admin, :as => :admin
end

attr_accessibleメソッドのオプション:asは、Rails 3.1で登場したものです。で、admin/users#updateアクションはこうなります。

def update
  @user = User.find(params[:id])
  @user.assign_attributes(params[:user], :as => :admin)
  if @user.save
    redirect_to @user
  else
    render :edit
  end
end

いかがでしょうか? 感動するかどうかは人それぞれでしょうが、少なくとも私は心の中で「ブラーヴォ」とつぶやいてしまいました。こういう類の渋い改善点をコンスタントに追加してくるところが、Railsコミュニティのすごいところです。