Использование сопоставителей shoulda для проверки «allow_nil» в сочетании с «уникальностью» и «областью действия»

Я создаю модель ActiveRecord для таблицы, в которой хранятся пары ключ/значение.

Пример -

|------------------------------|
| KEY      | VALUE             |
|----------|-------------------|
| LOCATION | San Francisco, CA |
| TITLE    | Manager           |
| LOCATION | New York City, NY |
|------------------------------|

Вот модель -

class CompanyEnum < ActiveRecord::Base
  KEYS = [:title, :department, :location]
  KEYS_ENUM = KEYS.map(&:to_s).map(&:upcase)

  # `key` column must be one of the above - LOCATION, DEPARTMENT, or TITLE
  validates(:key, inclusion: KEYS_ENUM, allow_nil: false)

  # `value` can be anything, but must be unique for a given key (ignoring case) 
  validates(
    :value,
    uniqueness: { scope: :key, case_sensitive: false },
    allow_nil: false
  )
end

Я использую сопоставители shoulda для написания спецификаций для этих проверок. Итак, в моем файле спецификаций у меня есть следующие две спецификации:

describe "validations" do
  it { should_not allow_value(nil).for(:key) }
  it { should_not allow_value(nil).for(:value) }
end

Моя проблема в том, что первая проверка для :key проходит, но вторая проверка для :value не проходит. В соответствии с определением модели оба используют одну и ту же опцию allow_nil: false.

1) CompanyEnum validations value should not allow value to be set to nil
   Failure/Error: it { should_not allow_value(nil).for(:value) }
     Expected errors when value is set to nil,
     got no errors
   # ./spec/models/company_enum_spec.rb:13:in `block (4 levels) in <top (required)>'
   # ./spec/support/analytics.rb:4:in `block (2 levels) in <top (required)>'

Есть ли какая-то логическая проблема с использованием allow_nil: false с uniqueness: и опциями scope:? Или это связано с тем, что я называю фактические столбцы :key и :value (поскольку они кажутся достаточно общими, чтобы конфликтовать с некоторыми другими методами)?

Спасибо!


person user2490003    schedule 02.12.2015    source источник
comment
Почему бы просто не использовать валидатор присутствия? Я думаю, что это беспорядок из-за пункта области видимости. Также просто упоминание о производительности, но вы можете заморозить каждую строку и массив для перечисления ключей, иначе они создаются с каждым экземпляром модели.   -  person CWitty    schedule 02.12.2015


Ответы (1)


allow_nil: false при проверке уникальности не означает, что nil не является допустимым значением. Это немного вводит в заблуждение.

Как вы, возможно, уже знаете, по умолчанию проверка уникальности работает следующим образом (судя по коду, который вы используете):

  • Если есть две записи с одинаковыми значениями key и value, то вторая запись должна быть недействительной по сравнению с первой (при условии, что первая существует в базе данных).
  • Если есть две записи с разными значениями key и value, то обе записи должны быть действительными.

Так что же делает allow_nil? Давайте посмотрим, что говорят документы:

:allow_nil — если установлено значение true, эта проверка пропускается, если атрибут равен nil (по умолчанию — false).

Таким образом, если значение true, allow_nil позволяет сосуществовать двум записям с nil значениями для проверяемого атрибута (в данном случае value).

Но вы указали allow_nil: false, а это значит, что этот вариант все равно не применяется.

Таким образом, ваше первое использование allow_value завершилось неудачно, потому что nil не является допустимым значением для key (проверка включения не удалась). Однако второе использование проходит успешно, поскольку nil является допустимым значением для value (при условии, что нет существующих записей, которые также имеют value из nil).

Думаю, я хочу сказать, что если вы действительно хотите проверить свои проверки, вы должны использовать сопоставители, которые непосредственно соответствуют этим проверкам:

it do
  should validate_inclusion_of(:key).
    in_array(["TITLE", "DEPARTMENT", "LOCATION"]).
    allow_nil
end

it do
  should validate_uniqueness_of(:value).
    scoped_to(:key).
    case_insensitive
end
person Elliot Winkler    schedule 02.12.2015
comment
Это имеет большой смысл, спасибо! Я предполагаю, что цель allow_nil состоит не в том, чтобы описать значение поля, а в том, чтобы отметить погоду, сама проверка должна выполняться для значений nil. Как вы упомянули, это немного вводит в заблуждение, но спасибо за разъяснение. - person user2490003; 02.12.2015