<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array"/>
  <modified type="array">
    <modified>
      <diff>@@ -1,3 +1,5 @@
+* Add first/last methods to associations/named_scope. Resolved #226. [Ryan Bates]
+
 *2.1.0 RC1 (May 11th, 2008)*
 
 * Ensure hm:t preloading honours reflection options. Resolves #137. [Frederick Cheung]</diff>
      <filename>activerecord/CHANGELOG</filename>
    </modified>
    <modified>
      <diff>@@ -48,6 +48,26 @@ module ActiveRecord
         end
       end
       
+      # fetch first using SQL if possible
+      def first(*args)
+        if fetch_first_or_last_using_find? args
+          find(:first, *args)
+        else
+          load_target unless loaded?
+          @target.first(*args)
+        end
+      end
+
+      # fetch last using SQL if possible
+      def last(*args)
+        if fetch_first_or_last_using_find? args
+          find(:last, *args)
+        else
+          load_target unless loaded?
+          @target.last(*args)
+        end
+      end
+
       def to_ary
         load_target
         @target.to_ary
@@ -330,7 +350,10 @@ module ActiveRecord
             raise ActiveRecord::RecordNotSaved, &quot;You cannot call create unless the parent is saved&quot;
           end
         end
-               
+
+        def fetch_first_or_last_using_find?(args)
+          args.first.kind_of?(Hash) || !(loaded? || @owner.new_record? || @reflection.options[:finder_sql] || !@target.blank? || args.first.kind_of?(Integer))
+        end
     end
   end
 end</diff>
      <filename>activerecord/lib/active_record/associations/association_collection.rb</filename>
    </modified>
    <modified>
      <diff>@@ -102,7 +102,13 @@ module ActiveRecord
     
     class Scope
       attr_reader :proxy_scope, :proxy_options
-      [].methods.each { |m| delegate m, :to =&gt; :proxy_found unless m =~ /(^__|^nil\?|^send|^object_id$|class|extend|find|count|sum|average|maximum|minimum|paginate)/ }
+
+      [].methods.each do |m|
+        unless m =~ /(^__|^nil\?|^send|^object_id$|class|extend|find|count|sum|average|maximum|minimum|paginate|first|last)/
+          delegate m, :to =&gt; :proxy_found
+        end
+      end
+
       delegate :scopes, :with_scope, :to =&gt; :proxy_scope
 
       def initialize(proxy_scope, options, &amp;block)
@@ -115,6 +121,22 @@ module ActiveRecord
         load_found; self
       end
 
+      def first(*args)
+        if args.first.kind_of?(Integer) || (@found &amp;&amp; !args.first.kind_of?(Hash))
+          proxy_found.first(*args)
+        else
+          find(:first, *args)
+        end
+      end
+
+      def last(*args)
+        if args.first.kind_of?(Integer) || (@found &amp;&amp; !args.first.kind_of?(Hash))
+          proxy_found.last(*args)
+        else
+          find(:last, *args)
+        end
+      end
+
       protected
       def proxy_found
         @found || load_found</diff>
      <filename>activerecord/lib/active_record/named_scope.rb</filename>
    </modified>
    <modified>
      <diff>@@ -401,6 +401,8 @@ class HasAndBelongsToManyAssociationsTest &lt; ActiveRecord::TestCase
 
   def test_include_uses_array_include_after_loaded
     project = projects(:active_record)
+    project.developers.class # force load target
+
     developer = project.developers.first
     
     assert_no_queries do</diff>
      <filename>activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb</filename>
    </modified>
    <modified>
      <diff>@@ -818,6 +818,8 @@ class HasManyAssociationsTest &lt; ActiveRecord::TestCase
 
   def test_include_uses_array_include_after_loaded
     firm = companies(:first_firm)
+    firm.clients.class # force load target
+
     client = firm.clients.first
 
     assert_no_queries do
@@ -857,4 +859,68 @@ class HasManyAssociationsTest &lt; ActiveRecord::TestCase
     assert ! firm.clients.include?(client)
   end
 
