mongoid 1.0.1 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/HISTORY CHANGED
@@ -1,3 +1,17 @@
1
+ === 1.0.2
2
+ - Named scopes get added functionality:
3
+
4
+ - named scopes can now be criteria objects.
5
+
6
+ - named scoped can now be procs with criteria.
7
+
8
+ - named scopes and class methods that return
9
+ criteria can be chained with each other.
10
+
11
+ - When calling save on an embedded document whose
12
+ validation passes but the parent's validation
13
+ fails, it will properly return false.
14
+
1
15
  === 1.0.1
2
16
  - Documents now have named_scopes similar to
3
17
  ActiveRecord named scopes. Please see rdoc or
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.0.1
1
+ 1.0.2
@@ -45,14 +45,15 @@ require "mongoid/commands"
45
45
  require "mongoid/config"
46
46
  require "mongoid/complex_criterion"
47
47
  require "mongoid/criteria"
48
- require "mongoid/named_scopes"
49
48
  require "mongoid/extensions"
50
49
  require "mongoid/errors"
51
- require "mongoid/fields/field"
50
+ require "mongoid/field"
52
51
  require "mongoid/fields"
53
52
  require "mongoid/finders"
54
53
  require "mongoid/indexes"
55
54
  require "mongoid/memoization"
55
+ require "mongoid/named_scope"
56
+ require "mongoid/scope"
56
57
  require "mongoid/timestamps"
57
58
  require "mongoid/versioning"
58
59
  require "mongoid/components"
@@ -15,9 +15,12 @@ module Mongoid #:nodoc:
15
15
  doc.run_callbacks :before_save
16
16
  parent = doc._parent
17
17
  doc.new_record = false
18
- parent ? Save.execute(parent) : doc.collection.save(doc.attributes)
19
- doc.run_callbacks :after_save
20
- return true
18
+ if parent ? Save.execute(parent, validate) : doc.collection.save(doc.attributes)
19
+ doc.run_callbacks :after_save
20
+ return true
21
+ else
22
+ return false
23
+ end
21
24
  end
22
25
  end
23
26
  end
@@ -15,7 +15,7 @@ module Mongoid #:nodoc
15
15
  include Observable
16
16
  include Validatable
17
17
  extend Finders
18
- extend NamedScopes
18
+ extend NamedScope
19
19
  end
20
20
  end
21
21
  end
@@ -103,7 +103,7 @@ module Mongoid #:nodoc:
103
103
  @count ||= @klass.collection.find(@selector, process_options).count
104
104
  end
105
105
 
106
- # Translate the supplied argument hash
106
+ # Merges the supplied argument hash into a single criteria
107
107
  #
108
108
  # Options:
109
109
  #
@@ -111,10 +111,10 @@ module Mongoid #:nodoc:
111
111
  #
112
112
  # Example:
113
113
  #
114
- # <tt>criteria.translate(:where => { :field => "value"}, :limit => 20)</tt>
114
+ # <tt>criteria.fuse(:where => { :field => "value"}, :limit => 20)</tt>
115
115
  #
116
116
  # Returns <tt>self</tt>
117
- def criteria(criteria_conditions = {})
117
+ def fuse(criteria_conditions = {})
118
118
  criteria_conditions.inject(self) do |criteria, (key, value)|
119
119
  criteria.send(key, value)
120
120
  end
@@ -453,6 +453,12 @@ module Mongoid #:nodoc:
453
453
  (@options[:limit] || 20).to_i
454
454
  end
455
455
 
456
+ # Returns the selector and options as a +Hash+ that would be passed to a
457
+ # scope for use with named scopes.
458
+ def scoped
459
+ { :where => @selector }.merge(@options)
460
+ end
461
+
456
462
  # Adds a criterion to the +Criteria+ that specifies how many results to skip
457
463
  # when returning Documents. This is mostly used in conjunction with
458
464
  # <tt>limit()</tt> to handle paginated results, and is similar to the
@@ -11,9 +11,11 @@ require "mongoid/extensions/hash/accessors"
11
11
  require "mongoid/extensions/hash/assimilation"
12
12
  require "mongoid/extensions/hash/conversions"
13
13
  require "mongoid/extensions/hash/criteria_helpers"
14
+ require "mongoid/extensions/hash/scoping"
14
15
  require "mongoid/extensions/integer/conversions"
15
16
  require "mongoid/extensions/nil/assimilation"
16
17
  require "mongoid/extensions/object/conversions"
18
+ require "mongoid/extensions/proc/scoping"
17
19
  require "mongoid/extensions/string/conversions"
18
20
  require "mongoid/extensions/string/inflections"
19
21
  require "mongoid/extensions/symbol/inflections"
@@ -46,6 +48,7 @@ class Hash #:nodoc
46
48
  include Mongoid::Extensions::Hash::Accessors
47
49
  include Mongoid::Extensions::Hash::Assimilation
48
50
  include Mongoid::Extensions::Hash::CriteriaHelpers
51
+ include Mongoid::Extensions::Hash::Scoping
49
52
  extend Mongoid::Extensions::Hash::Conversions
50
53
  end
51
54
 
