Reputation: 7364
I'm using
> ruby -v
ruby 2.1.0p0 (2013-12-25 revision 44422) [x86_64-darwin12.0]
> rails -v
Rails 4.0.2
So normally,
> [1,2,3,4,5].find_all{|x| x == 4}.count
and
> [1,2,3,4,5].count{|x| x == 4}
give the same value:
=> 1
...all good.
But in my app, something's wrong. When I put in a breakpoint (using pry), I notice that I'm getting an inconsistency:
(don't worry too much about the particular data structures here)
> Meme.find(24).punches.count{|punch| punch.new_to_user?(User.find(14))}
=> 6
Whereas:
> Meme.find(24).punches.find_all{|punch| punch.new_to_user?(User.find(14))}.count
=> 0
6 != 0, amirite? It seems from the http://ruby-doc.org/core-2.1.0/Enumerable.html documentation that ruby 2.1.0 should treat these two cases identically.
When I look at what these commands execute, it's clear that .count{} isn't really evaluating the code inside its block:
> 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
As opposed to the (I think) correct behavior of 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
Does the version of ruby or rails I'm using not support this use of count{block}? I have been using the ruby 2.1.0 doc http://ruby-doc.org/core-2.1.0/Enumerable.html as a reference.
Is the version my app is using, or pry is using, different from the 2.1.0/4.0.2 that I expect? FWIW, in my Gemfile I have
source 'https://rubygems.org'
ruby "2.1.0"
gem 'rails', '4.0.2'
The cacheing? I don't understand this at all.
Edit:
To clarify, new_to_user? does a bit of work with other ActiveRecords. That's why I say the find_all behavior seems correct. count{} seems to be running a simple SQL COUNT command, which is wrong for my purposes (but may be right for the version of ruby, for reasons I don't understand)
Upvotes: 1
Views: 257
Reputation: 2739
find_all
from enumerable is not the same method as find_all
from ActiveRecord.
Upvotes: 1
Reputation: 176442
Meme.find(24).punches
doesn't return an array. It returns an ActiveRecord::Relation
that generally behaves like an array, but it has some different properties.
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))}
Upvotes: 3