私の質問は、属性を更新する前にユーザーからのパスワードを確認することにより、ユーザーの「プロファイル」を更新するためのビューとコントローラーの設定に関するものです。おそらく何百万回も前に見たことがあるように、ユーザーは /users/:id/edit に移動し、テキスト フィールドに新しいメール アドレスを入力し、パスワード フィールドに現在のパスワードを入力して、送信ボタンをクリックして最終的にユーザーの電子メールを更新します。入力されたパスワードが正しくない場合、編集テンプレートが再度レンダリングされます。それ以外の場合、ユーザー レコードは新しい電子メールで更新され、:show (またはアプリに適したもの) にリダイレクトされます。update アクションでは update_attributes メソッドを使い続けるのが理にかなっていると思います。ただし、現在のパスワードの値は、私たちをうんざりさせてしまいます。
私が本当に求めているのは、私のアプローチに何か問題があるかどうかです。attr_accessible を怒らせることなく、params[:user] で update_attributes を呼び出すために、form_for ブロック内の :current_password フィールドの password_field_tag への呼び出しを含めることになりました。しかし、その後、すでにこれを行っている Web サイト (hulu や destroyallsoftware など) でいくつかのフォームを調べたところ、ユーザー ハッシュで :current_password 値を受け入れているようです (レールで構築されていると仮定して)。Twitter の設定ページを調べると、param の別のハッシュでこれを取得しているようです (つまり、params[:user][:current_password] ではなく params[:current_password])。
form_for 内で password_field_tag を使用するのは間違っていますか? これらの他のサイトは実際にこれをどのように行っているのでしょうか? 私が考えることができる唯一のことは、params ハッシュから :current_password を削除するか、各属性を個別に割り当てることです。
これが私が基本的に最終的に得たものです:
# /app/models/user.rb
class User < Activerecord::Base
attr_accessible :email, # ...
# ...
end
# /app/views/users/edit.html.erb
<%= form_for @user do |f| %>
# this is stored in params[:user][:email]
<%= f.label :email, 'Your new email' %>
<%= f.text_field :email, type: :email %>
# this is stored in params[:current_password]
<%= label_tag :current_password, 'Re-enter your password to update your email' %>
<%= password_field_tag :current_password %>
<%= f.submit 'Save changes' %>
<% end %>
# /app/controllers/users_controller.rb
# ...
def update
@user = User.find(params[:id])
if @user.authenticate(params[:current_password])
if @user.update_attributes(params[:user])
sign_in @user
flash[:success] = 'Sweet!'
redirect_to @user
else
render :edit
end
else
flash.now[:error] = 'Incorrect password'
render :edit
end
そうでなければ、これは私が考えたもう1つの方法です:
# /app/views/users/edit.html.erb
<%= form_for @user do |f| %>
# this is stored in params[:user][:email]
<%= f.label :email, 'Your new email' %>
<%= f.text_field :email, type: :email %>
# this is stored in params[:user][:current_password]
<%= f.label :current_password, 'Re-enter your password to update your email' %>
<%= f.password_field :current_password %>
<%= f.submit 'Save changes' %>
<% end %>
# /app/controllers/users_controller.rb
# ...
def update
@user = User.find(params[:id])
if @user.authenticate(params[:user][:current_password])
params[:user].delete(:current_password) # <-- this makes me feel a bit uneasy
if @user.update_attributes(params[:user])
sign_in @user
flash[:success] = 'Sweet!'
redirect_to @user
else
render :edit
end
else
flash.now[:error] = 'Incorrect password'
render :edit
end
または、コントローラーでこれを行う必要がありますか?:
def update
@user = User.find(params[:id])
if @user.authenticate(params[:user][:current_password])
@user.email = params[:user][:email]
if @user.save
# ...
アドバイスをいただければ幸いです。
PS - さらに、その更新アクションをリファクタリングするにはどうすればよいですか? :current_password で認証し、#update に #update_attributes の部分だけを保持する before_filter を試してみましたが、少し面倒です。この投稿は十分に長くなってきているので、来週までに理解できない場合は別の質問として投稿するかもしれません。