The difference between the two strategies for eager loading are discussed in the comments here
https://github.com/rails/rails/blob/3-0-stable/activerecord/lib/active_record/association_preload.rb
From the documentation:
# The second strategy is to use multiple database queries, one for each
# level of association. Since Rails 2.1, this is the default strategy. In
# situations where a table join is necessary (e.g. when the +:conditions+
# option references an association's column), it will fallback to the table
# join strategy.
I believe that the dot in "foo.bar" is causing active record to think that you are putting a condition on a table that is outside of the originating model which prompts the second strategy discussed in the documentation.
The two separate queries runs one with the Person model and the second with the Item model.
Person.includes(:items).where(:name => 'fubar')
Person Load (0.2ms) SELECT "people".* FROM "people" WHERE "people"."name" = 'fubar'
Item Load (0.4ms) SELECT "items".* FROM "items" WHERE ("items".person_id = 1) ORDER BY items.ordinal
Because you run the second query against the Item model, it inherits the default scope where you specified order(:ordinal)
.
The second query, which it attempts eager loading with the full runs off the person model and will not use the default scope of the association.
Person.includes(:items).where(:name => 'foo.bar')
Person Load (0.4ms) SELECT "people"."id" AS t0_r0, "people"."name" AS t0_r1,
"people"."created_at" AS t0_r2, "people"."updated_at" AS t0_r3, "items"."id" AS t1_r0,
"items"."person_id" AS t1_r1, "items"."name" AS t1_r2, "items"."ordinal" AS t1_r3,
"items"."created_at" AS t1_r4, "items"."updated_at" AS t1_r5 FROM "people" LEFT OUTER JOIN
"items" ON "items"."person_id" = "people"."id" WHERE "people"."name" = 'foo.bar'
It is a little buggy to think that, but I can see how it would be with the several different ways you can present a list of options, the way to be sure that you catch all of them would be to scan the completed "WHERE" conditions for a dot and use the second strategy, and they leave it that way because both strategies are functional. I would actually go as far as saying that the aberrant behavior is in the first query, not the second. If you would like the ordering to persist for this query, I recommend one of the following:
1) If you want the association to have an order by when it is called, then you can specify that with the association. Oddly enough, this is in the documentation, but I could not get it to work.
Source: http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#method-i-has_many
class Person < ActiveRecord::Base
has_many :items, :order => 'items.ordinal'
end
2) Another method would be to just add the order statement to the query in question.
Person.includes(:items).where(:name => 'foo.bar').order('items.ordinal')
3) Along the same lines would be setting up a named scope
class Person < ActiveRecord::Base
has_many :items
named_scope :with_items, includes(:items).order('items.ordinal')
end
And to call that:
Person.with_items.where(:name => 'foo.bar')