Переход на Ruby 2.3.0 (с 2.0.0) создает проблему при проверке активной записи

#ruby-on-rails #ruby #ruby-on-rails-4 #activerecord

#ruby-on-rails #ruby #ruby-on-rails-4 #activerecord

Вопрос:

Недавно я переключил свое приложение Rails 4.2 с ruby 2.0.0 на 2.3.0, и когда я запускаю свой rails-сервер ($ rails s), только что появилось новое уведомление

 /home/app/models/user.rb:127: warning: key :numericality is duplicated and overwritten on line 128
/homeapp/models/user.rb:127: warning: key :on is duplicated and overwritten on line 128
/home/app/admin/user.rb:142: warning: key :collection is duplicated and overwritten on line 147
/home/app/models/deal.rb:223: warning: key :numericality is duplicated and overwritten on line 226
/home/app/models/deal.rb:234: warning: key :numericality is duplicated and overwritten on line 237
  

Вот пример строки, вызывающей проблемы, это те, в которых при создании учетной записи я устанавливаю атрибут nb равным 3 (on: :create), и пользователь в течение срока действия своей учетной записи может увеличить количество разрешений, но никогда не сможет иметь большечем 7 (on: :update).

 validates :nb_of_permissions,
            presence:true,
            numericality: { equal_to: 3 }, on: :create,               
            numericality: { less_than_or_equal_to: 7 }, on: :update   
  

Что я должен изменить?

Спасибо

Комментарии:

1. Проблема / ошибка всегда была. Просто теперь вы можете это увидеть. 🙂

2. У меня было такое чувство:) спасибо Rails / ruby за строгие соглашения и уведомления 🙂

Ответ №1:

Основное отличие здесь в том, что обновление Ruby сообщает вам об ошибке в вашем приложении, которая ранее оставалась незамеченной. numericality: { less_than_or_equal_to: 7 }, on: :update На самом деле когда-либо использовался только второй.

 irb(main):001:0> { foo: 1, foo: 2 }
(irb):1: warning: key :foo is duplicated and overwritten on line 1
=> {:foo=>2}
  

Используйте validates :att, {} , когда у вас есть относительно простые условия.
Поскольку ваши проверки применяются к разным событиям жизненного цикла, вы должны объявлять каждое из них как отдельную проверку.

 validates :nb_of_permissions, { presence: true }
validates_numericality_of :nb_of_permissions equal_to: 3, on: :create 
validates_numericality_of :nb_of_permissions less_than_or_equal_to: 7, on: :update 
  

Тестирование базовых костей проверки модели на самом деле довольно просто (хотя и не так гладко, как с Shoulda-Matchers):

 RSpec.describe Thing do
  describe "validations"
    describe "#nb_of_permissions" do
      context "when updating" do
        let(:thing) { Thing.create(nb_of_permissions: 3) } 
        let(:errors) { thing.valid?.errors[:nb_of_permissions] }
        it 'is must be present' do
          thing.nb_of_permissions = nil
          expect(errors).to include 'must be present.'
        end
        it 'is must be at least 7' do
          thing.nb_of_permissions = 10000
          expect(errors).to include "must be less than or equal to 7"
        end
      end 
    end
  end
end
  

Комментарии:

1. PS. У вас действительно должен быть где-то тест, который гарантирует, что обе проверки будут применены nb_of_permissions , если это важно для вашего приложения.

2. @Mathieu: думаю, ваши тесты ничего не тестировали 🙂

3. ОК. в этом случае вы должны закомментировать обе строки и убедиться, что у вас есть по крайней мере два сбоя теста, поскольку ваши тесты вообще не охватывают его.

4. на самом деле у меня были тесты, но я закомментировал, поскольку мне так и не удалось заставить их работать для on: update. это было что-то вроде: validate_numericality_of(:max_nb).only_integer.is_less_than_or_equal_to(90).on(:update) / сбой, и у других людей такая же проблема: github.com/thoughtbot/shoulda-matchers/issues/678

5. Добавлен пример того, как выполнить тест проверки модели без shoulda-matchers. Если вы используете FactoryGirl, вы можете использовать build_stubbed для сценария обновления, поскольку он подделывает сохранение, поэтому on: update вызывается проверка.

Ответ №2:

Вы должны попробовать это

 validates :nb_of_permissions, presence:true, numericality: { equal_to: 3 }, on: :create
validates :nb_of_permissions, presence:true, numericality: { less_than_or_equal_to: 7 }, on: :update
  

Комментарии:

1. круто, что это сработало. просто сопутствующий вопрос: тогда, если при создании у меня есть presence: true, если подразумевает, что при обновлении оно, конечно, уже присутствует, тогда, может быть, в вашей второй строке предложения при обновлении мне не нужно будет снова проверять, что presence = true?

2. Нет, вы либо разделяете проверки наличия и численности, как в моем ответе, либо добавляете проверку наличия к обоим. Если вы удалите presence: true во второй строке, это позволит вам обновить значение nil для nb_of_permissions из-за способа validates_numericality_of работы.

3. Вам нужно проверить наличие, если это необходимо для существования. После всех nb_of_permissions потенциально может быть установлено значение null перед вызовом update.

Ответ №3:

Ваш старый код был ошибочным и не выполнял то, что вы думаете. Здесь не произошло никаких изменений в поведении; вам просто показывают полезное предупреждение, чтобы выделить вероятную ошибку!

Рассмотрим следующий простой пример:

 hash = {a: 1, a: 2}
  

В версиях Ruby 2.0 и 2.3 это эквивалентно простому определению:

 hash = {a: 2}
  

… Потому что хэш-ключ переопределяется. Аналогично, ваш код:

 validates :nb_of_permissions,
            presence:true,
            numericality: { equal_to: 3 }, on: :create,
            numericality: { less_than_or_equal_to: 7 }, on: :update
  

… Это и всегда было эквивалентно простой записи:

 validates :nb_of_permissions,
            presence:true,
            numericality: { less_than_or_equal_to: 7 },
            on: :update
  

Существует несколько способов сделать это, но, например, вы можете исправить эту ошибку, определив две проверки, такие как:

 validates :nb_of_permissions,
            presence:true,
            numericality: { less_than_or_equal_to: 7 },
            on: :update

validates :nb_of_permissions,
            presence:true,
            numericality: { equal_to: 3 },
            on: :create