模型实例方法在第一次运行时返回错误的结果,随后返回正确的结果

Rod*_*ano 11 ruby-on-rails

我有三个与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
Run Code Online (Sandbox Code Playgroud)

我在我的开发环境的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
Run Code Online (Sandbox Code Playgroud)

如您所见,该方法nil第一次返回(错误!),并返回true(正确!)以下时间.

令人惊奇的是,如果我定义这样的方法,我可以纠正这个问题:

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

现在执行是正确的,并且该方法始终返回相同的结果(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
Run Code Online (Sandbox Code Playgroud)

如果我删除该puts语句,我们将回到原点:该方法nil第一次返回,以及true以下时间.

而且,令人惊讶的是,如果我保留puts声明但删除了inspect(也就是说,我只是puts "employees are #{employees}"回到原点:nil第一次,以及true以下时间.

任何的想法?这里发生了什么?

顺便说一下,我正在运行Ruby 2.5.1和Rails 5.2.0.

kha*_*maa 5

我很高兴我偶然发现了这个错误的独角兽!

经过几个小时的调试后,我发现了以下内容:

  1. any?Rails 5.2 版本中有新的变化应该委托给Enumerable
  2. 令人惊讶的是,如果您将 binding.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 }它返回true。
  5. 如果您检查块内的值,include?它会返回 true 但any?仍然返回nil!!!
  6. 如果您避免解析has_may through关联(通过.to_a在块之前调用)或什至在any?块内使用不同的关联,一切都会按预期进行。
  7. 使用任何其他 ruby​​ 版本可以解决问题。

概括

这个问题是在开始包含ruby 2.5.1railsv5.2.0时引入的。它发生在尝试解决其块中的关联时。ActiveRecord::RelationEnumerable%w(none? any? one? many?)has many through