Skip to content

Commit

Permalink
Bring back support for passing a callable object to the default_scope…
Browse files Browse the repository at this point in the history
… macro. You can also just use a block.
  • Loading branch information
jonleighton committed Apr 18, 2011
1 parent 6f84c73 commit 019cd51
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 0 deletions.
11 changes: 11 additions & 0 deletions activerecord/CHANGELOG
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
12 changes: 12 additions & 0 deletions activerecord/lib/active_record/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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:
#
Expand Down Expand Up @@ -1233,6 +1242,7 @@ def self.default_scope
WARN
end

scope = Proc.new if block_given?
self.default_scopes = default_scopes.dup << scope
end

Expand All @@ -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
Expand Down
12 changes: 12 additions & 0 deletions activerecord/test/cases/relation_scoping_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
17 changes: 17 additions & 0 deletions activerecord/test/models/developer.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
require 'ostruct'

module DeveloperProjectsAssociationExtension
def find_most_recent
find(:first, :order => "id DESC")
Expand Down Expand Up @@ -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'

Expand Down

0 comments on commit 019cd51

Please sign in to comment.