Permalink
Browse files

Added ActiveRecord::Base.find(:last) (closes #11338) [miloops]

git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@9012 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
  • Loading branch information...
1 parent 8cc28da commit d5a4d5abb41c50f96b554374b937ffe49d472d7f @dhh dhh committed Mar 12, 2008
Showing with 67 additions and 1 deletion.
  1. +2 −0 activerecord/CHANGELOG
  2. +38 −1 activerecord/lib/active_record/base.rb
  3. +27 −0 activerecord/test/cases/base_test.rb
View
2 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]
View
39 activerecord/lib/active_record/base.rb
@@ -430,12 +430,14 @@ def self.reset_subclasses #: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 @@ class << self # Class methods
# 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 @@ def find(*args)
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 @@ def find_initial(options)
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])
View
27 activerecord/test/cases/base_test.rb
@@ -1616,6 +1616,33 @@ def test_scoped_find_order_including_has_many_association
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?

0 comments on commit d5a4d5a

Please sign in to comment.