почему вызов `length` для отношения ActiveRecord изменяет размер? - PullRequest
0 голосов
/ 29 августа 2018

На Rails 4.2.10 У меня есть эта модель:

class RegistrationApproval < ActiveRecord::Base
  belongs_to :registering_user, class_name: 'User', inverse_of: :registration_approvals
  belongs_to :approver, class_name: 'User', inverse_of: :approved_registrations
end

И в моей User модели у меня есть это:

has_many :registration_approvals, foreign_key: 'registering_user_id', dependent: :destroy, inverse_of: :registering_user
has_many :registration_approvers, through: :registration_approvals, class_name: 'User', source: :approver, inverse_of: :registering_users
has_many :approved_registrations, class_name: 'RegistrationApproval', foreign_key: 'approver_id', dependent: :destroy, inverse_of: :approver
has_many :registering_users, through: :approved_registrations, class_name: 'User', inverse_of: :registration_approvers

def approve_registration(approver)
  registration_approvals.create(approver: approver)
  Rails.logger.debug "approvers: #{registration_approvers.length}"
  return [registration_approvers.size, registration_approvers.count]
end

В методе approve_registration оператор отладки изменяет выходные данные этого метода. Без оператора отладки, когда есть два утверждения для пользователя, registration_approvers.size оценивается как 2, как и следовало ожидать, но с оператором отладки оно оценивается до 1. Почему это происходит? Это ошибка в Rails 4.2?

Оценка count остается неизменной.

Кажется, что вызов registration_approvers.length вызывает изменение в size

Вот неудачный тест, который показывает эту проблему:

 it 'should have size and count equal' do
    registering_user = FactoryBot.create(:user)
    _(registering_user).must_be :persisted?
    approver1 = FactoryBot.create(:user)
    _(approver1).must_be :persisted?
    approver2 = FactoryBot.create(:user)
    _(approver2).must_be :persisted?
    registering_user.approve_registration(approver1)
    size, count = registering_user.approve_registration(approver2)
    _(count).must_equal 2
    _(size).must_equal 2
  end

Все утверждения в тестовом прохождении, за исключением одного в последней строке, которая дает сбой, потому что size равно 1. Тест проходит, когда вызов метода registration_approvers.length удаляется из метода approve_registration.

Это проблема для меня, так как в методе approve_registration я хочу перебрать registration_approvers с each, но когда я это делаю, он пропускает последнее в коллекции, вероятно, потому что вызов метода each length в коллекции и изменяет значение размера, а затем выходит из цикла, думая, что он достиг конца.

Если мне удастся обойти обход этой коллекции без вызова length, это немного поможет.

Ответы [ 2 ]

0 голосов
/ 29 августа 2018

#size использует значение #length после загрузки результатов запроса, см. Фрагмент кода документа .

#length не запускает новый запрос, а просто вызывает #length для массива (ранее полученных / кэшированных) результатов.

OTOH: #count действительно запускает новый запрос (и фактически он запускается в первый раз, когда результаты еще не загружены.

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

registering_user.approve_registration(approver1)
registering_user.reload
size, count = registering_user.approve_registration(approver2)

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

0 голосов
/ 29 августа 2018

Имейте в виду, что registration_approvers является ActiveRecord :: Association объектом, но я не думаю, что длина определена в этом классе, она может быть унаследована от родителя ...

Я считаю, что результатом запросов являются массивы, но я не думаю, что длина определена в этом классе, она может быть унаследована от родителя ... Я считаю, что результатом запросов являются массивы

это исходный код ruby ​​для этого метода

static VALUE
rb_ary_length(VALUE ary)
{
    long len = RARRAY_LEN(ary);
    return LONG2NUM(len);
}

Мне нужно несколько очков / отзывов от вас

...