为什么我必须显式消除空白才能让 shoulda matcher `inclusion:` 验证通过?

Why do I have to explicitly nilify blanks for the shoulda matcher `inclusion:` validation to pass?

我的数据库中有一个包含时区的字符串列。有效值包括 nil 或 ActiveSupport 识别为时区的任何值

我使用 shoulda-matchers 为我的模型验证编写规范:

# app/models/my_model.rb
class MyModel < ApplicationRecord
  validates :timezone, inclusion: ActiveSupport::TimeZone::MAPPING.keys, allow_nil: true
end

# spec/models/my_model_spec.rb
describe "timezone" do
  it do 
    should validate_inclusion_of(:timezone).
      in_array(ActiveSupport::TimeZone::MAPPING.keys).
      allow_blank
    end
end

它抛出了一个错误:

Failure/Error: it { should validate_inclusion_of(:timezone).in_array(ActiveSupport::TimeZone::MAPPING.keys).allow_blank }

     MyModel did not properly validate that
     :timezone is either ‹"International Date Line West"›, ‹"Midway Island"›,
     ‹"American Samoa"›, ‹"Hawaii"›, ‹"Alaska"›, ‹"Pacific Time (US &
     .....
     .....
     .....
     ‹"Auckland"›, ‹"Wellington"›, ‹"Nuku'alofa"›, ‹"Tokelau Is."›, ‹"Chatham
     Is."›, or ‹"Samoa"›, but only if it is not blank.
       After setting :timezone to ‹""›, the matcher expected the
       MyModel to be valid, but it was invalid
       instead, producing these validation errors:

       * timezone: ["is not included in the list"]

Shoulda matchers 将列设置为 "" 并期望验证应该通过。但为什么会这样呢? nil 是严格允许的,但不应该是空字符串值,对吧?

有没有我错过的更合适的设置方法?

为了解决这个问题,我使用了 before_validation 块。 (而且我知道 nilify_blanks gem 做同样的事情)。但是我不得不把它包括在内感觉很奇怪

before_validation do
  self[:timezone] = nil if self[:timezone].blank?
end

.blank? 是一种 ActiveSupport 方法,对于 nilfalse 以及更重要的 ""(空字符串),returns 为真。

这就是 allow_blank 使用空字符串进行测试的原因。请改用 allow_nil

# spec/models/my_model_spec.rb
describe "timezone" do
  it do 
    should validate_inclusion_of(:timezone).
      in_array(ActiveSupport::TimeZone::MAPPING.keys).
      allow_nil
  end
end