default.rb 5.1 KB
Newer Older
1
require 'active_support/concern'
J
Jon Leighton 已提交
2
require 'active_support/deprecation'
3 4 5 6 7 8 9 10

module ActiveRecord
  module Scoping
    module Default
      extend ActiveSupport::Concern

      included do
        # Stores the default scope for the class
J
Jon Leighton 已提交
11
        class_attribute :default_scopes, instance_writer: false
12 13 14 15
        self.default_scopes = []
      end

      module ClassMethods
16
        # Returns a scope for the model without the default_scope.
17 18 19 20 21 22 23 24 25 26
        #
        #   class Post < ActiveRecord::Base
        #     def self.default_scope
        #       where :published => true
        #     end
        #   end
        #
        #   Post.all          # Fires "SELECT * FROM posts WHERE published = true"
        #   Post.unscoped.all # Fires "SELECT * FROM posts"
        #
27
        # This method also accepts a block. All queries inside the block will
28 29 30 31 32 33
        # not use the default_scope:
        #
        #   Post.unscoped {
        #     Post.limit(10) # Fires "SELECT * FROM posts LIMIT 10"
        #   }
        #
34
        # It is recommended that you use the block form of unscoped because chaining
35 36 37
        # unscoped with <tt>scope</tt> does not work.  Assuming that
        # <tt>published</tt> is a <tt>scope</tt>, the following two statements
        # are equal: the default_scope is applied on both.
38
        #
39 40
        #   Post.unscoped.published
        #   Post.published
41 42 43 44 45 46 47 48 49 50 51 52 53 54
        def unscoped #:nodoc:
          block_given? ? relation.scoping { yield } : relation
        end

        def before_remove_const #:nodoc:
          self.current_scope = nil
        end

        protected

        # Use this macro in your model to set a default scope for all operations on
        # the model.
        #
        #   class Article < ActiveRecord::Base
J
Jon Leighton 已提交
55
        #     default_scope { where(:published => true) }
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
        #   end
        #
        #   Article.all # => SELECT * FROM articles WHERE published = true
        #
        # The <tt>default_scope</tt> is also applied while creating/building a record. It is not
        # applied while updating a record.
        #
        #   Article.new.published    # => true
        #   Article.create.published # => true
        #
        # (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 use multiple <tt>default_scope</tt> declarations in your model then they will
        # be merged together:
        #
        #   class Article < ActiveRecord::Base
J
Jon Leighton 已提交
73 74
        #     default_scope { where(:published => true) }
        #     default_scope { where(:rating => 'G') }
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
        #   end
        #
        #   Article.all # => SELECT * FROM articles WHERE published = true AND rating = 'G'
        #
        # This is also the case with inheritance and module includes where the parent or module
        # defines a <tt>default_scope</tt> and the child or including class defines a second one.
        #
        # If you need to do more complex things with a default scope, you can alternatively
        # define it as a class method:
        #
        #   class Article < ActiveRecord::Base
        #     def self.default_scope
        #       # Should return a scope, you can call 'super' here etc.
        #     end
        #   end
90
        def default_scope(scope = nil)
91
          scope = Proc.new if block_given?
J
Jon Leighton 已提交
92

93
          if scope.is_a?(Relation) || !scope.respond_to?(:call)
J
Jon Leighton 已提交
94 95 96 97 98 99 100 101
            ActiveSupport::Deprecation.warn(
              "Calling #default_scope without a block is deprecated. For example instead " \
              "of `default_scope where(color: 'red')`, please use " \
              "`default_scope { where(color: 'red') }`. (Alternatively you can just redefine " \
              "self.default_scope.)"
            )
          end

102 103 104 105
          self.default_scopes = default_scopes + [scope]
        end

        def build_default_scope #:nodoc:
106 107
          if !Base.is_a?(method(:default_scope).owner)
            # The user has defined their own default scope method, so call that
108 109 110 111
            evaluate_default_scope { default_scope }
          elsif default_scopes.any?
            evaluate_default_scope do
              default_scopes.inject(relation) do |default_scope, scope|
112
                if !scope.is_a?(Relation) && scope.respond_to?(:call)
J
Jon Leighton 已提交
113
                  default_scope.merge(unscoped { scope.call })
114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
                else
                  default_scope.merge(scope)
                end
              end
            end
          end
        end

        def ignore_default_scope? #:nodoc:
          Thread.current["#{self}_ignore_default_scope"]
        end

        def ignore_default_scope=(ignore) #:nodoc:
          Thread.current["#{self}_ignore_default_scope"] = ignore
        end

        # The ignore_default_scope flag is used to prevent an infinite recursion situation where
        # a default scope references a scope which has a default scope which references a scope...
        def evaluate_default_scope
          return if ignore_default_scope?

          begin
            self.ignore_default_scope = true
            yield
          ensure
            self.ignore_default_scope = false
          end
        end

      end
    end
  end
end