memorandums

日々のメモです。

Rails4のvalidates(confirmation)ではまった理由をメモしておきます

以下の書籍でログイン機能を作ってたとき、2時間くらいハマりました。。。あとで「何だっけ?」となったときに思い出せるようにメモしておきます。

ちなみに、このやり方は古いようで、最近は暗号化も含めて簡単に記述(has_secure_password)できるようなので、Rails勉強中の方にとってはノイズになりますので、この記事はくれぐれも無視してください。

改訂3版基礎 Ruby on Rails (KS IMPRESS KISO SERIES)

改訂3版基礎 Ruby on Rails (KS IMPRESS KISO SERIES)

ログインするユーザを登録するときに、パスワードの入力誤りを防止するために2回入力させることがよくありますが、それを実装します。

Userモデル(name:string、hashed_password:string)に基本情報をいれます。テーブルにはハッシュ後のパスワードをいれるため、モデルにない項目を入力フォームで扱う必要があります。これはこれで勉強になります。

モデルに項目を追加するため、普通のRuby同様に、attr_accessorで定義します。あと、passwordのセッターを定義しておいて、passwordに代入された文字列を横取りして、テーブルに入れるパスワードを暗号化するメソッドを追加します。いたって普通です。

attr_accessor :password, :password_confirmation

  def password=(val)
    if val.present?
      self.hashed_password = BCrypt::Password.create(val)
    end
    @password = val
  end

さらに、Userモデルに以下のvalidatesを追加します。

  validates :name, presence: true, uniqueness: true
  validates :password, presence: true, length: {minimum: 4}, confirmation: true

さらに、コントローラのcreateアクションを以下のようにしました。

  def create
    @user = User.new(name: params[:user][:name], password: params[:user][:password])
    if @user.save
      redirect_to users_path
    else

で、この状態でユーザ登録すると、なぜかパスワードが確認用のパスと一致していなくても通過しちゃうんですね。。。ここから調べること2時間。。。もったいない。ほんと。

原因はコントローラの書き方でした。

上記の書籍ではストロングパラメタを使っているので私のようなことは起きません。私は学習を用意にするため、記述は面倒ですが、ストロングパラメタを使わず、1つずつビューから渡す記述をしていたんですね。。。

本来であれば、以下のように書くべきなんですね。めちゃくちゃ長くなってどうにもこうにもッテ感じがしますが、バリデーションする元ネタであるpassword_confirmationをUserモデルにセットしていなかった。。。ここがミスでした。

  def create
    @user = User.new(name: params[:user][:name], password: params[:user][:password], password_confirmation: params[:user][:password_confirmation])
    if @user.save
      redirect_to users_path
    else

それでも、validatesを記述しているんだから不一致のチェックくらいしてくれたら。。。と思うのですが、「password_confirmationがnilの場合は同一性のチェックは行わない」という仕様なんだそうです。確かにドキュメントに書いてあったような気がします。読み飛ばしてしまいましたが。。。そのため気づくのが遅れてしまったわけです。

password_confirmationが空かどうかもバリデーションすべきってことです。それで気づけたのかもしれません。Userモデルにその記述を追加すると以下のようになります。

  validates :name, presence: true, uniqueness: true
  validates :password, presence: true, length: {minimum: 4}, confirmation: true
  validates :password_confirmation, presence: true

2時間返して。。。