public
Description: Ruby on Rails
Homepage: http://rubyonrails.org
Clone URL: git://github.com/rails/rails.git
Added find_last_by dynamic finder [status:committed #762]

Signed-off-by: David Heinemeier Hansson <david@loudthinking.com>
miloops (author)
Mon Sep 01 09:36:42 -0700 2008
dhh (committer)
Tue Sep 09 21:44:52 -0700 2008
commit  567392bff32acd5cf357565415d33c2662004cf5
tree    3b6df7201a8147fd621487721ab5b16bb15c62f1
parent  f3f7d166d8e7a1a2e15371f2870115406e1aaac2
...
287
288
289
 
290
291
292
...
287
288
289
290
291
292
293
0
@@ -287,6 +287,7 @@ module ActiveRecord #:nodoc:
0
   # 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>
0
   # is actually <tt>Payment.find_all_by_amount(amount, options)</tt>. And the full interface to <tt>Person.find_by_user_name</tt> is
0
   # 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>.
0
+  # Also you may call <tt>Payment.find_last_by_amount(amount, options)</tt> returning the last record matching that amount and options.
0
   #
0
   # The same dynamic finder style can be used to create the object if it doesn't already exist. This dynamic finder is called with
0
   # <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
9
10
11
 
 
12
13
14
...
8
9
10
 
11
12
13
14
15
0
@@ -8,7 +8,8 @@ module ActiveRecord
0
     def initialize(method)
0
       @finder = :find_initial
0
       case method.to_s
0
-      when /^find_(all_by|by)_([_a-zA-Z]\w*)$/
0
+      when /^find_(all_by|last_by|by)_([_a-zA-Z]\w*)$/
0
+        @finder = :find_last if $1 == 'last_by'
0
         @finder = :find_every if $1 == 'all_by'
0
         names = $2
0
       when /^find_by_([_a-zA-Z]\w*)\!$/
...
197
198
199
200
 
201
202
203
204
 
205
206
207
...
418
419
420
421
 
422
423
424
...
589
590
591
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
592
593
594
...
782
783
784
785
 
786
787
788
...
197
198
199
 
200
201
202
203
 
204
205
206
207
...
418
419
420
 
421
422
423
424
...
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
...
814
815
816
 
817
818
819
820
0
@@ -197,11 +197,11 @@ class FinderTest < ActiveRecord::TestCase
0
     first = Topic.find(:first, :conditions => "title = 'The First Topic!'")
0
     assert_nil(first)
0
   end
0
-  
0
+
0
   def test_first
0
     assert_equal topics(:second).title, Topic.first(:conditions => "title = 'The Second Topic of the day'").title
0
   end
0
-  
0
+
0
   def test_first_failing
0
     assert_nil Topic.first(:conditions => "title = 'The Second Topic of the day!'")
0
   end
0
@@ -418,7 +418,7 @@ class FinderTest < ActiveRecord::TestCase
0
   def test_named_bind_variables
0
     assert_equal '1', bind(':a', :a => 1) # ' ruby-mode
0
     assert_equal '1 1', bind(':a :a', :a => 1)  # ' ruby-mode
0
-    
0
+
0
     assert_nothing_raised { bind("'+00:00'", :foo => "bar") }
0
 
0
     assert_kind_of Firm, Company.find(:first, :conditions => ["name = :name", { :name => "37signals" }])
0
@@ -589,6 +589,38 @@ class FinderTest < ActiveRecord::TestCase
0
     assert_nil Topic.find_by_title_and_author_name("The First Topic", "Mary")
0
   end
0
 
0
+  def test_find_last_by_one_attribute
0
+    assert_equal Topic.last, Topic.find_last_by_title(Topic.last.title)
0
+    assert_nil Topic.find_last_by_title("A title with no matches")
0
+  end
0
+
0
+  def test_find_last_by_one_attribute_caches_dynamic_finder
0
+    # ensure this test can run independently of order
0
+    class << Topic; self; end.send(:remove_method, :find_last_by_title) if Topic.public_methods.any? { |m| m.to_s == 'find_last_by_title' }
0
+    assert !Topic.public_methods.any? { |m| m.to_s == 'find_last_by_title' }
0
+    t = Topic.find_last_by_title(Topic.last)
0
+    assert Topic.public_methods.any? { |m| m.to_s == 'find_last_by_title' }
0
+  end
0
+
0
+  def test_find_last_by_invalid_method_syntax
0
+    assert_raises(NoMethodError) { Topic.fail_to_find_last_by_title("The First Topic") }
0
+    assert_raises(NoMethodError) { Topic.find_last_by_title?("The First Topic") }
0
+  end
0
+
0
+  def test_find_last_by_one_attribute_with_several_options
0
+    assert_equal accounts(:signals37), Account.find_last_by_credit_limit(50, :order => 'id DESC', :conditions => ['id != ?', 3])
0
+  end
0
+
0
+  def test_find_last_by_one_missing_attribute
0
+    assert_raises(NoMethodError) { Topic.find_last_by_undertitle("The Last Topic!") }
0
+  end
0
+
0
+  def test_find_last_by_two_attributes
0
+    topic = Topic.last
0
+    assert_equal topic, Topic.find_last_by_title_and_author_name(topic.title, topic.author_name)
0
+    assert_nil Topic.find_last_by_title_and_author_name(topic.title, "Anonymous")
0
+  end
0
+
0
   def test_find_all_by_one_attribute
0
     topics = Topic.find_all_by_content("Have a nice day")
0
     assert_equal 2, topics.size
0
@@ -782,7 +814,7 @@ class FinderTest < ActiveRecord::TestCase
0
     assert c.valid?
0
     assert !c.new_record?
0
   end
0
-  
0
+
0
   def test_dynamic_find_or_initialize_from_one_attribute_caches_method
0
     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' }
0
     assert !Company.public_methods.any? { |m| m.to_s == 'find_or_initialize_by_name' }

Comments