aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord
diff options
context:
space:
mode:
authorDavid Heinemeier Hansson <david@loudthinking.com>2008-03-12 21:26:02 +0000
committerDavid Heinemeier Hansson <david@loudthinking.com>2008-03-12 21:26:02 +0000
commitd5a4d5abb41c50f96b554374b937ffe49d472d7f (patch)
tree0fdec0b07287acabd90fb0af965625aff8cb76fc /activerecord
parent8cc28daad61dca238e00c92221492dca82e69dc6 (diff)
downloadrails-d5a4d5abb41c50f96b554374b937ffe49d472d7f.tar.gz
rails-d5a4d5abb41c50f96b554374b937ffe49d472d7f.tar.bz2
rails-d5a4d5abb41c50f96b554374b937ffe49d472d7f.zip
Added ActiveRecord::Base.find(:last) (closes #11338) [miloops]
git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@9012 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
Diffstat (limited to 'activerecord')
-rw-r--r--activerecord/CHANGELOG2
-rwxr-xr-xactiverecord/lib/active_record/base.rb39
-rwxr-xr-xactiverecord/test/cases/base_test.rb27
3 files changed, 67 insertions, 1 deletions
diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG
index d852a6d3d0..3f7eb54a21 100644
--- a/activerecord/CHANGELOG
+++ b/activerecord/CHANGELOG
@@ -1,5 +1,7 @@
*SVN*
+* Added ActiveRecord::Base.find(:last) #11338 [miloops]
+
* test_native_types expects DateTime.local_offset instead of DateTime.now.offset; fixes test breakage due to dst transition [Geoff Buesing]
* Add :readonly option to HasManyThrough associations. #11156 [miloops]
diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb
index d5da9408f3..a30036f561 100755
--- a/activerecord/lib/active_record/base.rb
+++ b/activerecord/lib/active_record/base.rb
@@ -430,12 +430,14 @@ module ActiveRecord #:nodoc:
@@schema_format = :ruby
class << self # Class methods
- # Find operates with three different retrieval approaches:
+ # Find operates with four different retrieval approaches:
#
# * Find by id: This can either be a specific id (1), a list of ids (1, 5, 6), or an array of ids ([5, 6, 10]).
# If no record can be found for all of the listed ids, then RecordNotFound will be raised.
# * Find first: This will return the first record matched by the options used. These options can either be specific
# conditions or merely an order. If no record can be matched, nil is returned.
+ # * Find last: This will return the last record matched by the options used. These options can either be specific
+ # conditions or merely an order. If no record can be matched, nil is returned.
# * Find all: This will return all the records matched by the options used. If no records are found, an empty array is returned.
#
# All approaches accept an options hash as their last parameter. The options are:
@@ -475,6 +477,11 @@ module ActiveRecord #:nodoc:
# Person.find(:first, :conditions => [ "user_name = ?", user_name])
# Person.find(:first, :order => "created_on DESC", :offset => 5)
#
+ # Examples for find last:
+ # Person.find(:last) # returns the last object fetched by SELECT * FROM people
+ # Person.find(:last, :conditions => [ "user_name = ?", user_name])
+ # Person.find(:last, :order => "created_on DESC", :offset => 5)
+ #
# Examples for find all:
# Person.find(:all) # returns an array of objects for all the rows fetched by SELECT * FROM people
# Person.find(:all, :conditions => [ "category IN (?)", categories], :limit => 50)
@@ -499,6 +506,7 @@ module ActiveRecord #:nodoc:
case args.first
when :first then find_initial(options)
+ when :last then find_last(options)
when :all then find_every(options)
else find_from_ids(args, options)
end
@@ -1236,6 +1244,35 @@ module ActiveRecord #:nodoc:
find_every(options).first
end
+ def find_last(options)
+ order = options[:order]
+
+ if order
+ order = reverse_sql_order(order)
+ elsif !scoped?(:find, :order)
+ order = "#{table_name}.#{primary_key} DESC"
+ end
+
+ if scoped?(:find, :order)
+ 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 }))
+ end
+
+ def reverse_sql_order(order_query)
+ reversed_query = order_query.split(/,/).each { |s|
+ if s.match(/\s(asc|ASC)$/)
+ s.gsub!(/\s(asc|ASC)$/, ' DESC')
+ elsif s.match(/\s(desc|DESC)$/)
+ s.gsub!(/\s(desc|DESC)$/, ' ASC')
+ elsif !s.match(/\s(asc|ASC|desc|DESC)$/)
+ s.concat(' DESC')
+ end
+ }.join(',')
+ end
+
def find_every(options)
include_associations = merge_includes(scope(:find, :include), options[:include])
diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb
index f2720130e2..91cfdee0cf 100755
--- a/activerecord/test/cases/base_test.rb
+++ b/activerecord/test/cases/base_test.rb
@@ -1616,6 +1616,33 @@ class BasicsTest < ActiveRecord::TestCase
end
end
+ def test_find_last
+ last = Developer.find :last
+ assert_equal last, Developer.find(:first, :order => 'id desc')
+ end
+
+ def test_find_ordered_last
+ last = Developer.find :last, :order => 'developers.salary ASC'
+ assert_equal last, Developer.find(:all, :order => 'developers.salary ASC').last
+ end
+
+ def test_find_reverse_ordered_last
+ last = Developer.find :last, :order => 'developers.salary DESC'
+ assert_equal last, Developer.find(:all, :order => 'developers.salary DESC').last
+ end
+
+ def test_find_multiple_ordered_last
+ last = Developer.find :last, :order => 'developers.name, developers.salary DESC'
+ assert_equal last, Developer.find(:all, :order => 'developers.name, developers.salary DESC').last
+ end
+
+ def test_find_scoped_ordered_last
+ last_developer = Developer.with_scope(:find => { :order => 'developers.salary ASC' }) do
+ Developer.find(:last)
+ end
+ assert_equal last_developer, Developer.find(:all, :order => 'developers.salary ASC').last
+ end
+
def test_abstract_class
assert !ActiveRecord::Base.abstract_class?
assert LoosePerson.abstract_class?