@@ -61,6 +64,10 @@ class Object #:nodoc:
61
64
  include Mongoid::Extensions::Object::Conversions
62
65
  end
63
66
 
67
+ class Proc #:nodoc:
68
+ include Mongoid::Extensions::Proc::Scoping
69
+ end
70
+
64
71
  class String #:nodoc
65
72
  include Mongoid::Extensions::String::Inflections
66
73
  extend Mongoid::Extensions::String::Conversions
@@ -0,0 +1,12 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Extensions #:nodoc:
4
+ module Hash #:nodoc:
5
+ module Scoping #:nodoc:
6
+ def scoped(*args)
7
+ self
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Extensions #:nodoc:
4
+ module Proc #:nodoc:
5
+ module Scoping #:nodoc:
6
+ def scoped(*args)
7
+ call(*args).scoped
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,38 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module NamedScope
4
+ # Creates a named_scope for the +Document+, similar to ActiveRecord's
5
+ # named_scopes. +NamedScopes+ are proxied +Criteria+ objects that can be
6
+ # chained.
7
+ #
8
+ # Example:
9
+ #
10
+ # class Person
11
+ # include Mongoid::Document
12
+ # field :active, :type => Boolean
13
+ # field :count, :type => Integer
14
+ #
15
+ # named_scope :active, :where => { :active => true }
16
+ # named_scope :count_gt_one, :where => { :count.gt => 1 }
17
+ # named_scope :at_least_count, lambda { |count| { :where => { :count.gt => count } } }
18
+ # end
19
+ def named_scope(name, options = {}, &block)
20
+ name = name.to_sym
21
+ scopes[name] = lambda do |parent, *args|
22
+ Scope.new(parent, options.scoped(*args), &block)
23
+ end
24
+ (class << self; self; end).class_eval <<-EOT
25
+ def #{name}(*args)
26
+ scopes[:#{name}].call(self, *args)
27
+ end
28
+ EOT
29
+ end
30
+
31
+ # Return the scopes or default to an empty +Hash+.
32
+ def scopes
33
+ read_inheritable_attribute(:scopes) || write_inheritable_attribute(:scopes, {})
34
+ end
35
+
36
+ end
37
+ end
38
+
@@ -0,0 +1,59 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ class Scope #:nodoc:
4
+
5
+ delegate :scopes, :to => "@parent"
6
+
7
+ # Create the new +Scope+. If a block is passed in, this Scope will extend
8
+ # the block.
9
+ #
10
+ # Options:
11
+ #
12
+ # parent: The class the scope belongs to, or a parent +Scope+.
13
+ # conditions: A +Hash+ of conditions.
14
+ #
15
+ # Example:
16
+ #
17
+ # Mongoid::Scope.new(Person, { :title => "Sir" }) do
18
+ # def knighted?
19
+ # title == "Sir"
20
+ # end
21
+ # end
22
+ def initialize(parent, conditions, &block)
23
+ @parent, @conditions = parent, conditions
24
+ extend Module.new(&block) if block_given?
25
+ end
26
+
27
+ # Return the class for the +Scope+. This will be the parent if the parent
28
+ # is a class, otherwise will be nil.
29
+ def klass
30
+ @klass ||= @parent unless @parent.is_a?(Scope)
31
+ end
32
+
33
+ # Chaining is supported through method_missing. If a scope is already
34
+ # defined with the method name the call will be passed there, otherwise it
35
+ # will be passed to the target or parent.
36
+ def method_missing(name, *args, &block)
37
+ if scopes.include?(name)
38
+ scopes[name].call(self, *args)
39
+ elsif klass
40
+ target.send(name, *args, &block)
41
+ else
42
+ @parent.fuse(@conditions); @parent.send(name, *args, &block)
43
+ end
44
+ end
45
+
46
+ # The +Scope+ must respond like a +Criteria+ object. If this is a parent
47
+ # criteria delegate to the target, otherwise bubble up to the parent.
48
+ def respond_to?(name)
49
+ super || (klass ? target.respond_to?(name) : @parent.respond_to?(name))
50
+ end
51
+
52
+ # Returns the target criteria if it has already been set or creates a new
53
+ # criteria from the parent class.
54
+ def target
55
+ @target ||= klass.criteria.fuse(@conditions)
56
+ end
57
+ end
58
+ end
59
+
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{mongoid}
8
- s.version = "1.0.1"
8
+ s.version = "1.0.2"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Durran Jordan"]
12
- s.date = %q{2010-01-10}
12
+ s.date = %q{2010-01-11}
13
13
  s.email = %q{durran@gmail.com}
14
14
  s.extra_rdoc_files = [
15
15
  "README.rdoc"
@@ -61,19 +61,22 @@ Gem::Specification.new do |s|
61
61
  "lib/mongoid/extensions/hash/assimilation.rb",
62
62
  "lib/mongoid/extensions/hash/conversions.rb",
63
63
  "lib/mongoid/extensions/hash/criteria_helpers.rb",
64
+ "lib/mongoid/extensions/hash/scoping.rb",
64
65
  "lib/mongoid/extensions/integer/conversions.rb",
65
66
  "lib/mongoid/extensions/nil/assimilation.rb",
66
67
  "lib/mongoid/extensions/object/conversions.rb",
68
+ "lib/mongoid/extensions/proc/scoping.rb",
67
69
  "lib/mongoid/extensions/string/conversions.rb",
68
70
  "lib/mongoid/extensions/string/inflections.rb",
69
71
  "lib/mongoid/extensions/symbol/inflections.rb",
70
72
  "lib/mongoid/extensions/time/conversions.rb",
73
+ "lib/mongoid/field.rb",
71
74
  "lib/mongoid/fields.rb",
72
- "lib/mongoid/fields/field.rb",
73
75
  "lib/mongoid/finders.rb",
74
76
  "lib/mongoid/indexes.rb",
75
77
  "lib/mongoid/memoization.rb",
76
- "lib/mongoid/named_scopes.rb",
78
+ "lib/mongoid/named_scope.rb",
79
+ "lib/mongoid/scope.rb",
77
80
  "lib/mongoid/timestamps.rb",
78
81
  "lib/mongoid/versioning.rb",
79
82
  "mongoid.gemspec",
@@ -84,7 +87,7 @@ Gem::Specification.new do |s|
84
87
  "spec/integration/mongoid/document_spec.rb",
85
88
  "spec/integration/mongoid/finders_spec.rb",
86
89
  "spec/integration/mongoid/inheritance_spec.rb",
87
- "spec/integration/mongoid/named_scopes_spec.rb",
90
+ "spec/integration/mongoid/named_scope_spec.rb",
88
91
  "spec/spec.opts",
89
92
  "spec/spec_helper.rb",
90
93
  "spec/unit/mongoid/associations/belongs_to_related_spec.rb",
@@ -120,9 +123,11 @@ Gem::Specification.new do |s|
120
123
  "spec/unit/mongoid/extensions/hash/assimilation_spec.rb",
121
124
  "spec/unit/mongoid/extensions/hash/conversions_spec.rb",
122
125
  "spec/unit/mongoid/extensions/hash/criteria_helpers_spec.rb",
126
+ "spec/unit/mongoid/extensions/hash/scoping_spec.rb",
123
127
  "spec/unit/mongoid/extensions/integer/conversions_spec.rb",
124
128
  "spec/unit/mongoid/extensions/nil/assimilation_spec.rb",
125
129
  "spec/unit/mongoid/extensions/object/conversions_spec.rb",
130
+ "spec/unit/mongoid/extensions/proc/scoping_spec.rb",
126
131
  "spec/unit/mongoid/extensions/string/conversions_spec.rb",
127
132
  "spec/unit/mongoid/extensions/string/inflections_spec.rb",
128
133
  "spec/unit/mongoid/extensions/symbol/inflections_spec.rb",
@@ -132,7 +137,8 @@ Gem::Specification.new do |s|
132
137
  "spec/unit/mongoid/finders_spec.rb",
133
138
  "spec/unit/mongoid/indexes_spec.rb",
134
139
  "spec/unit/mongoid/memoization_spec.rb",
135
- "spec/unit/mongoid/named_scopes_spec.rb",
140
+ "spec/unit/mongoid/named_scope_spec.rb",
141
+ "spec/unit/mongoid/scope_spec.rb",
136
142
  "spec/unit/mongoid/timestamps_spec.rb",
137
143
  "spec/unit/mongoid/versioning_spec.rb",
138
144
  "spec/unit/mongoid_spec.rb"
@@ -149,7 +155,7 @@ Gem::Specification.new do |s|
149
155
  "spec/integration/mongoid/document_spec.rb",
150
156
  "spec/integration/mongoid/finders_spec.rb",
151
157
  "spec/integration/mongoid/inheritance_spec.rb",
152
- "spec/integration/mongoid/named_scopes_spec.rb",
158
+ "spec/integration/mongoid/named_scope_spec.rb",
153
159
  "spec/spec_helper.rb",
154
160
  "spec/unit/mongoid/associations/belongs_to_related_spec.rb",
155
161
  "spec/unit/mongoid/associations/belongs_to_spec.rb",
@@ -184,9 +190,11 @@ Gem::Specification.new do |s|
184
190
  "spec/unit/mongoid/extensions/hash/assimilation_spec.rb",
185
191
  "spec/unit/mongoid/extensions/hash/conversions_spec.rb",
186
192
  "spec/unit/mongoid/extensions/hash/criteria_helpers_spec.rb",
193
+ "spec/unit/mongoid/extensions/hash/scoping_spec.rb",
187
194
  "spec/unit/mongoid/extensions/integer/conversions_spec.rb",
188
195
  "spec/unit/mongoid/extensions/nil/assimilation_spec.rb",
189
196
  "spec/unit/mongoid/extensions/object/conversions_spec.rb",
197
+ "spec/unit/mongoid/extensions/proc/scoping_spec.rb",
190
198
  "spec/unit/mongoid/extensions/string/conversions_spec.rb",
191
199
  "spec/unit/mongoid/extensions/string/inflections_spec.rb",
192
200
  "spec/unit/mongoid/extensions/symbol/inflections_spec.rb",
@@ -196,7 +204,8 @@ Gem::Specification.new do |s|
196
204
  "spec/unit/mongoid/finders_spec.rb",
197
205
  "spec/unit/mongoid/indexes_spec.rb",
198
206
  "spec/unit/mongoid/memoization_spec.rb",
199
- "spec/unit/mongoid/named_scopes_spec.rb",
207
+ "spec/unit/mongoid/named_scope_spec.rb",
208
+ "spec/unit/mongoid/scope_spec.rb",
200
209
  "spec/unit/mongoid/timestamps_spec.rb",
201
210
  "spec/unit/mongoid/versioning_spec.rb",
202
211
  "spec/unit/mongoid_spec.rb"
@@ -0,0 +1,46 @@
1
+ require "spec_helper"
2
+
3
+ describe Mongoid::NamedScope do
4
+
5
+ describe ".named_scope" do
6
+
7
+ class Person
8
+ named_scope :doctors, {:where => {:title => 'Dr.'} }
9
+ named_scope :old, criteria.where(:age.gt => 50)
10
+ end
11
+
12
+ before do
13
+ @document = Person.create(:title => "Dr.", :age => 65, :terms => true)
14
+ end
15
+
16
+ after do
17
+ Person.delete_all
18
+ end
19
+
20
+ context "accessing a single named scope" do
21
+
22
+ it "returns the document" do
23
+ Person.doctors.first.should == @document
24
+ end
25
+
26
+ end
27
+
28
+ context "chaining named scopes" do
29
+
30
+ it "returns the document" do
31
+ Person.old.doctors.first.should == @document
32
+ end
33
+
34
+ end
35
+
36
+ context "mixing named scopes and class methods" do
37
+
38
+ it "returns the document" do
39
+ Person.accepted.old.doctors.first.should == @document
40
+ end
41
+
42
+ end
43
+
44
+ end
45
+
46
+ end
@@ -5,8 +5,8 @@ describe Mongoid::Commands::Save do
5
5
  describe "#execute" do
6
6
 
7
7
  before do
8
- @parent_collection = stub(:save)
9
- @doc_collection = stub(:save)
8
+ @parent_collection = stub(:save => true)
9
+ @doc_collection = stub(:save => true)
10
10
  @parent = stub(:collection => @parent_collection,
11
11
  :valid? => true,
12
12
  :run_callbacks => true,
@@ -860,6 +860,19 @@ describe Mongoid::Criteria do
860
860
 
861
861
  end
862
862
 
863
+ describe "#scoped" do
864
+
865
+ before do
866
+ @criteria = Person.where(:title => "Sir").skip(20)
867
+ end
868
+
869
+ it "returns the selector plus the options" do
870
+ @criteria.scoped.should ==
871
+ { :where => { :title => "Sir", :_type=>{ "$in" => [ "Doctor", "Person" ] } }, :skip => 20 }
872
+ end
873
+
874
+ end
875
+
863
876
  describe "#skip" do
864
877
 
865
878
  context "when value provided" do
@@ -1129,15 +1142,15 @@ describe Mongoid::Criteria do
1129
1142
 
1130
1143
  end
1131
1144
 
1132
- context "#criteria" do
1145
+ context "#fuse" do
1133
1146
 
1134
1147
  it ":where => {:title => 'Test'} returns a criteria with the correct selector" do
1135
- @result = @criteria.criteria(:where => { :title => 'Test' })
1148
+ @result = @criteria.fuse(:where => { :title => 'Test' })
1136
1149
  @result.selector[:title].should == 'Test'
1137
1150
  end
1138
1151
 
1139
1152
  it ":where => {:title => 'Test'}, :skip => 10 returns a criteria with the correct selector and options" do
1140
- @result = @criteria.criteria(:where => { :title => 'Test' }, :skip => 10)
1153
+ @result = @criteria.fuse(:where => { :title => 'Test' }, :skip => 10)
1141
1154
  @result.selector[:title].should == 'Test'
1142
1155
  @result.options.should == { :skip => 10 }
1143
1156
  end
@@ -0,0 +1,14 @@
1
+ require "spec_helper"
2
+
3
+ describe Mongoid::Extensions::Hash::Scoping do
4
+
5
+ describe "#scoped" do
6
+
7
+ it "returns self" do
8
+ { :where => { :active => true } }.scoped.should ==
9
+ { :where => { :active => true } }
10
+ end
11
+
12
+ end
13
+
14
+ end
@@ -0,0 +1,34 @@
1
+ require "spec_helper"
2
+
3
+ describe Mongoid::Extensions::Proc::Scoping do
4
+
5
+ describe "#scoped" do
6
+
7
+ context "when the proc accessed a hash" do
8
+
9
+ before do
10
+ @proc = lambda { |number| { :where => { :count => number } } }
11
+ end
12
+
13
+ it "calls the hash with the args" do
14
+ @proc.scoped(10).should == { :where => { :count => 10 } }
15
+ end
16
+
17
+ end
18
+
19
+ context "when the proc calls a criteria" do
20
+
21
+ before do
22
+ @proc = lambda { |title| Person.where(:title => title) }
23
+ end
24
+
25
+ it "returns the criteria scoped" do
26
+ @proc.scoped("Sir").should ==
27
+ { :where => { :_type => { "$in" => [ "Doctor", "Person" ] }, :title => "Sir" } }
28
+ end
29
+
30
+ end
31
+
32
+ end
33
+
34
+ end
@@ -0,0 +1,110 @@
1
+ require "spec_helper"
2
+
3
+ describe Mongoid::NamedScope do
4
+
5
+ class Player
6
+ include Mongoid::Document
7
+ field :active, :type => Boolean
8
+ field :frags, :type => Integer
9
+ field :deaths, :type => Integer
10
+ field :status
11
+
12
+ named_scope :active, criteria.where(:active => true) do
13
+ def extension
14
+ "extension"
15
+ end
16
+ end
17
+ named_scope :inactive, :where => { :active => false }
18
+ named_scope :frags_over, lambda { |count| { :where => { :frags.gt => count } } }
19
+ named_scope :deaths_under, lambda { |count| criteria.where(:deaths.lt => count) }
20
+
21
+ class << self
22
+ def alive
23
+ criteria.where(:status => "Alive")
24
+ end
25
+ end
26
+ end
27
+
28
+ describe ".named_scope" do
29
+
30
+ it "adds a class method for the scope" do
31
+ Player.should respond_to(:active)
32
+ end
33
+
34
+ it "adds the scope to the scopes" do
35
+ Player.scopes.should include(:active)
36
+ end
37
+
38
+ context "when options are a hash" do
39
+
40
+ it "adds the selector to the scope" do
41
+ Player.inactive.selector[:active].should be_false
42
+ end
43
+
44
+ end
45
+
46
+ context "when options are a criteria" do
47
+
48
+ it "adds the selector to the scope" do
49
+ Player.active.selector[:active].should be_true
50
+ end
51
+
52
+ end
53
+
54
+ context "when options are a proc" do
55
+
56
+ context "when the proc delegates to a hash" do
57
+
58
+ it "adds the selector to the scope" do
59
+ Player.frags_over(50).selector[:frags].should == { "$gt" => 50 }
60
+ end
61
+
62
+ end
63
+
64
+ context "when the proc delegates to a criteria" do
65
+
66
+ it "adds the selector to the scope" do
67
+ Player.deaths_under(40).selector[:deaths].should == { "$lt" => 40 }
68
+ end
69
+
70
+ end
71
+
72
+ end
73
+
74
+ context "when a block is supplied" do
75
+
76
+ it "adds the block as an extension" do
77
+ Player.active.extension.should == "extension"
78
+ end
79
+
80
+ end
81
+
82
+ end
83
+
84
+ context "chained scopes" do
85
+
86
+ context "when chaining two named scopes" do
87
+
88
+ it "merges the criteria" do
89
+ selector = Player.active.frags_over(10).selector
90
+ selector[:active].should be_true
91
+ selector[:frags].should == { "$gt" => 10 }
92
+ end
93
+
94
+ end
95
+
96
+ context "when chaining named scoped with criteria class methods" do
97
+
98
+ it "merges the criteria" do
99
+ selector = Player.active.frags_over(10).alive.selector
100
+ selector[:active].should be_true
101
+ selector[:frags].should == { "$gt" => 10 }
102
+ selector[:status].should == "Alive"
103
+ end
104
+
105
+ end
106
+
107
+ end
108
+
109
+ end
110
+
@@ -0,0 +1,166 @@
1
+ require "spec_helper"
2
+
3
+ describe Mongoid::Scope do
4
+
5
+ describe ".initialize" do
6
+
7
+ before do
8
+ @parent = Mongoid::Scope.new(Person, {})
9
+ end
10
+
11
+ context "when parent is another scope" do
12
+
13
+ before do
14
+ @scope = Mongoid::Scope.new(@parent, {})
15
+ end
16
+
17
+ it "does not set the class" do
18
+ @scope.klass.should be_nil
19
+ end
20
+
21
+ end
22
+
23
+ context "when parent is a class" do
24
+
25
+ before do
26
+ @scope = Mongoid::Scope.new(Person, {})
27
+ end
28
+
29
+ it "returns the parent class" do
30
+ @scope.klass.should == Person
31
+ end
32
+
33
+ end
34
+
35
+ context "when a block is passed in" do
36
+
37
+ before do
38
+ @scope = Mongoid::Scope.new(Person, {}) do
39
+ def extended
40
+ "extended"
41
+ end
42
+ end
43
+ end
44
+
45
+ it "extends the block" do
46
+ @scope.extended.should == "extended"
47
+ end
48
+
49
+ end
50
+
51
+ end
52
+
53
+ describe "#method_missing" do
54
+
55
+ context "when a scope has been defined for the name" do
56
+
57
+ before do
58
+ @defined = mock
59
+ @class = mock
60
+ @class.expects(:scopes).twice.returns({ :testing => @defined })
61
+ @scope = Mongoid::Scope.new(@class, {})
62
+ end
63
+
64
+ it "calls the matching scope" do
65
+ @defined.expects(:call).with(@scope, "Testing").returns(true)
66
+ @scope.testing("Testing").should be_true
67
+ end
68
+
69
+ end
70
+
71
+ context "when a scope is not defined for the name" do
72
+
73
+ context "when the scope is the parent" do
74
+
75
+ before do
76
+ @target = mock
77
+ @scope = Mongoid::Scope.new(Person, {})
78
+ @scope.instance_variable_set("@target", @target)
79
+ end
80
+
81
+ it "sends the call to the target" do
82
+ @target.expects(:testing).with("Testing").returns(true)
83
+ @scope.testing("Testing").should be_true
84
+ end
85
+
86
+ end
87
+
88
+ context "when the scope is not the parent" do
89
+
90
+ before do
91
+ @parent = mock
92
+ @criteria = mock
93
+ @parent.expects(:scopes).returns({})
94
+ @parent.expects(:is_a?).with(Mongoid::Scope).returns(true)
95
+ @parent.expects(:fuse)
96
+ @scope = Mongoid::Scope.new(@parent, {})
97
+ end
98
+
99
+ it "creates a criteria from the parent scope" do
100
+ @parent.expects(:testing).returns(true)
101
+ @scope.testing.should be_true
102
+ end
103
+
104
+ end
105
+
106
+ end
107
+
108
+ end
109
+
110
+ describe "#respond_to?" do
111
+
112
+ context "when parent is a class" do
113
+
114
+ before do
115
+ @scope = Mongoid::Scope.new(Person, {})
116
+ end
117
+
118
+ it "delegates to the target" do
119
+ @scope.respond_to?(:only).should be_true
120
+ end
121
+
122
+ end
123
+
124
+ context "when parent is a scope" do
125
+
126
+ before do
127
+ @parent = Mongoid::Scope.new(Person, {})
128
+ @scope = Mongoid::Scope.new(@parent, {})
129
+ end
130
+
131
+ it "delegates to the parent" do
132
+ @scope.respond_to?(:only).should be_true
133
+ end
134
+
135
+ end
136
+
137
+ end
138
+
139
+ describe "#scopes" do
140
+
141
+ before do
142
+ @parent = mock
143
+ @scope = Mongoid::Scope.new(@parent, {})
144
+ end
145
+
146
+ it "delegates to the parent" do
147
+ @parent.expects(:scopes).returns({})
148
+ @scope.scopes.should == {}
149
+ end
150
+
151
+ end
152
+
153
+ describe "#target" do
154
+
155
+ before do
156
+ @scope = Mongoid::Scope.new(Person, { :where => { :title => "Sir" } })
157
+ end
158
+
159
+ it "returns the conditions criteria" do
160
+ @scope.target.selector.should ==
161
+ { :title => "Sir", :_type => { "$in" => [ "Doctor", "Person" ] } }
162
+ end
163
+
164
+ end
165
+
166
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mongoid
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Durran Jordan
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2010-01-10 00:00:00 -05:00
12
+ date: 2010-01-11 00:00:00 -05:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -127,19 +127,22 @@ files:
127
127
  - lib/mongoid/extensions/hash/assimilation.rb
128
128
  - lib/mongoid/extensions/hash/conversions.rb
129
129
  - lib/mongoid/extensions/hash/criteria_helpers.rb
130
+ - lib/mongoid/extensions/hash/scoping.rb
130
131
  - lib/mongoid/extensions/integer/conversions.rb
131
132
  - lib/mongoid/extensions/nil/assimilation.rb
132
133
  - lib/mongoid/extensions/object/conversions.rb
134
+ - lib/mongoid/extensions/proc/scoping.rb
133
135
  - lib/mongoid/extensions/string/conversions.rb
134
136
  - lib/mongoid/extensions/string/inflections.rb
135
137
  - lib/mongoid/extensions/symbol/inflections.rb
136
138
  - lib/mongoid/extensions/time/conversions.rb
139
+ - lib/mongoid/field.rb
137
140
  - lib/mongoid/fields.rb
138
- - lib/mongoid/fields/field.rb
139
141
  - lib/mongoid/finders.rb
140
142
  - lib/mongoid/indexes.rb
141
143
  - lib/mongoid/memoization.rb
142
- - lib/mongoid/named_scopes.rb
144
+ - lib/mongoid/named_scope.rb
145
+ - lib/mongoid/scope.rb
143
146
  - lib/mongoid/timestamps.rb
144
147
  - lib/mongoid/versioning.rb
145
148
  - mongoid.gemspec
@@ -150,7 +153,7 @@ files:
150
153
  - spec/integration/mongoid/document_spec.rb
151
154
  - spec/integration/mongoid/finders_spec.rb
152
155
  - spec/integration/mongoid/inheritance_spec.rb
153
- - spec/integration/mongoid/named_scopes_spec.rb
156
+ - spec/integration/mongoid/named_scope_spec.rb
154
157
  - spec/spec.opts
155
158
  - spec/spec_helper.rb
156
159
  - spec/unit/mongoid/associations/belongs_to_related_spec.rb
@@ -186,9 +189,11 @@ files:
186
189
  - spec/unit/mongoid/extensions/hash/assimilation_spec.rb
187
190
  - spec/unit/mongoid/extensions/hash/conversions_spec.rb
188
191
  - spec/unit/mongoid/extensions/hash/criteria_helpers_spec.rb
192
+ - spec/unit/mongoid/extensions/hash/scoping_spec.rb
189
193
  - spec/unit/mongoid/extensions/integer/conversions_spec.rb
190
194
  - spec/unit/mongoid/extensions/nil/assimilation_spec.rb
191
195
  - spec/unit/mongoid/extensions/object/conversions_spec.rb
196
+ - spec/unit/mongoid/extensions/proc/scoping_spec.rb
192
197
  - spec/unit/mongoid/extensions/string/conversions_spec.rb
193
198
  - spec/unit/mongoid/extensions/string/inflections_spec.rb
194
199
  - spec/unit/mongoid/extensions/symbol/inflections_spec.rb
@@ -198,7 +203,8 @@ files:
198
203
  - spec/unit/mongoid/finders_spec.rb
199
204
  - spec/unit/mongoid/indexes_spec.rb
200
205
  - spec/unit/mongoid/memoization_spec.rb
201
- - spec/unit/mongoid/named_scopes_spec.rb
206
+ - spec/unit/mongoid/named_scope_spec.rb
207
+ - spec/unit/mongoid/scope_spec.rb
202
208
  - spec/unit/mongoid/timestamps_spec.rb
203
209
  - spec/unit/mongoid/versioning_spec.rb
204
210
  - spec/unit/mongoid_spec.rb
@@ -237,7 +243,7 @@ test_files:
237
243
  - spec/integration/mongoid/document_spec.rb
238
244
  - spec/integration/mongoid/finders_spec.rb
239
245
  - spec/integration/mongoid/inheritance_spec.rb
240
- - spec/integration/mongoid/named_scopes_spec.rb
246
+ - spec/integration/mongoid/named_scope_spec.rb
241
247
  - spec/spec_helper.rb
242
248
  - spec/unit/mongoid/associations/belongs_to_related_spec.rb
243
249
  - spec/unit/mongoid/associations/belongs_to_spec.rb
@@ -272,9 +278,11 @@ test_files:
272
278
  - spec/unit/mongoid/extensions/hash/assimilation_spec.rb
273
279
  - spec/unit/mongoid/extensions/hash/conversions_spec.rb
274
280
  - spec/unit/mongoid/extensions/hash/criteria_helpers_spec.rb
281
+ - spec/unit/mongoid/extensions/hash/scoping_spec.rb
275
282
  - spec/unit/mongoid/extensions/integer/conversions_spec.rb
276
283
  - spec/unit/mongoid/extensions/nil/assimilation_spec.rb
277
284
  - spec/unit/mongoid/extensions/object/conversions_spec.rb
285
+ - spec/unit/mongoid/extensions/proc/scoping_spec.rb
278
286
  - spec/unit/mongoid/extensions/string/conversions_spec.rb
279
287
  - spec/unit/mongoid/extensions/string/inflections_spec.rb
280
288
  - spec/unit/mongoid/extensions/symbol/inflections_spec.rb
@@ -284,7 +292,8 @@ test_files:
284
292
  - spec/unit/mongoid/finders_spec.rb
285
293
  - spec/unit/mongoid/indexes_spec.rb
286
294
  - spec/unit/mongoid/memoization_spec.rb
287
- - spec/unit/mongoid/named_scopes_spec.rb
295
+ - spec/unit/mongoid/named_scope_spec.rb
296
+ - spec/unit/mongoid/scope_spec.rb
288
297
  - spec/unit/mongoid/timestamps_spec.rb
289
298
  - spec/unit/mongoid/versioning_spec.rb
290
299
  - spec/unit/mongoid_spec.rb
@@ -1,94 +0,0 @@
1
- # encoding: utf-8
2
- module Mongoid #:nodoc:
3
- module NamedScopes
4
- # Return the scopes or default to an empty +Hash+.
5
- def scopes
6
- read_inheritable_attribute(:scopes) || write_inheritable_attribute(:scopes, {})
7
- end
8
-
9
- # Creates a named_scope for the +Document+, similar to ActiveRecord's
10
- # named_scopes. +NamedScopes+ are proxied +Criteria+ objects that can be
11
- # chained.
12
- #
13
- # Example:
14
- #
15
- # class Person
16
- # include Mongoid::Document
17
- # field :active, :type => Boolean
18
- # field :count, :type => Integer
19
- #
20
- # named_scope :active, :where => { :active => true }
21
- # named_scope :count_gt_one, :where => { :count.gt => 1 }
22
- # named_scope :at_least_count, lambda { |count| { :where => { :count.gt => count } } }
23
- # end
24
- def named_scope(name, options = {}, &block)
25
- name = name.to_sym
26
- scopes[name] = lambda do |parent_scope, *args|
27
- CriteriaProxy.new(parent_scope, Hash === options ? options : options.call(*args), &block)
28
- end
29
- (class << self; self; end).class_eval <<-EOT
30
- def #{name}(*args)
31
- scopes[:#{name}].call(self, *args)
32
- end
33
- EOT
34
- end
35
-
36
- class CriteriaProxy #:nodoc
37
- attr_accessor :conditions, :klass, :parent_scope
38
-
39
- delegate :scopes, :to => :parent_scope
40
-
41
- # Instantiate the new +CriteriaProxy+. If the conditions contains an
42
- # extension, the proxy will extend from that module. If a block is given
43
- # it will be extended as well.
44
- #
45
- # Example:
46
- #
47
- # <tt>CriteriaProxy.new(parent, :where => { :active => true })</tt>
48
- def initialize(parent_scope, conditions, &block)
49
- conditions ||= {}
50
- [ conditions.delete(:extend) ].flatten.each do |extension|
51
- extend extension
52
- end if conditions.include?(:extend)
53
- extend Module.new(&block) if block_given?
54
- self.klass = parent_scope unless CriteriaProxy === parent_scope
55
- self.parent_scope, self.conditions = parent_scope, conditions
56
- end
57
-
58
- # First check if the proxy has the scope defined, otherwise look to the
59
- # parent scope.
60
- def respond_to?(method, include_private = false)
61
- super || if klass
62
- proxy_found.respond_to?(method, include_private)
63
- else
64
- parent_scope.respond_to?(method, include_private)
65
- end
66
- end
67
-
68
- protected
69
-
70
- def proxy_found
71
- @found || load_found
72
- end
73
-
74
- private
75
-
76
- def method_missing(method, *args, &block)
77
- if scopes.include?(method)
78
- scopes[method].call(self, *args)
79
- elsif klass
80
- proxy_found.send(method, *args, &block)
81
- else
82
- parent_scope.criteria(conditions)
83
- parent_scope.send(method, *args, &block)
84
- end
85
- end
86
-
87
- def load_found
88
- @found = Criteria.new(klass)
89
- @found.criteria(conditions); @found
90
- end
91
- end
92
- end
93
- end
94
-
@@ -1,25 +0,0 @@
1
- require "spec_helper"
2
-
3
- describe Mongoid::NamedScopes do
4
-
5
- describe ".named_scope" do
6
-
7
- class Person
8
- named_scope :doctors, {:where => {:title => 'Dr.'}}
9
- end
10
-
11
- before do
12
- @document = Person.create(:title => "Dr.")
13
- end
14
-
15
- after do
16
- Person.delete_all
17
- end
18
-
19
- it "returns the document" do
20
- Person.doctors.first.should == @document
21
- end
22
-
23
- end
24
-
25
- end
@@ -1,86 +0,0 @@
1
- require "spec_helper"
2
-
3
- describe Mongoid::NamedScopes do
4
-
5
- context "AR2 scopes" do
6
- module Extension
7
- def extension_module_method
8
- "extension module method"
9
- end
10
- end
11
-
12
- class NamedScopeTest
13
- include Mongoid::Document
14
-
15
- field :active, :type => Boolean
16
- field :count, :type => Integer
17
-
18
- named_scope :active, :where => {:active => true} do
19
- def extension_method
20
- "extension method"
21
- end
22
- end
23
- named_scope :count_gt_one, :where => { :count.gt => 1 }, :extend => Extension
24
- named_scope :at_least_count, lambda { |count| { :where => { :count.gt => count } } }
25
- end
26
-
27
- context ".named_scope" do
28
- it "adds the named scope to the hash of scopes" do
29
- NamedScopeTest.scopes.should have_key(:active)
30
- end
31
-
32
- it "creates a class method for the named scope" do
33
- NamedScopeTest.should respond_to(:active)
34
- end
35
- end
36
-
37
- context "accessing a named scope" do
38
- it "is a criteria proxy" do
39
- Mongoid::NamedScopes::CriteriaProxy.should === NamedScopeTest.active
40
- end
41
-
42
- it "responds like a criteria" do
43
- NamedScopeTest.active.should respond_to(:selector)
44
- end
45
-
46
- it "instantiates the criteria" do
47
- criteria = Mongoid::Criteria.new(NamedScopeTest)
48
- Mongoid::Criteria.expects(:new).returns(criteria)
49
- NamedScopeTest.active.selector
50
- end
51
-
52
- it "has set the conditions on the criteria" do
53
- NamedScopeTest.active.selector[:active].should be_true
54
- end
55
-
56
- it "sets the association extension by block" do
57
- NamedScopeTest.active.extension_method.should == "extension method"
58
- end
59
-
60
- it "sets the association extension by :extend" do
61
- NamedScopeTest.count_gt_one.extension_module_method.should == "extension module method"
62
- end
63
-
64
- context "when using a lambda" do
65
- it "accepts parameters to the criteria" do
66
- NamedScopeTest.at_least_count(3).selector[:count].should == {'$gt' => 3}
67
- end
68
- end
69
- end
70
-
71
- context "chained scopes" do
72
- it "instantiates the criteria" do
73
- criteria = Mongoid::Criteria.new(NamedScopeTest)
74
- Mongoid::Criteria.expects(:new).returns(criteria)
75
- NamedScopeTest.active.count_gt_one.selector
76
- end
77
-
78
- it "merges the criteria" do
79
- selector = NamedScopeTest.active.count_gt_one.selector
80
- selector[:count].should == {'$gt' => 1}
81
- selector[:active].should be_true
82
- end
83
- end
84
- end
85
- end
86
-