Skip to content
Browse files

Bring back support for passing a callable object to the default_scope…

… macro. You can also just use a block.
  • Loading branch information...
1 parent 6f84c73 commit 019cd51a3f36ec7631bf1b63c069e62a3b5185d4 @jonleighton jonleighton committed Apr 18, 2011
View
11 activerecord/CHANGELOG
@@ -1,5 +1,16 @@
*Rails 3.1.0 (unreleased)*
+* default_scope can take a block, lambda, or any other object which responds to `call` for lazy
+ evaluation:
+
+ default_scope { ... }
+ default_scope lambda { ... }
+ default_scope method(:foo)
+
+ This feature was originally implemented by Tim Morgan, but was then removed in favour of
+ defining a 'default_scope' class method, but has now been added back in by Jon Leighton.
+ The relevant lighthouse ticket is #1812.
+
* Default scopes are now evaluated at the latest possible moment, to avoid problems where
scopes would be created which would implicitly contain the default scope, which would then
be impossible to get rid of via Model.unscoped.
View
12 activerecord/lib/active_record/base.rb
@@ -1196,6 +1196,15 @@ def current_scope=(scope) #:nodoc:
# Article.new.published # => true
# Article.create.published # => true
#
+ # You can also use <tt>default_scope</tt> with a block, in order to have it lazily evaluated:
+ #
+ # class Article < ActiveRecord::Base
+ # default_scope { where(:published_at => Time.now - 1.week) }
+ # end
+ #
+ # (You can also pass any object which responds to <tt>call</tt> to the <tt>default_scope</tt>
+ # macro, and it will be called when building the default scope.)
+ #
# If you need to do more complex things with a default scope, you can alternatively
# define it as a class method:
#
@@ -1233,6 +1242,7 @@ def self.default_scope
WARN
end
+ scope = Proc.new if block_given?
self.default_scopes = default_scopes.dup << scope
end
@@ -1245,6 +1255,8 @@ def build_default_scope #:nodoc:
default_scopes.inject(relation) do |default_scope, scope|
if scope.is_a?(Hash)
default_scope.apply_finder_options(scope)
+ elsif !scope.is_a?(Relation) && scope.respond_to?(:call)
+ default_scope.merge(scope.call)
else
default_scope.merge(scope)
end
View
12 activerecord/test/cases/relation_scoping_test.rb
@@ -312,6 +312,18 @@ def test_default_scope_as_class_method
assert_equal [developers(:david).becomes(ClassMethodDeveloperCalledDavid)], ClassMethodDeveloperCalledDavid.all
end
+ def test_default_scope_with_lambda
+ assert_equal [developers(:david).becomes(LazyLambdaDeveloperCalledDavid)], LazyLambdaDeveloperCalledDavid.all
+ end
+
+ def test_default_scope_with_block
+ assert_equal [developers(:david).becomes(LazyBlockDeveloperCalledDavid)], LazyBlockDeveloperCalledDavid.all
+ end
+
+ def test_default_scope_with_callable
+ assert_equal [developers(:david).becomes(CallableDeveloperCalledDavid)], CallableDeveloperCalledDavid.all
+ end
+
def test_default_scope_is_unscoped_on_find
assert_equal 1, DeveloperCalledDavid.count
assert_equal 11, DeveloperCalledDavid.unscoped.count
View
17 activerecord/test/models/developer.rb
@@ -1,3 +1,5 @@
+require 'ostruct'
+
module DeveloperProjectsAssociationExtension
def find_most_recent
find(:first, :order => "id DESC")
@@ -102,6 +104,21 @@ class DeveloperCalledDavid < ActiveRecord::Base
default_scope where("name = 'David'")
end
+class LazyLambdaDeveloperCalledDavid < ActiveRecord::Base
+ self.table_name = 'developers'
+ default_scope lambda { where(:name => 'David') }
+end
+
+class LazyBlockDeveloperCalledDavid < ActiveRecord::Base
+ self.table_name = 'developers'
+ default_scope { where(:name => 'David') }
+end
+
+class CallableDeveloperCalledDavid < ActiveRecord::Base
+ self.table_name = 'developers'
+ default_scope OpenStruct.new(:call => where(:name => 'David'))
+end
+
class ClassMethodDeveloperCalledDavid < ActiveRecord::Base
self.table_name = 'developers'

0 comments on commit 019cd51

Please sign in to comment.
Something went wrong with that request. Please try again.