Rails: проверка включения логических тестов с ошибкой

Я пытаюсь обеспечить, чтобы поле моей модели было логическим, но мои тесты продолжают терпеть неудачу.

После прочтения этого: Проверка логического значения в Rspec и Rails и Rails: как я могу проверить, что что-то является boolean? Я закончил так:

class Model < ActiveRecord::Base

  validates :my_field, :inclusion => { :in => [true, false] }

end

Я пробовал проверить это несколько разных способов (используя rspec и consa matchers), и поскольку мои тесты продолжают работать, я сейчас до самого невероятного (?) пути. Тем не менее, тесты не проходят, и я предполагаю, что есть какой-то механизм, который где-то преобразует значение.

Вот что я использую, чтобы узнать, что происходит:

# create instance without setting value ...

# these work as expected
model_instance.valid?.should be_false      # passes
model_instance.my_field = true
model_instance.valid?.should be_true       # passes
model_instance.my_field = false       
model_instance.valid?.should be_true       # passes

# works as expected
model_instance.my_field = ""
model_instance.valid?.should be_false      # passes

# these should pass but fail
model_instance.my_field = "foo"
model_instance.my_field.should == "foo"    # fails as well, my_field == false
model_instance.valid?.should be_false      # fails

model_instance.my_field = "false"
model_instance.my_field.should == "false"  # fails as well, my_field == false
model_instance.valid?.should be_false      # fails

model_instance.my_field = "123"
model_instance.valid?.should be_false      # fails

model_instance.my_field = "true"
model_instance.my_field.should == "true"   # fails as well, my_field == true
model_instance.valid?.should be_false      # fails

Что мне не хватает? Кажется, значение конвертируется несколько логично, но где и как его предотвратить? Как правильно выполнить такую ​​проверку?

Ответы

Ответ 1

Я не знаю, откуда появилась эта идея о том, что вам нужно проверить логическое поле как true/false, но недавно я видел это в нескольких несвязанных проектах, поэтому я начинаю задаваться вопросом, не является ли это который начал где-то.

Это булевское поле, оно должно быть истинным или ложным. AR заботится о деталях реализации логических значений для вас. Если вы хотите логическое поле, создайте логическое поле в базе данных. Конец истории.

Посмотрите на источник, здесь код, который преобразует значение в логическое:

# File activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb, line 147
147:         def value_to_boolean(value)
148:           if value.is_a?(String) && value.blank?
149:             nil
150:           else
151:             TRUE_VALUES.include?(value)
152:           end
153:         end

И вот значения, которые приравниваются к их логическим аналогам:

TRUE_VALUES = [true, 1, '1', 't', 'T', 'true', 'TRUE'].to_set
FALSE_VALUES = [false, 0, '0', 'f', 'F', 'false', 'FALSE'].to_set

Итак, взглянув на ваш код выше, все так, как должно быть, за исключением того, что то, что вы ожидаете, неверно. присваивая "foo" логическому значению, мы видим, что "foo" не входит в список допустимых истинных значений, поэтому логическое поле по умолчанию имеет значение false, что и будет возвращено позже. Логическое значение для поля все еще :in => [true, false], поэтому модель действительна. "ложь" и "123" не работают по той же причине. "true" является допустимым значением логического значения true, поэтому поле получает значение true и по-прежнему действует по причине, описанной выше.

Ответ 2

Я не гуру Rails, но я буду придерживаться моей шеи и скажу, что вы абсолютно не хотите проверять логические значения для :inclusion => { :in => [true, false] } - то есть, если вы не хотите допускать значения nil (NULL).

Исходя из фона программирования баз данных, я научился иметь в виду, что логическое поле может иметь три значения: true, false и NULL. При программировании в родном SQL NULL требуют специальной обработки (is null вместо = null), что вызывает много дополнительной работы.

При тестировании с Rails 3.2 сегодня было непросто создать неутвержденное логическое поле с нулевым значением и сохранить его в моей базе данных PostgreSQL, где он был послушно хранится как NULL. Чтобы избежать всех проблем, которые могут возникнуть, я планирую использовать этот подход для booleans в Rails:

  • Определить логические поля с :null => false при миграции.
  • Используйте :inclusion => { :in => [true, false] } для проверки поля в модели. Это дает хорошее сообщение, если поле неинициализировано (nil).

Вначале не интуитивно понятно, что я не могу использовать проверку :presence, но оказывается, что :presence проверяет, что значение не пустое, а значение false пустое. Другими словами, если вы подтвердите :presence => true и установите значение false, проверка не завершится. См. validates_presence_of в комментарии API и @Paul A Jungwirth на ответ @Karmajunkie.