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,阿米特?从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作为参考。

  • 我的应用程序正在使用或 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​​ 版本可能是正确的,原因我不明白)

4

2 回答 2

3

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

When you invoke #count on the relation, the ActiveRecord association #count method is executed, and not the Enumerable #count. This means that Meme.find(24).punches.count is supposed to SQL count and return the number of punches for the meme, regardless the block (which is ignored in this case).

If you want to achieve the same result, you first need to convert the association into an Array.

Meme.find(24).punches.to_a.count{|punch| punch.new_to_user?(User.find(14))}
于 2014-01-24T21:07:52.683 回答
1

find_all来自可枚举的方法与find_all来自 ActiveRecord 的方法不同。

于 2014-01-24T21:11:08.740 回答