我有三种与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
(正确!)。
令人惊奇的是,如果我定义如下方法,我可以纠正问题:
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和Rails 5.2.0。
答案 0 :(得分:4)
很高兴我偶然发现了这个独角兽的bug!
调试了几个小时后,我发现了以下内容:
any?
在rails 5.2 release中进行了新更改,应该将其委派给Enumerable
令人惊讶的事情是,如果在any?
的实现中放入binding.pry并调用super
,即使是第一次也返回true,然后该方法返回{{1} }。 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
,它将返回true。any? { |_| true }
的块内检查值,则返回true,但include?
仍返回any?
!nil
关联(通过在块之前调用has_may through
),甚至避免在.to_a
块内使用其他关联,那么一切都会按预期进行。此问题是在any?
开始包含2.5.1
时在ruby v5.2.0
导轨ActiveRecord::Relation
中引入的。在尝试解决{{ 1}}关联中。