Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 193 lines (177 sloc) 7.444 kb
081ddb6 risk danger olson Merge the has_finder gem, renamed as 'named_scope'. Closes #11404 [nkall...
technoweenie authored
1 module ActiveRecord
2 module NamedScope
ff894b5 Update rdoc: all is not a named_scope [#1959 state:committed]
Zack Hobson authored
3 # All subclasses of ActiveRecord::Base have one named scope:
6ef3546 Pratik Merge docrails
lifo authored
4 # * <tt>scoped</tt> - which allows for the creation of anonymous \scopes, on the fly: <tt>Shirt.scoped(:conditions => {:color => 'red'}).scoped(:include => :washing_instructions)</tt>
081ddb6 risk danger olson Merge the has_finder gem, renamed as 'named_scope'. Closes #11404 [nkall...
technoweenie authored
5 #
6ef3546 Pratik Merge docrails
lifo authored
6 # These anonymous \scopes tend to be useful when procedurally generating complex queries, where passing
081ddb6 risk danger olson Merge the has_finder gem, renamed as 'named_scope'. Closes #11404 [nkall...
technoweenie authored
7 # intermediate values (scopes) around as first-class objects is convenient.
ff894b5 Update rdoc: all is not a named_scope [#1959 state:committed]
Zack Hobson authored
8 #
9 # You can define a scope that applies to all finders using ActiveRecord::Base.default_scope.
081ddb6 risk danger olson Merge the has_finder gem, renamed as 'named_scope'. Closes #11404 [nkall...
technoweenie authored
10 def self.included(base)
11 base.class_eval do
12 extend ClassMethods
13 named_scope :scoped, lambda { |scope| scope }
14 end
15 end
16
17 module ClassMethods
18 def scopes
19 read_inheritable_attribute(:scopes) || write_inheritable_attribute(:scopes, {})
20 end
21
22 # Adds a class method for retrieving and querying objects. A scope represents a narrowing of a database query,
23 # such as <tt>:conditions => {:color => :red}, :select => 'shirts.*', :include => :washing_instructions</tt>.
24 #
25 # class Shirt < ActiveRecord::Base
26 # named_scope :red, :conditions => {:color => 'red'}
27 # named_scope :dry_clean_only, :joins => :washing_instructions, :conditions => ['washing_instructions.dry_clean_only = ?', true]
28 # end
29 #
6ef3546 Pratik Merge docrails
lifo authored
30 # The above calls to <tt>named_scope</tt> define class methods Shirt.red and Shirt.dry_clean_only. Shirt.red,
081ddb6 risk danger olson Merge the has_finder gem, renamed as 'named_scope'. Closes #11404 [nkall...
technoweenie authored
31 # in effect, represents the query <tt>Shirt.find(:all, :conditions => {:color => 'red'})</tt>.
32 #
6ef3546 Pratik Merge docrails
lifo authored
33 # Unlike <tt>Shirt.find(...)</tt>, however, the object returned by Shirt.red is not an Array; it resembles the association object
081ddb6 risk danger olson Merge the has_finder gem, renamed as 'named_scope'. Closes #11404 [nkall...
technoweenie authored
34 # constructed by a <tt>has_many</tt> declaration. For instance, you can invoke <tt>Shirt.red.find(:first)</tt>, <tt>Shirt.red.count</tt>,
35 # <tt>Shirt.red.find(:all, :conditions => {:size => 'small'})</tt>. Also, just
6ef3546 Pratik Merge docrails
lifo authored
36 # as with the association objects, named \scopes act like an Array, implementing Enumerable; <tt>Shirt.red.each(&block)</tt>,
37 # <tt>Shirt.red.first</tt>, and <tt>Shirt.red.inject(memo, &block)</tt> all behave as if Shirt.red really was an Array.
081ddb6 risk danger olson Merge the has_finder gem, renamed as 'named_scope'. Closes #11404 [nkall...
technoweenie authored
38 #
6ef3546 Pratik Merge docrails
lifo authored
39 # These named \scopes are composable. For instance, <tt>Shirt.red.dry_clean_only</tt> will produce all shirts that are both red and dry clean only.
081ddb6 risk danger olson Merge the has_finder gem, renamed as 'named_scope'. Closes #11404 [nkall...
technoweenie authored
40 # Nested finds and calculations also work with these compositions: <tt>Shirt.red.dry_clean_only.count</tt> returns the number of garments
41 # for which these criteria obtain. Similarly with <tt>Shirt.red.dry_clean_only.average(:thread_count)</tt>.
42 #
39e1ac6 Pratik Merge docrails
lifo authored
43 # All \scopes are available as class methods on the ActiveRecord::Base descendant upon which the \scopes were defined. But they are also available to
081ddb6 risk danger olson Merge the has_finder gem, renamed as 'named_scope'. Closes #11404 [nkall...
technoweenie authored
44 # <tt>has_many</tt> associations. If,
45 #
46 # class Person < ActiveRecord::Base
47 # has_many :shirts
48 # end
49 #
50 # then <tt>elton.shirts.red.dry_clean_only</tt> will return all of Elton's red, dry clean
51 # only shirts.
52 #
6ef3546 Pratik Merge docrails
lifo authored
53 # Named \scopes can also be procedural:
081ddb6 risk danger olson Merge the has_finder gem, renamed as 'named_scope'. Closes #11404 [nkall...
technoweenie authored
54 #
55 # class Shirt < ActiveRecord::Base
56 # named_scope :colored, lambda { |color|
57 # { :conditions => { :color => color } }
58 # }
59 # end
60 #
61 # In this example, <tt>Shirt.colored('puce')</tt> finds all puce shirts.
62 #
6ef3546 Pratik Merge docrails
lifo authored
63 # Named \scopes can also have extensions, just as with <tt>has_many</tt> declarations:
081ddb6 risk danger olson Merge the has_finder gem, renamed as 'named_scope'. Closes #11404 [nkall...
technoweenie authored
64 #
65 # class Shirt < ActiveRecord::Base
66 # named_scope :red, :conditions => {:color => 'red'} do
67 # def dom_id
68 # 'red_shirts'
69 # end
70 # end
71 # end
72 #
0b8b582 Duncan Beevers Add test for named_scope#proxy_options. [#97 state:resolved]
duncanbeevers authored
73 #
6ef3546 Pratik Merge docrails
lifo authored
74 # For testing complex named \scopes, you can examine the scoping options using the
0b8b582 Duncan Beevers Add test for named_scope#proxy_options. [#97 state:resolved]
duncanbeevers authored
75 # <tt>proxy_options</tt> method on the proxy itself.
76 #
77 # class Shirt < ActiveRecord::Base
78 # named_scope :colored, lambda { |color|
79 # { :conditions => { :color => color } }
80 # }
81 # end
82 #
83 # expected_options = { :conditions => { :colored => 'red' } }
84 # assert_equal expected_options, Shirt.colored('red').proxy_options
081ddb6 risk danger olson Merge the has_finder gem, renamed as 'named_scope'. Closes #11404 [nkall...
technoweenie authored
85 def named_scope(name, options = {}, &block)
1afae84 Tarmo Tänav Fixed that scopes defined with a string name could not be composed
tarmo authored
86 name = name.to_sym
081ddb6 risk danger olson Merge the has_finder gem, renamed as 'named_scope'. Closes #11404 [nkall...
technoweenie authored
87 scopes[name] = lambda do |parent_scope, *args|
88 Scope.new(parent_scope, case options
89 when Hash
90 options
91 when Proc
17f336e Jeremy Kemper Revert "Methods invoked within named scope Procs should respect the scop...
jeremy authored
92 options.call(*args)
081ddb6 risk danger olson Merge the has_finder gem, renamed as 'named_scope'. Closes #11404 [nkall...
technoweenie authored
93 end, &block)
94 end
95 (class << self; self end).instance_eval do
96 define_method name do |*args|
97 scopes[name].call(self, *args)
98 end
99 end
100 end
101 end
c3aa2bc Manfred Stienstra Ensure nested with_scope merges conditions inside out [#2193 state:resol...
Manfred authored
102
081ddb6 risk danger olson Merge the has_finder gem, renamed as 'named_scope'. Closes #11404 [nkall...
technoweenie authored
103 class Scope
0dd2f96 Diego Algorta Fixed bug that makes named_scopes _forgot_ current scope
oboxodo authored
104 attr_reader :proxy_scope, :proxy_options, :current_scoped_methods_when_defined
7f179f8 Tom Stuart Make NamedScope#size behave identically to AssociationCollection#size. [...
tomstuart authored
105 NON_DELEGATE_METHODS = %w(nil? send object_id class extend find size count sum average maximum minimum paginate first last empty? any? respond_to?).to_set
73c5963 Ryan Bates Add first/last methods to associations/named_scope. [#226 state:resolved...
ryanb authored
106 [].methods.each do |m|
c036134 Joshua Peek 1.9: methods need to be coerced into strings
josh authored
107 unless m =~ /^__/ || NON_DELEGATE_METHODS.include?(m.to_s)
73c5963 Ryan Bates Add first/last methods to associations/named_scope. [#226 state:resolved...
ryanb authored
108 delegate m, :to => :proxy_found
109 end
110 end
111
628b4ad Alexander Podgorbunsky Default scope :order should be overridden by named scopes.
shurikp authored
112 delegate :scopes, :with_scope, :scoped_methods, :to => :proxy_scope
081ddb6 risk danger olson Merge the has_finder gem, renamed as 'named_scope'. Closes #11404 [nkall...
technoweenie authored
113
114 def initialize(proxy_scope, options, &block)
91b98cf Elijah Miller Returning nil from named scope lambda is equivalent to an empty hash [#1...
jqr authored
115 options ||= {}
081ddb6 risk danger olson Merge the has_finder gem, renamed as 'named_scope'. Closes #11404 [nkall...
technoweenie authored
116 [options[:extend]].flatten.each { |extension| extend extension } if options[:extend]
117 extend Module.new(&block) if block_given?
0dd2f96 Diego Algorta Fixed bug that makes named_scopes _forgot_ current scope
oboxodo authored
118 unless Scope === proxy_scope
119 @current_scoped_methods_when_defined = proxy_scope.send(:current_scoped_methods)
120 end
081ddb6 risk danger olson Merge the has_finder gem, renamed as 'named_scope'. Closes #11404 [nkall...
technoweenie authored
121 @proxy_scope, @proxy_options = proxy_scope, options.except(:extend)
122 end
123
124 def reload
125 load_found; self
126 end
127
73c5963 Ryan Bates Add first/last methods to associations/named_scope. [#226 state:resolved...
ryanb authored
128 def first(*args)
129 if args.first.kind_of?(Integer) || (@found && !args.first.kind_of?(Hash))
130 proxy_found.first(*args)
131 else
132 find(:first, *args)
133 end
134 end
135
136 def last(*args)
137 if args.first.kind_of?(Integer) || (@found && !args.first.kind_of?(Hash))
138 proxy_found.last(*args)
139 else
140 find(:last, *args)
141 end
142 end
143
7f179f8 Tom Stuart Make NamedScope#size behave identically to AssociationCollection#size. [...
tomstuart authored
144 def size
145 @found ? @found.length : count
146 end
147
c2fbcba Ryan Bates Ensure named_scope#empty? uses count query. [#262 state:resolved]
ryanb authored
148 def empty?
149 @found ? @found.empty? : count.zero?
150 end
151
3724daf Tom Lea Fix incorrect signature for NamedScope#respond_to? [#852 state:resolved]
cwninja authored
152 def respond_to?(method, include_private = false)
153 super || @proxy_scope.respond_to?(method, include_private)
1ee9e3f Eloy Durán Fix ActiveRecord::NamedScope::Scope#respond_to? [#818 state:resolved]
alloy authored
154 end
155
93e10f9 Jan De Poorter Ensure NamedScope#any? uses COUNT query wherever possible. [#680 state:r...
DefV authored
156 def any?
157 if block_given?
158 proxy_found.any? { |*block_args| yield(*block_args) }
159 else
160 !empty?
161 end
162 end
163
081ddb6 risk danger olson Merge the has_finder gem, renamed as 'named_scope'. Closes #11404 [nkall...
technoweenie authored
164 protected
165 def proxy_found
166 @found || load_found
167 end
168
169 private
170 def method_missing(method, *args, &block)
171 if scopes.include?(method)
172 scopes[method].call(self, *args)
173 else
c3aa2bc Manfred Stienstra Ensure nested with_scope merges conditions inside out [#2193 state:resol...
Manfred authored
174 with_scope({:find => proxy_options, :create => proxy_options[:conditions].is_a?(Hash) ? proxy_options[:conditions] : {}}, :reverse_merge) do
84af99e Daniel Ensure NamedScope#build/create/create!/new works as expected when named ...
dguettler authored
175 method = :new if method == :build
628b4ad Alexander Podgorbunsky Default scope :order should be overridden by named scopes.
shurikp authored
176 if current_scoped_methods_when_defined && !scoped_methods.include?(current_scoped_methods_when_defined)
0dd2f96 Diego Algorta Fixed bug that makes named_scopes _forgot_ current scope
oboxodo authored
177 with_scope current_scoped_methods_when_defined do
178 proxy_scope.send(method, *args, &block)
179 end
180 else
181 proxy_scope.send(method, *args, &block)
182 end
081ddb6 risk danger olson Merge the has_finder gem, renamed as 'named_scope'. Closes #11404 [nkall...
technoweenie authored
183 end
184 end
185 end
186
187 def load_found
188 @found = find(:all)
189 end
190 end
191 end
a34cc42 - Updated tzinfo to use Rational() instead of Rational.new! due to "priv...
Nate Wiger authored
192 end
Something went wrong with that request. Please try again.