Bring back support for passing a callable object to the default_scope macro. You can also just use a block.

This commit is contained in:
Jon Leighton 2011-04-18 23:35:22 +01:00
parent 6f84c73dc4
commit 019cd51a3f
4 changed files with 52 additions and 0 deletions

View File

@ -1,5 +1,16 @@
*Rails 3.1.0 (unreleased)* *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 * 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 scopes would be created which would implicitly contain the default scope, which would then
be impossible to get rid of via Model.unscoped. be impossible to get rid of via Model.unscoped.

View File

@ -1196,6 +1196,15 @@ MSG
# Article.new.published # => true # Article.new.published # => true
# Article.create.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 # If you need to do more complex things with a default scope, you can alternatively
# define it as a class method: # define it as a class method:
# #
@ -1233,6 +1242,7 @@ end
WARN WARN
end end
scope = Proc.new if block_given?
self.default_scopes = default_scopes.dup << scope self.default_scopes = default_scopes.dup << scope
end end
@ -1245,6 +1255,8 @@ end
default_scopes.inject(relation) do |default_scope, scope| default_scopes.inject(relation) do |default_scope, scope|
if scope.is_a?(Hash) if scope.is_a?(Hash)
default_scope.apply_finder_options(scope) default_scope.apply_finder_options(scope)
elsif !scope.is_a?(Relation) && scope.respond_to?(:call)
default_scope.merge(scope.call)
else else
default_scope.merge(scope) default_scope.merge(scope)
end end

View File

@ -312,6 +312,18 @@ class DefaultScopingTest < ActiveRecord::TestCase
assert_equal [developers(:david).becomes(ClassMethodDeveloperCalledDavid)], ClassMethodDeveloperCalledDavid.all assert_equal [developers(:david).becomes(ClassMethodDeveloperCalledDavid)], ClassMethodDeveloperCalledDavid.all
end 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 def test_default_scope_is_unscoped_on_find
assert_equal 1, DeveloperCalledDavid.count assert_equal 1, DeveloperCalledDavid.count
assert_equal 11, DeveloperCalledDavid.unscoped.count assert_equal 11, DeveloperCalledDavid.unscoped.count

View File

@ -1,3 +1,5 @@
require 'ostruct'
module DeveloperProjectsAssociationExtension module DeveloperProjectsAssociationExtension
def find_most_recent def find_most_recent
find(:first, :order => "id DESC") find(:first, :order => "id DESC")
@ -102,6 +104,21 @@ class DeveloperCalledDavid < ActiveRecord::Base
default_scope where("name = 'David'") default_scope where("name = 'David'")
end 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 class ClassMethodDeveloperCalledDavid < ActiveRecord::Base
self.table_name = 'developers' self.table_name = 'developers'