Railsチュートリアル(第6版)の10.1.4 -TDDで編集を成功させる でつまり、
午前中いっぱいかけて解消させた疑問の備忘録です。
私以外の人はこんなところでつまらないかもしれないですが・・
つまったところと疑問点
つまったところ:パスワードが空のままでも更新できるようにする
「ユーザー情報を編集する際、いちいちパスワードを入力するのは不便なので
パスワードを変えないときは、入力しなくてもユーザー情報を更新できるようにする。」
という内容。
具体的には、現状
class User < ApplicationRecord
  has_secure_password
  validates :password, presence: true, length: { minimum: 6 }
となっていて、nilや空文字、6文字未満のパスワードは受け入れられないところを
class User < ApplicationRecord
  has_secure_password
  validates :password, presence: true, length: { minimum: 6 }, allow_nil: true
と、allow_nilというオプションで、パスワードが空だった時の例外処理を与える(バリデーションをスキップする)よ。
ということでした。
何が疑問なの?という方、どうかこの記事をそっと温かくスキップしてください
疑問点:そもそも、presence: true書く必要あったの??
なぜにそんな疑問が生まれてしまったのかというと、チュートリアル中の、
「allow_nilによって新規登録時にパスワードがなくても登録できちゃう!?と心配になるけど、
has_secure_passwordがオブジェクト生成時に存在性を検証するようになっているから、
その心配もいらないよ!」
という文。
(簡略化してます。語弊があったら申し訳ないです。。)
以下、私の疑問と疑問が生まれる原因となった勘違いです。
- 新規登録時はパスワード必須にしたい → has_secure_passwordで検証できる
- 編集時はパスワード入れなくても更新できるようにしたい → has_secure_passwordはオブジェクト生成時にのみ、存在性を検証するので、編集時はパスワード入れなくてもOK!
 ⇒ あれ、has_secure_passwordだけで完結する?presence: trueいらなくない・・?
大事なことを見落としていたわけですね。
結論:編集時、空の場合はスキップしたいが、空文字の場合はバリデーションかけねば。
改めて、下記3点を明確にしておきたいと思います。
- 
has_secure_password:DBにレコードが生成された時だけ存在性のvalidationをおこなう
- 
presence: true: 値がnilや空文字でないことを確認
- 
allow_nil: 対象の値がnilの場合にvalidationをスキップ = 値が空文字の場合はvalidationにひっかかる
つまり、presence: trueは書いておかないと、
「編集時、パスワード欄にスペースを打ちこまれちゃった場合にも更新ができてしまう」
のでしたね・・
どうやって疑問を解消したのか
「presence: trueいらなくない・・?」と思っていたので
presence: trueを消して、下記の状態でテストを回してみました。
class User < ApplicationRecord
  has_secure_password
  validates :password, length: { minimum: 6 }, allow_nil:true
すると下記統合テストはGREENに。
  test "successful edit" do
    get edit_user_path(@user)
    assert_template 'users/edit'
    name = "Foo Bar"
    email = "foo@bar.com"
    patch user_path(@user), params: { user: {name: name,
                                            email: email,
                                            password: "",
                                            password_confirmation: "" }}
    assert_not flash.empty?
    assert_redirected_to @user
    @user.reload
    assert_equal name, @user.name
    assert_equal email, @user.email
  end
やはりpresence: trueいらないのか・・?と思ったそのとき、
別のテストが私に思い出させてくれました。
  test "password should be present (nonblank)" do
    @user.password = @user.password_confirmation = " " * 6
    assert_not @user.valid?
  end
こちらがしっかりREDになっていた!
まずこのテストはユーザを新規登録せずにパスワードの値を空文字に設定しているため
has_secure_passwordの検証にはひっかかりません。
そしてpresence: trueを消してしまったがために、空文字入力がOKになり、
ユーザーが有効になってしまったのでテストがREDになっていたのでした。
ここでしっかり、" " * 6と空文字がテストされていたおかげで
自分の勘違いに気づくことができました。。
同じようにうっかりここで詰まってしまった方のご参考になれば幸いです。