Ruby on Railsで、controllerとmodel両方のエラーをうまく扱う方法を教えてください。

たとえば以下のような役割分担でパスワード変更画面を作るとします。

+ パスワードの書式はmodel側で、validates_xxx_ofを使ってチェックする
+ 旧パスワードが登録されているものと一致しているかはcontrollerでチェックする

この場合、modelとcontrollerのエラーをひとつにまとめて見せたいとしたら、どうやるのがエレガントなのでしょうか。

回答の条件
  • 1人5回まで
  • 登録:
  • 終了:2006/05/20 06:01:22
※ 有料アンケート・ポイント付き質問機能は2023年2月28日に終了しました。

ベストアンサー

id:masuidrive No.4

回答回数10ベストアンサー獲得回数2

ポイント486pt

私のコードのままでもControllerの

@user.current_password_check_required = true

の代わりに

@user.current_password ||= ''

と書き、current_passwordにnilが入っていた場合に、''を代入しチェックを有効にする事ができます。どうでしょ?

id:pekeq

なるほど。これはかっこいいですね。

頂いた内容で、controller側にチェックロジックを持たなくてもよくなったので、質問を終了します。

masuidriveさん、おつきあい頂いてありがとうございました。

2006/05/20 05:56:25

その他の回答3件)

id:masuidrive No.1

回答回数10ベストアンサー獲得回数2

旧パスワードのチェックをModelでやらせるのはどうでしょうか?

コードが出てないので、ちょっと具体的には分かりませんが、その辺だけでも切り出せませんか?

id:pekeq

 使っているModelは、login_generatorのapp/models/user.rbそのままです。(http://d.hatena.ne.jp/pekeq/20060518/p1にソースを貼っておきます)

 このModelには、User#change_passwordというメソッドがあるにはあるのですが、このメソッドを使ってパスワードを変更してしまうと、validates_xxx_ofに書いてあるチェックが効かないのです。

 じゃあ、modelにcurrent_passwordとかいうアクセサを定義して、validate_on_updateでcurrent_passwordと現在のパスワードが一致しているかをチェックすればいいかと思ったんですが、そうすると他の要素(例えばログイン名)を変更する際にもcurrent_passwordを定義してやらねばならないので、これはこれで困りものです。

 じゃあやっぱりcontrollerで、変更前のパスワードをチェックして、@user.saveでvalidatorのチェックをするしかないかなー、というのでこの質問になりました。

* 追記

http://d.hatena.ne.jp/pekeq/20060518/p1に、modelとcontrollerのエラーをひとつにまとめて見せたいコードの例を載せました。

2006/05/19 00:05:16
id:masuidrive No.2

回答回数10ベストアンサー獲得回数2

Userモデルにこれを追加するのはどうでしょう?

パスワードを変更したい場合はchange_passwordを使わず、普通にセットするだけで大丈夫です。

また、current_passwordに何かセットした場合のみ、変更前のパスワードと一致するか、チェックする事ができます。

class User

attr_accessor :current_password

after_validate :crypt_password

〜〜

def crypt_password

old_date = User.find(self.id)

return if old_date && self.password == old_date.password

update_attribute "password", self.class.sha1(self.password)

end

def validate

return true if @current_password.nil?

errors.add(:current_password, "を正しく入力してください") unless User.find(self.id).password == @current_password

end

end

id:pekeq

ありがとうございます。

でもこれだと、current_passwordを空欄にしてsubmitすると、パスワード変更ができてしまいますよね。そうすると、やっぱりcontroller側でのチェックをしないといけなくなってしまうのではないでしょうか。

要件がはっきりしてないのがいけないですね。

- 入力項目のチェックが全部model側でできるのがベスト

- パスワード変更時は、「current_passwordが入力されている」「current_passwordが変更前パスワードと一致している」「password, password_confirmationが一致している」「passwordがvalidaterで記述した書式と一致している」ことをmodel側で確認したい

- 一方で、Userモデルのその他の要素(loginなど)を変更する場合は、current_passwordの入力チェック・パスワードチェックは不要

- パスワード変更も、その他要素の変更も、User#saveで行えればベスト

…と、ここまで書いて思いましたが、Model側でやるなら、こういう実装にすればいいかもしれないですね。

1. current_password_check_requiredとかいうフラグ変数をattr_accessorで用意しておく

2. controller側からフラグをセット

3. validateの中で、フラグが立っていたらcurrent_passwordの入力チェック、認証チェックをする

(あんまり格好良くないけど)

* 追記

私なりに書いてみたコードをhttp://d.hatena.ne.jp/pekeq/20060519/p1に載せてみました。

2006/05/19 15:36:52
id:masuidrive No.3

回答回数10ベストアンサー獲得回数2

先ほどのコードにミスありました。

after_validate -> after_validation

でお願いします。

id:masuidrive No.4

回答回数10ベストアンサー獲得回数2ここでベストアンサー

ポイント486pt

私のコードのままでもControllerの

@user.current_password_check_required = true

の代わりに

@user.current_password ||= ''

と書き、current_passwordにnilが入っていた場合に、''を代入しチェックを有効にする事ができます。どうでしょ?

id:pekeq

なるほど。これはかっこいいですね。

頂いた内容で、controller側にチェックロジックを持たなくてもよくなったので、質問を終了します。

masuidriveさん、おつきあい頂いてありがとうございました。

2006/05/20 05:56:25

コメントはまだありません

この質問への反応(ブックマークコメント)

「あの人に答えてほしい」「この質問はあの人が答えられそう」というときに、回答リクエストを送ってみてましょう。

これ以上回答リクエストを送信することはできません。制限について

回答リクエストを送信したユーザーはいません