Rails 中的 patch 方法如何只更新一些属性而其他属性为空?

How could patch method in Rails update only some attributes while the other attributes are blank?

我在阅读 Ruby On Rails 教程时有这些问题 here

用户 class 的验证是:

class User < ActiveRecord::Base
   before_save { self.email = email.downcase }
   validates :name, presence: true, length: { maximum: 50 }
   VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
   validates :email, presence: true, length: { maximum: 255 }
              format: { with: VALID_EMAIL_REGEX },
              uniqueness: { case_sensitive: false }
   has_secure_password
   validates :password, length: { minimum: 6 }, allow_blank: true
   .
   .
   .
end

在测试中,像这样将更新的用户信息修补到用户的路由中:

def setup
  @user = users(:michael)
end
.
.
.
test "successful edit" do
  get edit_user_path(@user)
  assert_template 'users/edit'
  name  = "Foo Bar"
  email = "foo@bar.com"
  patch user_path(@user), user: { name:  name,
                                email: email,
                                password:              "",
                                password_confirmation: "" }
  assert_not flash.empty?
  assert_redirected_to @user
  @user.reload
  assert_equal name,  @user.name
  email, @user.email
end

测试会通过,只有用户名和电子邮件会更新,密码不会更改。

如果密码验证不包含 "allow_blank:true",则此测试将失败。

所以我不明白:当测试通过时,密码可以为空,为什么不把密码改成空? Rails 怎么知道我只想更新一些属性?

has_secure_password 向您的模型添加了一个 password= setter method 方法,该方法在设置密码时会丢弃 empty? 输入。

irb(main):012:0> "".empty?
=> true

这可以防止用户选择空白密码。如果你不想相信我的话,你可以轻松地测试一下:

test "does not change password to empty string" do
  patch user_path(@user), user: { name:  name,
                                  email: email,
                                  password:              "",
                                  password_confirmation: "" }
  @user.reload
  assert_false @user.authenticate("")
end

然而,您的验证所做的是,如果用户设置密码,密码必须超过 6 个字符:

test "does not allow a password less than 6 characters" do
  patch user_path(@user), user: { name:  name,
                                  email: email,
                                  password:              "abc",
                                  password_confirmation: "abc" }
  assert assigns(:user).errors.key?(:password)
end

(PS。这是在模型测试中比在控制器测试中测试得更好的东西)