Trivial Rails Question, I am unable to explain the behavior

Following 2 statements look identical to me, however the first one returns only one object but the second one returns correctly an array of 4 objects.

What is happening here is simple, I get all the Grade objects (1st one using Grade.find(:all) and second one using Grade.all), and then filter out all the grade objects where they have worksheets assigned to them.

@grades = Grade.find(:all){|grade| Worksheet.find(:all,:conditions=> [‘grade_id =?’,grade.id]).count > 0}

@grades = Grade.all {|grade| Worksheet.find(:all,:conditions=> [‘grade_id =?’,grade.id]).count > 0}

Can somebody explain why the results would be different for these 2 statements.

I don’t have rails in front of me, but looks like there may be a problem with implied parentheses. Have you tried things like:

@grades = Grade.all*() ** {|grade| Worksheet.find(:all,:conditions=> [‘grade_id =?’,grade.id]).count > 0}*

Again I can’t test so not sure if that helps

The find method takes the option hash as the last parameter. Either use Model.all(options) or Model.find(:all, options)

The correct code is:

@grades = Grade.find(:all, {|grade| Worksheet.find(:all,:conditions=> [‘grade_id =?’,grade.id]).count > 0})

Dheeraj Kumar

The find method takes the option hash as the last parameter. Either use Model.all(options) or Model.find(:all, options)

The correct code is:

@grades = Grade.find(:all, {|grade| Worksheet.find(:all,:conditions=> [‘grade_id =?’,grade.id]).count > 0})

Um, no. That won’t even parse.

I don’t have rails in front of me, but looks like there may be a problem with implied parentheses. Have you tried things like:
@grades = Grade.all*() ** {|grade| Worksheet.find(:all,:conditions=> [‘grade_id =?’,grade.id]).count > 0}*

Again I can’t test so not sure if that helps

Following 2 statements look identical to me, however the first one returns only one object but the second one returns correctly an array of 4 objects.

What is happening here is simple, I get all the Grade objects (1st one using Grade.find(:all) and second one using Grade.all), and then filter out all the grade objects where they have worksheets assigned to them.

@grades = Grade.find(:all){|grade| Worksheet.find(:all,:conditions=> [‘grade_id =?’,grade.id]).count > 0}

@grades = Grade.all {|grade| Worksheet.find(:all,:conditions=> [‘grade_id =?’,grade.id]).count > 0}

There are two things going on here:

  • in the first statement, find(:all) acts like Enumerable#find when passed a block. Each of the found objects is passed to the block and the first one that returns a truthy value is returned.

  • in the second statement, all doesn’t call the block so you get exactly the same results you would have without a block at all.

If this is an operation you do a lot in your app, you may want to check out the :counter_cache option of belongs_to, which will allow you to write this (correctly) as:

@grades = Grade.where(‘worksheets_count > 0’).all

Also note that if counts are all you’re looking for, you should avoid doing a find(:all) on them (as on Worksheet above); the count method on the model class can do this much more efficiently (with a SQL COUNT statement, rather than loading a bunch of objects and then counting them).

–Matt Jones

Thanks Matt, Your alternative method on how to best get the data definitely is a much optimized solution then what I was using, I am not using that and everything works great.

However, I am still confused on one part.

as per my understanding

Grade.find(:all)

and

**Grade.all **

both should return an array of Grade objects, so how are the results different if I just pass the objects through this block.

{|grade| Worksheet.find(:all,:conditions=> [‘grade_id =?’,grade.id]).count > 0}

Thanks again for the help.

A block by itself doesn’t do ANYTHING - it’s entirely dependent on the function it’s passed to. For instance:

def hello(who)

puts “Hello #{who}”

end

hello(‘World’) # prints “Hello World”

hello(‘World’) { |x| puts ‘WAT’ } # prints “Hello World” - doesn’t do anything with the block

The case you encountered with find(:all) was specifically coded to use the block just like Enumerable#find:

a = [1,2,3,4]

a.find { |x| x > 2 } # => returns 3, the first value in the array that the block returns a truthy value for

–Matt Jones