Метод экземпляра модели возвращает неправильный результат при первом запуске, а затем правильный результат

У меня три модели, связанные с has_many: через ассоциации:

class Account < ApplicationRecord
  has_many :account_owners
  has_many :employees, through: account_owners

  def is_owned_or_belongs_to_team_of_employees(employee)
    employee.team.any? { |m| employees.include?(m) }
  end
end

class AccountOwner < ApplicationRecord
  belongs_to :account
  belongs_to :employee
end

class Employee < ApplicationRecord
  has_many :account_owners
  has_many :accounts, through: :account_owners

  def team
    self.class.where(
      'id IN (?)',
      self. class.find_by_sql(['WITH RECURSIVE search_tree(id, path) AS (
                                  SELECT id, ARRAY[id]
                                    FROM employees
                                    WHERE id = ?
                                  UNION ALL
                                  SELECT employees.id, path || employees.id
                                    FROM search_tree
                                    JOIN employees ON employees.manager_id = search_tree.id
                                    WHERE NOT employees.id = ANY(path)
                                )
                                SELECT id FROM search_tree ORDER BY path',
                             self.id])
    ).order(:id)
  end
end

Я вручную тестирую консоль Rails в своей среде разработки (используя некоторые приборы, которые я сначала загрузил в базу данных), метод Account#is_owned_or_belongs_to_team_of_employees.

Когда я запускаю метод в консоли, это происходит:

> a = Account.first
 => #<Account id: 534788375, name: "Sales Rep 1 (elena)-owned account", code: "EEE", created_at: "2018-07-15 09:41:55", updated_at: "2018-07-15 09:41:55">
> e = Employee.find_by(first_name: 'Elena')
 => #<Employee id: 701979064, first_name: "Elena", last_name: "López", manager_id: 1069403509, created_at: "2018-07-15 09:41:55", updated_at: "2018-07-15 09:41:55", mobile: nil, work: nil>
> e.team
 => #<ActiveRecord::Relation [#<Employee id: 701979064, first_name: "Elena", last_name: "López", manager_id: 1069403509, created_at: "2018-07-15 09:41:55", updated_at: "2018-07-15 09:41:55", mobile: nil, work: nil>]>
> a.is_owned_or_belongs_to_team_of e
 => nil
> a.is_owned_or_belongs_to_team_of e
 => true

Как вы можете видеть, метод возвращает nil (неправильный!) В первый раз и возвращает true (correct!) Следующие времена.

Удивительно то, что я могу исправить проблему, если я определяю метод следующим образом:

def is_owned_or_belongs_to_team_of employee
  puts "employees are #{employees.inspect}"
  employee.team.any? { |m| employees.include?(m) }
end

Теперь выполнение правильное, и метод возвращает неизменно тот же результат (true в моем примере):

> a = Account.first
 => #<Account id: 534788375, name: "Sales Rep 1 (elena)-owned account", code: "EEE", created_at: "2018-07-15 09:41:55", updated_at: "2018-07-15 09:41:55">
> e = Employee.find_by(first_name: 'Elena')
 => #<Employee id: 701979064, first_name: "Elena", last_name: "López", manager_id: 1069403509, created_at: "2018-07-15 09:41:55", updated_at: "2018-07-15 09:41:55", mobile: nil, work: nil>
> e.team
 => #<ActiveRecord::Relation [#<Employee id: 701979064, first_name: "Elena", last_name: "López", manager_id: 1069403509, created_at: "2018-07-15 09:41:55", updated_at: "2018-07-15 09:41:55", mobile: nil, work: nil>]>
> a.is_owned_or_belongs_to_team_of e
 => true
> a.is_owned_or_belongs_to_team_of e
 => true

Если я удалю оператор puts, мы вернемся к квадрату: метод возвращает nil в первый раз, а true - следующее.

И, что удивительно, если я сохраняю инструкцию puts но удаляю inspect (то есть, я просто делаю puts "employees are #{employees}" мы также возвращаемся к квадрату: nil в первый раз и true в следующие моменты.

Любая идея? Что здесь происходит?

Кстати, я запускаю Ruby 2.5.1 y Rails 5.2.0.

Ответы

Ответ 1

Я рад, что наткнулся на этого Единорога из-за ошибки!

После отладки в течение нескольких часов я узнал следующее:

  1. any? были новые изменения в релизе rails 5.2, который должен был делегировать его Enumerable
  2. удивительно, что если вы поместите bind.pry в реализацию any? и вызов super возвращает true даже в первый раз, а затем метод возвращает nil. ~/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/activerecord-5.2.0/lib/active_record/relation.rb @line 228 ActiveRecord::Relation#any?:

  3. если вы добавляете к employee.team .to_a все работает последовательно.

  4. если вы положили any? { |_| true } any? { |_| true } any? { |_| true } возвращает true.
  5. Если вы проверяете значение внутри блока для include? он возвращает true, но any? все еще возвращает nil !!!
  6. Если вы избегаете разрешения связи has_may through (путем вызова .to_a перед блоком) или даже с использованием другой ассоциации внутри any? блокировать все работает так, как ожидалось.
  7. проблема с использованием любой другой рубиновой версии.

Резюме

Проблема была введена в v5.2.0 2.5.1 rails v5.2.0 когда ActiveRecord::Relation включило Enumerable Это происходит с %w(none? any? one? many?) v5.2.0 %w(none? any? one? many?), Пытаясь разрешить a, has many through ассоциацию в своем блоке,