+  def test_calling_first_or_last_on_association_should_not_load_association
+    firm = companies(:first_firm)
+    firm.clients.first
+    firm.clients.last
+    assert !firm.clients.loaded?
+  end
+
+  def test_calling_first_or_last_on_loaded_association_should_not_fetch_with_query
+    firm = companies(:first_firm)
+    firm.clients.class # force load target
+    assert firm.clients.loaded?
+
+    assert_no_queries do
+      firm.clients.first
+      assert_equal 2, firm.clients.first(2).size
+      firm.clients.last
+      assert_equal 2, firm.clients.last(2).size
+    end
+  end
+
+  def test_calling_first_or_last_on_existing_record_with_build_should_load_association
+    firm = companies(:first_firm)
+    firm.clients.build(:name =&gt; 'Foo')
+    assert !firm.clients.loaded?
+
+    assert_queries 1 do
+      firm.clients.first
+      firm.clients.last
+    end
+
+    assert firm.clients.loaded?
+  end
+
+  def test_calling_first_or_last_on_new_record_should_not_run_queries
+    firm = Firm.new
+
+    assert_no_queries do
+      firm.clients.first
+      firm.clients.last
+    end
+  end
+
+  def test_calling_first_or_last_with_find_options_on_loaded_association_should_fetch_with_query
+    firm = companies(:first_firm)
+    firm.clients.class # force load target
+
+    assert_queries 2 do
+      assert firm.clients.loaded?
+      firm.clients.first(:order =&gt; 'name')
+      firm.clients.last(:order =&gt; 'name')
+    end
+  end
+
+  def test_calling_first_or_last_with_integer_on_association_should_load_association
+    firm = companies(:first_firm)
+
+    assert_queries 1 do
+      firm.clients.first(2)
+      firm.clients.last(2)
+    end
+
+    assert firm.clients.loaded?
+  end
+
 end</diff>
      <filename>activerecord/test/cases/associations/has_many_associations_test.rb</filename>
    </modified>
    <modified>
      <diff>@@ -664,6 +664,8 @@ class AssociationsJoinModelTest &lt; ActiveRecord::TestCase
 
   def test_has_many_through_include_uses_array_include_after_loaded
     david = authors(:david)
+    david.categories.class # force load target
+
     category = david.categories.first
 
     assert_no_queries do</diff>
      <filename>activerecord/test/cases/associations/join_model_test.rb</filename>
    </modified>
    <modified>
      <diff>@@ -99,12 +99,12 @@ class AssociationProxyTest &lt; ActiveRecord::TestCase
     david = authors(:david)
     assert_equal  david, david.posts.proxy_owner
     assert_equal  david.class.reflect_on_association(:posts), david.posts.proxy_reflection
-    david.posts.first   # force load target
+    david.posts.class   # force load target
     assert_equal  david.posts, david.posts.proxy_target
 
     assert_equal  david, david.posts_with_extension.testing_proxy_owner
     assert_equal  david.class.reflect_on_association(:posts_with_extension), david.posts_with_extension.testing_proxy_reflection
-    david.posts_with_extension.first   # force load target
+    david.posts_with_extension.class   # force load target
     assert_equal  david.posts_with_extension, david.posts_with_extension.testing_proxy_target
   end
 </diff>
      <filename>activerecord/test/cases/associations_test.rb</filename>
    </modified>
    <modified>
      <diff>@@ -118,4 +118,32 @@ class NamedScopeTest &lt; ActiveRecord::TestCase
     assert_equal expected_proxy_options, Topic.approved.proxy_options
   end
 
+  def test_first_and_last_should_support_find_options
+    assert_equal Topic.base.first(:order =&gt; 'title'), Topic.base.find(:first, :order =&gt; 'title')
+    assert_equal Topic.base.last(:order =&gt; 'title'), Topic.base.find(:last, :order =&gt; 'title')
+  end
+
+  def test_first_and_last_should_allow_integers_for_limit
+    assert_equal Topic.base.first(2), Topic.base.to_a.first(2)
+    assert_equal Topic.base.last(2), Topic.base.to_a.last(2)
+  end
+
+  def test_first_and_last_should_not_use_query_when_results_are_loaded
+    topics = Topic.base
+    topics.reload # force load
+    assert_no_queries do
+      topics.first
+      topics.last
+    end
+  end
+
+  def test_first_and_last_find_options_should_use_query_when_results_are_loaded
+    topics = Topic.base
+    topics.reload # force load
+    assert_queries(2) do
+      topics.first(:order =&gt; 'title')
+      topics.last(:order =&gt; 'title')
+    end
+  end
+
 end</diff>
      <filename>activerecord/test/cases/named_scope_test.rb</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>ebb642fa3a2b1a4e31abf9610ca634e6bb5d57d3</id>
    </parent>
  </parents>
  <author>
    <name>Ryan Bates</name>
    <login>ryanb</login>
    <email>ryan@railscasts.com</email>
  </author>
  <url>http://github.com/rails/rails/commit/73c59638549686fccc749ffd3ac53cb533c5fd61</url>
  <id>73c59638549686fccc749ffd3ac53cb533c5fd61</id>
  <committed-date>2008-05-20T04:27:14-07:00</committed-date>
  <authored-date>2008-05-20T04:11:25-07:00</authored-date>
  <message>Add first/last methods to associations/named_scope. [#226 state:resolved]

Signed-off-by: Pratik Naik &lt;pratiknaik@gmail.com&gt;</message>
  <tree>44aebe6752869fdb13240d0bf8422388cca81767</tree>
  <committer>
    <name>Pratik Naik</name>
    <login>lifo</login>
    <email>pratiknaik@gmail.com</email>
  </committer>
</commit>
