Permalink
Browse files

Added find_last_by dynamic finder [status:committed #762]

Signed-off-by: David Heinemeier Hansson <david@loudthinking.com>
  • Loading branch information...
miloops authored and dhh committed Sep 1, 2008
1 parent f3f7d16 commit 567392bff32acd5cf357565415d33c2662004cf5
View
@@ -287,6 +287,7 @@ def initialize(errors)
# It's even possible to use all the additional parameters to find. For example, the full interface for <tt>Payment.find_all_by_amount</tt>
# is actually <tt>Payment.find_all_by_amount(amount, options)</tt>. And the full interface to <tt>Person.find_by_user_name</tt> is
# actually <tt>Person.find_by_user_name(user_name, options)</tt>. So you could call <tt>Payment.find_all_by_amount(50, :order => "created_on")</tt>.
+ # Also you may call <tt>Payment.find_last_by_amount(amount, options)</tt> returning the last record matching that amount and options.
#
# The same dynamic finder style can be used to create the object if it doesn't already exist. This dynamic finder is called with
# <tt>find_or_create_by_</tt> and will return the object if it already exists and otherwise creates it, then returns it. Protected attributes won't be set unless they are given in a block. For example:
@@ -8,7 +8,8 @@ def self.match(method)
def initialize(method)
@finder = :find_initial
case method.to_s
- when /^find_(all_by|by)_([_a-zA-Z]\w*)$/
+ when /^find_(all_by|last_by|by)_([_a-zA-Z]\w*)$/
+ @finder = :find_last if $1 == 'last_by'
@finder = :find_every if $1 == 'all_by'
names = $2
when /^find_by_([_a-zA-Z]\w*)\!$/
@@ -197,11 +197,11 @@ def test_find_first_failing
first = Topic.find(:first, :conditions => "title = 'The First Topic!'")
assert_nil(first)
end
-
+
def test_first
assert_equal topics(:second).title, Topic.first(:conditions => "title = 'The Second Topic of the day'").title
end
-
+
def test_first_failing
assert_nil Topic.first(:conditions => "title = 'The Second Topic of the day!'")
end
@@ -418,7 +418,7 @@ def test_bind_arity
def test_named_bind_variables
assert_equal '1', bind(':a', :a => 1) # ' ruby-mode
assert_equal '1 1', bind(':a :a', :a => 1) # ' ruby-mode
-
+
assert_nothing_raised { bind("'+00:00'", :foo => "bar") }
assert_kind_of Firm, Company.find(:first, :conditions => ["name = :name", { :name => "37signals" }])
@@ -589,6 +589,38 @@ def test_find_by_two_attributes
assert_nil Topic.find_by_title_and_author_name("The First Topic", "Mary")
end
+ def test_find_last_by_one_attribute
+ assert_equal Topic.last, Topic.find_last_by_title(Topic.last.title)
+ assert_nil Topic.find_last_by_title("A title with no matches")
+ end
+
+ def test_find_last_by_one_attribute_caches_dynamic_finder
+ # ensure this test can run independently of order
+ class << Topic; self; end.send(:remove_method, :find_last_by_title) if Topic.public_methods.any? { |m| m.to_s == 'find_last_by_title' }
+ assert !Topic.public_methods.any? { |m| m.to_s == 'find_last_by_title' }
+ t = Topic.find_last_by_title(Topic.last)
+ assert Topic.public_methods.any? { |m| m.to_s == 'find_last_by_title' }
+ end
+
+ def test_find_last_by_invalid_method_syntax
+ assert_raises(NoMethodError) { Topic.fail_to_find_last_by_title("The First Topic") }
+ assert_raises(NoMethodError) { Topic.find_last_by_title?("The First Topic") }
+ end
+
+ def test_find_last_by_one_attribute_with_several_options
+ assert_equal accounts(:signals37), Account.find_last_by_credit_limit(50, :order => 'id DESC', :conditions => ['id != ?', 3])
+ end
+
+ def test_find_last_by_one_missing_attribute
+ assert_raises(NoMethodError) { Topic.find_last_by_undertitle("The Last Topic!") }
+ end
+
+ def test_find_last_by_two_attributes
+ topic = Topic.last
+ assert_equal topic, Topic.find_last_by_title_and_author_name(topic.title, topic.author_name)
+ assert_nil Topic.find_last_by_title_and_author_name(topic.title, "Anonymous")
+ end
+
def test_find_all_by_one_attribute
topics = Topic.find_all_by_content("Have a nice day")
assert_equal 2, topics.size
@@ -782,7 +814,7 @@ def test_find_or_create_should_set_protected_attributes_if_given_as_block
assert c.valid?
assert !c.new_record?
end
-
+
def test_dynamic_find_or_initialize_from_one_attribute_caches_method
class << Company; self; end.send(:remove_method, :find_or_initialize_by_name) if Company.public_methods.any? { |m| m.to_s == 'find_or_initialize_by_name' }
assert !Company.public_methods.any? { |m| m.to_s == 'find_or_initialize_by_name' }

0 comments on commit 567392b

Please sign in to comment.