Background
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.
Problem
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
Why is it doing this?
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
Possible answers
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.
Thanks!
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)