diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb
index b20da51..952b884 100755
--- a/activerecord/lib/active_record/base.rb
+++ b/activerecord/lib/active_record/base.rb
@@ -566,15 +566,39 @@ module ActiveRecord #:nodoc:
end
# A convenience wrapper for find(:first, *args). You can pass in all the
- # same arguments to this method as you can to find(:first).
+ # same arguments to this method as you can to find(:first) If the first
+ # argument is an integer an array of the first n records will be returned.
+ #
+ # ==== Examples
+ #
+ # Person.first # Equivalent to Person.find(:first)
+ # Person.first(:conditions => [ "user_name = ?", user_name])
+ # Person.first(5) # Equivalent to Person.find(:all, :limit => 5)
+ # Person.first(5, :conditions => [ "user_name = ?", user_name])
def first(*args)
- find(:first, *args)
+ if args.first.kind_of?(Integer)
+ find(:all, (args[1] || {}).merge(:limit => args.first))
+ else
+ find(:first, *args)
+ end
end
- # A convenience wrapper for find(:last, *args). You can pass in all the
- # same arguments to this method as you can to find(:last).
+ # A convenience wrapper for find(:first, *args). You can pass in all the
+ # same arguments to this method as you can to find(:first) If the first
+ # argument is an integer an array of the last n records will be returned.
+ #
+ # ==== Examples
+ #
+ # Person.last # Equivalent to Person.find(:last)
+ # Person.last(:conditions => [ "user_name = ?", user_name])
+ # Person.last(5) # Equivalent to Person.find(:all, :limit => 5, :order => 'ID desc').reverse!
+ # Person.last(5, :conditions => [ "user_name = ?", user_name])
def last(*args)
- find(:last, *args)
+ if args.first.kind_of?(Integer)
+ find(:all, reverse_order_in_options(args[1] || {}).merge(:limit => args.first)).reverse!
+ else
+ find(:last, *args)
+ end
end
# This is an alias for find(:all). You can pass in all the same arguments to this method as you can
@@ -1396,6 +1420,10 @@ module ActiveRecord #:nodoc:
end
def find_last(options)
+ find_initial(reverse_order_in_options(options))
+ end
+
+ def reverse_order_in_options(options)
order = options[:order]
if order
@@ -1408,8 +1436,7 @@ module ActiveRecord #:nodoc:
scoped_order = reverse_sql_order(scope(:find, :order))
scoped_methods.select { |s| s[:find].update(:order => scoped_order) }
end
-
- find_initial(options.merge({ :order => order }))
+ options.merge({ :order => order })
end
def reverse_sql_order(order_query)
diff --git a/activerecord/lib/active_record/named_scope.rb b/activerecord/lib/active_record/named_scope.rb
index 83043c2..1fbae87 100644
--- a/activerecord/lib/active_record/named_scope.rb
+++ b/activerecord/lib/active_record/named_scope.rb
@@ -121,7 +121,9 @@ module ActiveRecord
end
def first(*args)
- if args.first.kind_of?(Integer) || (@found && !args.first.kind_of?(Hash))
+ if args.first.kind_of?(Integer) && !@found
+ find(:all, (args[1] || {}).merge(:limit => args.first))
+ elsif args.first.kind_of?(Integer) || (@found && !args.first.kind_of?(Hash))
proxy_found.first(*args)
else
find(:first, *args)
@@ -129,7 +131,9 @@ module ActiveRecord
end
def last(*args)
- if args.first.kind_of?(Integer) || (@found && !args.first.kind_of?(Hash))
+ if args.first.kind_of?(Integer) && !@found
+ find(:all, reverse_order_in_options({:order => proxy_options.delete(:order)}.merge(args[1] || {})).merge(:limit => args.first)).reverse!
+ elsif args.first.kind_of?(Integer) || (@found && !args.first.kind_of?(Hash))
proxy_found.last(*args)
else
find(:last, *args)
diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb
index aebcca6..1dde5bf 100644
--- a/activerecord/test/cases/base_test.rb
+++ b/activerecord/test/cases/base_test.rb
@@ -1718,6 +1718,12 @@ class BasicsTest < ActiveRecord::TestCase
def test_last
assert_equal Developer.find(:first, :order => 'id desc'), Developer.last
+ assert_equal Developer.find(:all, :order => 'id desc', :limit => 2), Developer.last(2)
+ end
+
+ def test_first
+ assert_equal Developer.find(:first, :order => 'id asc'), Developer.first
+ assert_equal Developer.find(:all, :order => 'id asc', :limit => 2), Developer.first(2)
end
def test_all_with_conditions
diff --git a/activerecord/test/cases/named_scope_test.rb b/activerecord/test/cases/named_scope_test.rb
index 444debd..9059d54 100644
--- a/activerecord/test/cases/named_scope_test.rb
+++ b/activerecord/test/cases/named_scope_test.rb
@@ -169,6 +169,19 @@ class NamedScopeTest < ActiveRecord::TestCase
assert_equal Topic.base.last(2), Topic.base.to_a.last(2)
end
+ def test_first_and_last_with_integer_uses_query_with_limit_rather_than_loading_all
+ assert_queries(2) do
+ base = Topic.base
+ base.first(2)
+ base.first(3)
+ end
+ assert_queries(2) do
+ base = Topic.base
+ base.last(2)
+ base.last(3)
+ end
+ end
+
def test_first_and_last_should_not_use_query_when_results_are_loaded
topics = Topic.base
topics.reload # force load