diff --git a/activerecord/lib/active_record/named_scope.rb b/activerecord/lib/active_record/named_scope.rb
index 83043c2..f4c85a0 100644
--- a/activerecord/lib/active_record/named_scope.rb
+++ b/activerecord/lib/active_record/named_scope.rb
@@ -69,6 +69,12 @@ module ActiveRecord
# end
# end
#
+ # If you're creating a named \scope that will only return one object then you can set the option :first => true. This means the named \scope will return the actual object rather than the proxy association. (This is equivalent to chaining the named \scope to first.)
+ #
+ # class Book
+ # named_scope :last_read, :first => true, :order => "last_read DESC", :limit => 1
+ # end
+ #
#
# For testing complex named \scopes, you can examine the scoping options using the
# proxy_options method on the proxy itself.
@@ -82,6 +88,8 @@ module ActiveRecord
# expected_options = { :conditions => { :colored => 'red' } }
# assert_equal expected_options, Shirt.colored('red').proxy_options
def named_scope(name, options = {}, &block)
+ select_first = options.delete(:first) if options.is_a? Hash
+
name = name.to_sym
scopes[name] = lambda do |parent_scope, *args|
Scope.new(parent_scope, case options
@@ -93,12 +101,16 @@ module ActiveRecord
end
(class << self; self end).instance_eval do
define_method name do |*args|
- scopes[name].call(self, *args)
+ if select_first
+ scopes[name].call(self, *args).first
+ else
+ scopes[name].call(self, *args)
+ end
end
end
end
end
-
+
class Scope
attr_reader :proxy_scope, :proxy_options
NON_DELEGATE_METHODS = %w(nil? send object_id class extend find size count sum average maximum minimum paginate first last empty? any? respond_to?).to_set
diff --git a/activerecord/test/cases/named_scope_test.rb b/activerecord/test/cases/named_scope_test.rb
index 64e8997..e9577ae 100644
--- a/activerecord/test/cases/named_scope_test.rb
+++ b/activerecord/test/cases/named_scope_test.rb
@@ -277,4 +277,10 @@ class NamedScopeTest < ActiveRecord::TestCase
post = Post.find(1)
assert_equal post.comments.size, Post.scoped(:joins => join).scoped(:joins => join, :conditions => "posts.id = #{post.id}").size
end
+
+ def test_first_argument_should_return_object_not_array
+ last_read_topic = Topic.first :order => "last_read DESC"
+ assert_equal last_read_topic, Topic.last_read
+ end
+
end
diff --git a/activerecord/test/models/topic.rb b/activerecord/test/models/topic.rb
index 39ca1bf..6b6c51f 100644
--- a/activerecord/test/models/topic.rb
+++ b/activerecord/test/models/topic.rb
@@ -31,7 +31,8 @@ class Topic < ActiveRecord::Base
end
named_scope :named_extension, :extend => NamedExtension
named_scope :multiple_extensions, :extend => [MultipleExtensionTwo, MultipleExtensionOne]
-
+ named_scope :last_read, :first => true, :order => "last_read DESC", :limit => 1
+
has_many :replies, :dependent => :destroy, :foreign_key => "parent_id"
serialize :content