Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 143 lines (130 sloc) 5.081 kb
2b22564 @jonleighton Move DefaultScope and NamedScope under Scoping
jonleighton authored
1 require 'active_support/concern'
2
3 module ActiveRecord
4 module Scoping
5 module Default
6 extend ActiveSupport::Concern
7
8 included do
9 # Stores the default scope for the class
10 class_attribute :default_scopes, :instance_writer => false
11 self.default_scopes = []
12 end
13
14 module ClassMethods
e149d50 @htanata Improve doc for ActiveRecord::Base.unscoped.
htanata authored
15 # Returns a scope for the model without the default_scope.
2b22564 @jonleighton Move DefaultScope and NamedScope under Scoping
jonleighton authored
16 #
17 # class Post < ActiveRecord::Base
18 # def self.default_scope
19 # where :published => true
20 # end
21 # end
22 #
23 # Post.all # Fires "SELECT * FROM posts WHERE published = true"
24 # Post.unscoped.all # Fires "SELECT * FROM posts"
25 #
e149d50 @htanata Improve doc for ActiveRecord::Base.unscoped.
htanata authored
26 # This method also accepts a block. All queries inside the block will
2b22564 @jonleighton Move DefaultScope and NamedScope under Scoping
jonleighton authored
27 # not use the default_scope:
28 #
29 # Post.unscoped {
30 # Post.limit(10) # Fires "SELECT * FROM posts LIMIT 10"
31 # }
32 #
e149d50 @htanata Improve doc for ActiveRecord::Base.unscoped.
htanata authored
33 # It is recommended to use the block form of unscoped because chaining
34 # unscoped with <tt>scope</tt> does not work. Assuming that
35 # <tt>published</tt> is a <tt>scope</tt>, the following two statements
36 # are equal: the default_scope is applied on both.
2b22564 @jonleighton Move DefaultScope and NamedScope under Scoping
jonleighton authored
37 #
e149d50 @htanata Improve doc for ActiveRecord::Base.unscoped.
htanata authored
38 # Post.unscoped.published
39 # Post.published
2b22564 @jonleighton Move DefaultScope and NamedScope under Scoping
jonleighton authored
40 def unscoped #:nodoc:
41 block_given? ? relation.scoping { yield } : relation
42 end
43
44 def before_remove_const #:nodoc:
45 self.current_scope = nil
46 end
47
48 protected
49
50 # Use this macro in your model to set a default scope for all operations on
51 # the model.
52 #
53 # class Article < ActiveRecord::Base
54 # default_scope where(:published => true)
55 # end
56 #
57 # Article.all # => SELECT * FROM articles WHERE published = true
58 #
59 # The <tt>default_scope</tt> is also applied while creating/building a record. It is not
60 # applied while updating a record.
61 #
62 # Article.new.published # => true
63 # Article.create.published # => true
64 #
65 # You can also use <tt>default_scope</tt> with a block, in order to have it lazily evaluated:
66 #
67 # class Article < ActiveRecord::Base
68 # default_scope { where(:published_at => Time.now - 1.week) }
69 # end
70 #
71 # (You can also pass any object which responds to <tt>call</tt> to the <tt>default_scope</tt>
72 # macro, and it will be called when building the default scope.)
73 #
74 # If you use multiple <tt>default_scope</tt> declarations in your model then they will
75 # be merged together:
76 #
77 # class Article < ActiveRecord::Base
78 # default_scope where(:published => true)
79 # default_scope where(:rating => 'G')
80 # end
81 #
82 # Article.all # => SELECT * FROM articles WHERE published = true AND rating = 'G'
83 #
84 # This is also the case with inheritance and module includes where the parent or module
85 # defines a <tt>default_scope</tt> and the child or including class defines a second one.
86 #
87 # If you need to do more complex things with a default scope, you can alternatively
88 # define it as a class method:
89 #
90 # class Article < ActiveRecord::Base
91 # def self.default_scope
92 # # Should return a scope, you can call 'super' here etc.
93 # end
94 # end
95 def default_scope(scope = {})
96 scope = Proc.new if block_given?
97 self.default_scopes = default_scopes + [scope]
98 end
99
100 def build_default_scope #:nodoc:
101 if method(:default_scope).owner != ActiveRecord::Scoping::Default::ClassMethods
102 evaluate_default_scope { default_scope }
103 elsif default_scopes.any?
104 evaluate_default_scope do
105 default_scopes.inject(relation) do |default_scope, scope|
106 if scope.is_a?(Hash)
107 default_scope.apply_finder_options(scope)
108 elsif !scope.is_a?(Relation) && scope.respond_to?(:call)
109 default_scope.merge(scope.call)
110 else
111 default_scope.merge(scope)
112 end
113 end
114 end
115 end
116 end
117
118 def ignore_default_scope? #:nodoc:
119 Thread.current["#{self}_ignore_default_scope"]
120 end
121
122 def ignore_default_scope=(ignore) #:nodoc:
123 Thread.current["#{self}_ignore_default_scope"] = ignore
124 end
125
126 # The ignore_default_scope flag is used to prevent an infinite recursion situation where
127 # a default scope references a scope which has a default scope which references a scope...
128 def evaluate_default_scope
129 return if ignore_default_scope?
130
131 begin
132 self.ignore_default_scope = true
133 yield
134 ensure
135 self.ignore_default_scope = false
136 end
137 end
138
139 end
140 end
141 end
142 end
Something went wrong with that request. Please try again.