Rails枚举:为什么find_all {...}。count返回一个不同于count {...}的值?

时间:2014-01-24 20:13:39

标签: ruby-on-rails ruby ruby-2.1

背景

我正在使用

> ruby -v
ruby 2.1.0p0 (2013-12-25 revision 44422) [x86_64-darwin12.0]

> rails -v
Rails 4.0.2

通常情况下,

> [1,2,3,4,5].find_all{|x| x == 4}.count

> [1,2,3,4,5].count{|x| x == 4}

给出相同的值:

=> 1

......一切都很好。

问题

但是在我的应用中,出了点问题。当我输入一个断点(使用pry)时,我注意到我的不一致:

(不要过分担心这里的特定数据结构)

> Meme.find(24).punches.count{|punch| punch.new_to_user?(User.find(14))}
=> 6

鉴于:

> Meme.find(24).punches.find_all{|punch| punch.new_to_user?(User.find(14))}.count
=> 0

为什么要这样做?

6!= 0,amirite?从http://ruby-doc.org/core-2.1.0/Enumerable.html文档中可以看出,ruby 2.1.0应该完全相同地处理这两种情况。

当我查看这些命令执行的内容时,很明显.count {}并没有真正评估其块内的代码:

> Meme.find(24).punches.count{|punch| punch.new_to_user?(User.find(14))}
 CACHE (0.0ms)  SELECT "memes".* FROM "memes" WHERE "memes"."id" = $1 LIMIT 1  [["id", 24]]
 CACHE (0.0ms)  SELECT COUNT(*) FROM "punches" WHERE "punches"."meme_id" = $1  [["meme_id", 24]]
=> 6

与(我认为)find_all的正确行为相反:

> Meme.find(24).punches.find_all{|punch| punch.new_to_user?(User.find(14))}.count
 CACHE (0.0ms)  SELECT "memes".* FROM "memes" WHERE "memes"."id" = $1 LIMIT 1  [["id", 24]]
 CACHE (0.0ms)  SELECT "punches".* FROM "punches" WHERE "punches"."meme_id" = $1  [["meme_id", 24]]
 CACHE (0.0ms)  SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 14]]
 CACHE (0.2ms)  SELECT "votes".* FROM "votes" INNER JOIN "vote_decisions" ON "votes"."vote_decision_id" = "vote_decisions"."id" WHERE "vote_decisions"."user_id" = $1 AND "votes"."punch_id" = 531  [["user_id", 14]]
 CACHE (0.0ms)  SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 14]]
 CACHE (0.0ms)  SELECT "votes".* FROM "votes" INNER JOIN "vote_decisions" ON "votes"."vote_decision_id" = "vote_decisions"."id" WHERE "vote_decisions"."user_id" = $1 AND "votes"."punch_id" = 532  [["user_id", 14]]
 CACHE (0.0ms)  SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 14]]
 CACHE (0.0ms)  SELECT "votes".* FROM "votes" INNER JOIN "vote_decisions" ON "votes"."vote_decision_id" = "vote_decisions"."id" WHERE "vote_decisions"."user_id" = $1 AND "votes"."punch_id" = 533  [["user_id", 14]]
 CACHE (0.0ms)  SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 14]]
 CACHE (0.0ms)  SELECT "votes".* FROM "votes" INNER JOIN "vote_decisions" ON "votes"."vote_decision_id" = "vote_decisions"."id" WHERE "vote_decisions"."user_id" = $1 AND "votes"."punch_id" = 534  [["user_id", 14]]
 CACHE (0.0ms)  SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 14]]
 CACHE (0.0ms)  SELECT "votes".* FROM "votes" INNER JOIN "vote_decisions" ON "votes"."vote_decision_id" = "vote_decisions"."id" WHERE "vote_decisions"."user_id" = $1 AND "votes"."punch_id" = 535  [["user_id", 14]]
 CACHE (0.0ms)  SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 14]]
 CACHE (0.0ms)  SELECT "votes".* FROM "votes" INNER JOIN "vote_decisions" ON "votes"."vote_decision_id" = "vote_decisions"."id" WHERE "vote_decisions"."user_id" = $1 AND "votes"."punch_id" = 536  [["user_id", 14]]
=> 0

可能的答案

  • 我使用的ruby或rails版本是否不支持使用count {block}?我一直在使用ruby 2.1.0 doc http://ruby-doc.org/core-2.1.0/Enumerable.html作为参考。

  • 我的app正在使用的版本,或pry正在使用的版本,与我期望的2.1.0 / 4.0.2不同? FWIW,在我的Gemfile中我有

    source 'https://rubygems.org'
    ruby "2.1.0"
    gem 'rails', '4.0.2'
    
  • 缓存?我根本不明白这一点。

谢谢!

编辑:

澄清一下,new_to_user?与其他ActiveRecords做了一些工作。这就是为什么我说find_all行为似乎是正确的。 count {}似乎正在运行一个简单的SQL COUNT命令,这对我来说是错误的(但可能是正确的ruby版本,原因我不明白)

2 个答案:

答案 0 :(得分:3)

Meme.find(24).punches不会返回数组。它返回一个ActiveRecord::Relation,它通常表现得像一个数组,但它有一些不同的属性。

当您在关系上调用#count时,会执行ActiveRecord association #count方法,而不是可枚举#count。这意味着Meme.find(24).punches.count应该是SQL计数并返回meme的打孔数,而不管块(在这种情况下被忽略)。

如果您想获得相同的结果,首先需要将关联转换为Array

Meme.find(24).punches.to_a.count{|punch| punch.new_to_user?(User.find(14))}

答案 1 :(得分:1)

来自enumerable的

find_all与来自ActiveRecord的find_all的方法不同。