<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array">
    <added>
      <filename>activerecord/lib/active_record/dynamic_scope_match.rb</filename>
    </added>
  </added>
  <modified type="array">
    <modified>
      <diff>@@ -1,5 +1,7 @@
 *2.3.0/3.0*
 
+* Added dynamic scopes ala dynamic finders #1648 [Yaroslav Markin]
+
 * Fixed that ActiveRecord::Base#new_record? should return false (not nil) for existing records #1219 [Yaroslav Markin]
 
 * I18n the word separator for error messages. Introduces the activerecord.errors.format.separator translation key.  #1294 [Akira Matsuda]</diff>
      <filename>activerecord/CHANGELOG</filename>
    </modified>
    <modified>
      <diff>@@ -51,6 +51,7 @@ module ActiveRecord
   autoload :Callbacks, 'active_record/callbacks'
   autoload :Dirty, 'active_record/dirty'
   autoload :DynamicFinderMatch, 'active_record/dynamic_finder_match'
+  autoload :DynamicScopeMatch, 'active_record/dynamic_scope_match'
   autoload :Migration, 'active_record/migration'
   autoload :Migrator, 'active_record/migration'
   autoload :NamedScope, 'active_record/named_scope'</diff>
      <filename>activerecord/lib/active_record.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1456,7 +1456,10 @@ module ActiveRecord #:nodoc:
       def respond_to?(method_id, include_private = false)
         if match = DynamicFinderMatch.match(method_id)
           return true if all_attributes_exists?(match.attribute_names)
+        elsif match = DynamicScopeMatch.match(method_id)
+          return true if all_attributes_exists?(match.attribute_names)
         end
+        
         super
       end
 
@@ -1809,7 +1812,11 @@ module ActiveRecord #:nodoc:
         # This also enables you to initialize a record if it is not found, such as find_or_initialize_by_amount(amount)
         # or find_or_create_by_user_and_password(user, password).
         #
-        # Each dynamic finder or initializer/creator is also defined in the class after it is first invoked, so that future
+        # Also enables dynamic scopes like scoped_by_user_name(user_name) and scoped_by_user_name_and_password(user_name, password) that
+        # are turned into scoped(:conditions =&gt; [&quot;user_name = ?&quot;, user_name]) and scoped(:conditions =&gt; [&quot;user_name = ? AND password = ?&quot;, user_name, password])
+        # respectively.
+        #
+        # Each dynamic finder, scope or initializer/creator is also defined in the class after it is first invoked, so that future
         # attempts to use it do not run through method_missing.
         def method_missing(method_id, *arguments, &amp;block)
           if match = DynamicFinderMatch.match(method_id)
@@ -1868,6 +1875,22 @@ module ActiveRecord #:nodoc:
               }, __FILE__, __LINE__
               send(method_id, *arguments, &amp;block)
             end
+          elsif match = DynamicScopeMatch.match(method_id)
+            attribute_names = match.attribute_names
+            super unless all_attributes_exists?(attribute_names)
+            if match.scope?
+              self.class_eval %{
+                def self.#{method_id}(*args)                        # def self.scoped_by_user_name_and_password(*args)
+                  options = args.extract_options!                   #   options = args.extract_options!
+                  attributes = construct_attributes_from_arguments( #   attributes = construct_attributes_from_arguments(
+                    [:#{attribute_names.join(',:')}], args          #     [:user_name, :password], args
+                  )                                                 #   )
+                                                                    # 
+                  scoped(:conditions =&gt; attributes)                 #   scoped(:conditions =&gt; attributes)
+                end                                                 # end
+              }, __FILE__, __LINE__
+              send(method_id, *arguments)
+            end
           else
             super
           end</diff>
      <filename>activerecord/lib/active_record/base.rb</filename>
    </modified>
    <modified>
      <diff>@@ -278,3 +278,23 @@ class NamedScopeTest &lt; ActiveRecord::TestCase
     assert_equal post.comments.size, Post.scoped(:joins =&gt; join).scoped(:joins =&gt; join, :conditions =&gt; &quot;posts.id = #{post.id}&quot;).size
   end
 end
+
+class DynamicScopeMatchTest &lt; ActiveRecord::TestCase  
+  def test_scoped_by_no_match
+    assert_nil ActiveRecord::DynamicScopeMatch.match(&quot;not_scoped_at_all&quot;)
+  end
+
+  def test_scoped_by
+    match = ActiveRecord::DynamicScopeMatch.match(&quot;scoped_by_age_and_sex_and_location&quot;)
+    assert_not_nil match
+    assert match.scope?
+    assert_equal %w(age sex location), match.attribute_names
+  end
+end
+
+class DynamicScopeTest &lt; ActiveRecord::TestCase
+  def test_dynamic_scope
+    assert_equal Post.scoped_by_author_id(1).find(1), Post.find(1)
+    assert_equal Post.scoped_by_author_id_and_title(1, &quot;Welcome to the weblog&quot;).first, Post.find(:first, :conditions =&gt; { :author_id =&gt; 1, :title =&gt; &quot;Welcome to the weblog&quot;})
+  end
+end</diff>
      <filename>activerecord/test/cases/named_scope_test.rb</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>1fb275541a58e6a2100261c6117e96e6c014cc6c</id>
    </parent>
  </parents>
  <author>
    <name>Yaroslav Markin</name>
    <login>yaroslav</login>
    <email>yaroslav@markin.net</email>
  </author>
  <url>http://github.com/rails/rails/commit/66ee5890c5f21995b7fe0c486547f1287afe2b55</url>
  <id>66ee5890c5f21995b7fe0c486547f1287afe2b55</id>
  <committed-date>2008-12-28T11:52:46-08:00</committed-date>
  <authored-date>2008-12-28T11:25:55-08:00</authored-date>
  <message>Introduce dynamic scopes for ActiveRecord: you can now use class methods like scoped_by_user_name(user_name) and scoped_by_user_name_and_password(user_name, password) that will use the scoped method with attributes you supply. [#1648 state:committed]

Signed-off-by: David Heinemeier Hansson &lt;david@loudthinking.com&gt;</message>
  <tree>81b44888712ff3ea7093209b3235858948354d25</tree>
  <committer>
    <name>David Heinemeier Hansson</name>
    <login>dhh</login>
    <email>david@loudthinking.com</email>
  </committer>
</commit>
