The #first and #last class methods on ActiveRecord::Base are examples
of Rails cleverness gone a little over the edge. Who hasn't run into
this problem?:
Person.blog_posts.first # => #<BlogPost name='foo'>
Person.blog_posts.first.name = 'bar'
Person.blog_posts.first # => #<BlogPost name='foo'>
The #first and #last methods always go to the database when called on
an unloaded association collection proxy, and do not update the
association target with the resulting record. So:
Person.blog_posts.loaded? # => false
Person.blog_posts.first # => #<BlogPost>
Person.blog_posts.loaded? # => false
Person.blog_posts.target # =>
This is obviously for performance reasons, given the assumption that
the most frequent case will be someone loading the first or last
record once, in which case returning a single record from the database
is faster than returning all records and then asking the underlying
array for the first one. Of course, in other cases, this causes
problems. For instance:
Person.blog_posts.count # => 5
Person.blog_posts.first.mark_for_destruction # => true
Person.save! # => true
Person.blog_posts.count #= 5 (!)
Now, if you start returning an arbitrarily large number of records
from the #first and #last methods, the single advantage of those
methods (performance) goes away, leaving only liabilities. Consider
this:
Person.blog_posts(100) # => [#<BlogPost, #<BlogPost]... ]
Person.blog_posts # => [#<BlogPost, #<BlogPost]... ]
Both of those calls generated database calls; the first returned 100
records, the second returned all records *including the 100 already
returned previously*. And nothing in that code makes it clear that
you're doing more database calls than necessary, or that you're
creating two in-memory objects for each of the 100 records returned
twice, or that the first 100 records aren't included in the
association proxy target.
Note that calling #first or #last with a count parameter on a
collection proxy works as expected with the current code because it
loads records and then delegates to the underlying array. This patch
would modify this existing behavior on association collections.
I realize the original intention was to add this capability to
ActiveRecord::Base class objects, but keep in mind you can't add an
ActiveRecord::Base class method without affecting the way association
proxies work (consider #sum).