Метод экземпляра модели возвращает неправильный результат при первом запуске, а затем правильный результат
У меня три модели, связанные с 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
Я рад, что наткнулся на этого Единорога из-за ошибки!
После отладки в течение нескольких часов я узнал следующее:
-
any?
были новые изменения в релизе rails 5.2, который должен был делегировать его Enumerable
-
удивительно, что если вы поместите 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?:
-
если вы добавляете к employee.team
.to_a
все работает последовательно.
- если вы положили
any? { |_| true }
any? { |_| true }
any? { |_| true }
возвращает true. - Если вы проверяете значение внутри блока для
include?
он возвращает true, но any?
все еще возвращает nil
!!! - Если вы избегаете разрешения связи
has_may through
(путем вызова .to_a
перед блоком) или даже с использованием другой ассоциации внутри any?
блокировать все работает так, как ожидалось. - проблема с использованием любой другой рубиновой версии.
Резюме
Проблема была введена в 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
ассоциацию в своем блоке,