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

git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@9012 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
This commit is contained in:
David Heinemeier Hansson 2008-03-12 21:26:02 +00:00
parent 8cc28daad6
commit d5a4d5abb4
3 changed files with 67 additions and 1 deletions

@ -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]

@ -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])

@ -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?