mongoid 1.0.6 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (79) hide show
  1. data/HISTORY +39 -0
  2. data/Rakefile +1 -1
  3. data/VERSION +1 -1
  4. data/lib/mongoid.rb +3 -1
  5. data/lib/mongoid/associations.rb +5 -5
  6. data/lib/mongoid/associations/has_many.rb +8 -2
  7. data/lib/mongoid/associations/has_many_related.rb +10 -4
  8. data/lib/mongoid/attributes.rb +19 -13
  9. data/lib/mongoid/commands.rb +12 -6
  10. data/lib/mongoid/commands/create.rb +2 -2
  11. data/lib/mongoid/commands/delete_all.rb +1 -1
  12. data/lib/mongoid/commands/save.rb +2 -2
  13. data/lib/mongoid/components.rb +1 -0
  14. data/lib/mongoid/contexts.rb +4 -0
  15. data/lib/mongoid/contexts/enumerable.rb +105 -0
  16. data/lib/mongoid/contexts/mongo.rb +228 -0
  17. data/lib/mongoid/contexts/paging.rb +42 -0
  18. data/lib/mongoid/criteria.rb +42 -191
  19. data/lib/mongoid/document.rb +19 -13
  20. data/lib/mongoid/extensions.rb +1 -0
  21. data/lib/mongoid/extensions/array/accessors.rb +3 -1
  22. data/lib/mongoid/extensions/float/conversions.rb +1 -1
  23. data/lib/mongoid/extensions/hash/accessors.rb +1 -1
  24. data/lib/mongoid/extensions/integer/conversions.rb +1 -0
  25. data/lib/mongoid/fields.rb +6 -5
  26. data/lib/mongoid/matchers.rb +36 -0
  27. data/lib/mongoid/matchers/all.rb +11 -0
  28. data/lib/mongoid/matchers/default.rb +20 -0
  29. data/lib/mongoid/matchers/exists.rb +13 -0
  30. data/lib/mongoid/matchers/gt.rb +11 -0
  31. data/lib/mongoid/matchers/gte.rb +11 -0
  32. data/lib/mongoid/matchers/in.rb +11 -0
  33. data/lib/mongoid/matchers/lt.rb +11 -0
  34. data/lib/mongoid/matchers/lte.rb +11 -0
  35. data/lib/mongoid/matchers/ne.rb +11 -0
  36. data/lib/mongoid/matchers/nin.rb +11 -0
  37. data/lib/mongoid/matchers/size.rb +11 -0
  38. data/lib/mongoid/scope.rb +17 -1
  39. data/mongoid.gemspec +51 -5
  40. data/spec/integration/mongoid/associations_spec.rb +67 -5
  41. data/spec/integration/mongoid/attributes_spec.rb +22 -0
  42. data/spec/integration/mongoid/commands_spec.rb +51 -12
  43. data/spec/integration/mongoid/criteria_spec.rb +3 -3
  44. data/spec/integration/mongoid/document_spec.rb +8 -8
  45. data/spec/integration/mongoid/finders_spec.rb +1 -1
  46. data/spec/integration/mongoid/inheritance_spec.rb +6 -0
  47. data/spec/integration/mongoid/named_scope_spec.rb +1 -1
  48. data/spec/spec_helper.rb +47 -6
  49. data/spec/unit/mongoid/associations/has_many_related_spec.rb +42 -0
  50. data/spec/unit/mongoid/associations/has_many_spec.rb +40 -1
  51. data/spec/unit/mongoid/attributes_spec.rb +1 -1
  52. data/spec/unit/mongoid/commands/create_spec.rb +3 -3
  53. data/spec/unit/mongoid/commands/delete_all_spec.rb +2 -2
  54. data/spec/unit/mongoid/commands/save_spec.rb +2 -2
  55. data/spec/unit/mongoid/commands_spec.rb +12 -12
  56. data/spec/unit/mongoid/contexts/enumerable_spec.rb +208 -0
  57. data/spec/unit/mongoid/contexts/mongo_spec.rb +370 -0
  58. data/spec/unit/mongoid/criteria_spec.rb +182 -21
  59. data/spec/unit/mongoid/extensions/array/accessors_spec.rb +9 -9
  60. data/spec/unit/mongoid/extensions/date/conversions_spec.rb +2 -1
  61. data/spec/unit/mongoid/extensions/float/conversions_spec.rb +4 -4
  62. data/spec/unit/mongoid/extensions/integer/conversions_spec.rb +1 -1
  63. data/spec/unit/mongoid/fields_spec.rb +3 -3
  64. data/spec/unit/mongoid/identity_spec.rb +2 -2
  65. data/spec/unit/mongoid/matchers/all_spec.rb +27 -0
  66. data/spec/unit/mongoid/matchers/default_spec.rb +27 -0
  67. data/spec/unit/mongoid/matchers/exists_spec.rb +56 -0
  68. data/spec/unit/mongoid/matchers/gt_spec.rb +39 -0
  69. data/spec/unit/mongoid/matchers/gte_spec.rb +49 -0
  70. data/spec/unit/mongoid/matchers/in_spec.rb +27 -0
  71. data/spec/unit/mongoid/matchers/lt_spec.rb +39 -0
  72. data/spec/unit/mongoid/matchers/lte_spec.rb +49 -0
  73. data/spec/unit/mongoid/matchers/ne_spec.rb +27 -0
  74. data/spec/unit/mongoid/matchers/nin_spec.rb +27 -0
  75. data/spec/unit/mongoid/matchers/size_spec.rb +27 -0
  76. data/spec/unit/mongoid/matchers_spec.rb +329 -0
  77. data/spec/unit/mongoid/scope_spec.rb +70 -0
  78. data/spec/unit/mongoid/timestamps_spec.rb +2 -2
  79. metadata +50 -4
@@ -4,7 +4,7 @@ describe Mongoid::Associations::HasMany do
4
4
 
5
5
  before do
6
6
  @attributes = { :addresses => [
7
- { :_id => "street-1", :street => "Street 1" },
7
+ { :_id => "street-1", :street => "Street 1", :state => "CA" },
8
8
  { :_id => "street-2", :street => "Street 2" } ] }
9
9
  @document = stub(:attributes => @attributes, :add_observer => true, :update => true)
10
10
  end
@@ -322,6 +322,45 @@ describe Mongoid::Associations::HasMany do
322
322
 
323
323
  end
324
324
 
325
+ describe "#method_missing" do
326
+
327
+ context "when the association class has a criteria class method" do
328
+
329
+ before do
330
+ @association = Mongoid::Associations::HasMany.new(
331
+ @document,
332
+ Mongoid::Associations::Options.new(:name => :addresses)
333
+ )
334
+ end
335
+
336
+ it "returns the criteria" do
337
+ @association.california.should be_a_kind_of(Mongoid::Criteria)
338
+ end
339
+
340
+ it "sets the documents on the criteria" do
341
+ criteria = @association.california
342
+ criteria.documents.should == @association.entries
343
+ end
344
+
345
+ it "returns the scoped documents" do
346
+ addresses = @association.california
347
+ addresses.size.should == 1
348
+ addresses.first.should be_a_kind_of(Address)
349
+ addresses.first.state.should == "CA"
350
+ end
351
+
352
+ end
353
+
354
+ context "when no class method exists" do
355
+
356
+ it "delegates to the array" do
357
+
358
+ end
359
+
360
+ end
361
+
362
+ end
363
+
325
364
  describe "#push" do
326
365
 
327
366
  before do
@@ -223,7 +223,7 @@ describe Mongoid::Attributes do
223
223
  attrs[:age].should == 30
224
224
  attrs[:terms].should == true
225
225
  attrs[:_id].should == "1"
226
- attrs[:score].should == ""
226
+ attrs[:score].should be_nil
227
227
  end
228
228
 
229
229
  end
@@ -9,19 +9,19 @@ describe Mongoid::Commands::Create do
9
9
  end
10
10
 
11
11
  it "executes a save command" do
12
- Mongoid::Commands::Save.expects(:execute).with(@document, true).returns(@document)
12
+ Mongoid::Commands::Save.expects(:execute).with(@document, true, false).returns(@document)
13
13
  Mongoid::Commands::Create.execute(@document)
14
14
  end
15
15
 
16
16
  it "runs the before and after create callbacks" do
17
17
  @document.expects(:run_callbacks).with(:before_create)
18
- Mongoid::Commands::Save.expects(:execute).with(@document, true).returns(@document)
18
+ Mongoid::Commands::Save.expects(:execute).with(@document, true, false).returns(@document)
19
19
  @document.expects(:run_callbacks).with(:after_create)
20
20
  Mongoid::Commands::Create.execute(@document)
21
21
  end
22
22
 
23
23
  it "returns the document" do
24
- Mongoid::Commands::Save.expects(:execute).with(@document, true).returns(@document)
24
+ Mongoid::Commands::Save.expects(:execute).with(@document, true, false).returns(@document)
25
25
  Mongoid::Commands::Create.execute(@document).should == @document
26
26
  end
27
27
 
@@ -33,7 +33,7 @@ describe Mongoid::Commands::DeleteAll do
33
33
 
34
34
  it "drops the collection" do
35
35
  @klass.expects(:collection).returns(@collection)
36
- @collection.expects(:drop)
36
+ @collection.expects(:remove).with(:_type => "Person")
37
37
  Mongoid::Commands::DeleteAll.execute(@klass)
38
38
  end
39
39
 
@@ -47,7 +47,7 @@ describe Mongoid::Commands::DeleteAll do
47
47
 
48
48
  it "drops the collection" do
49
49
  @klass.expects(:collection).returns(@collection)
50
- @collection.expects(:drop)
50
+ @collection.expects(:remove).with(:_type => "Person")
51
51
  Mongoid::Commands::DeleteAll.execute(@klass, {})
52
52
  end
53
53
 
@@ -39,7 +39,7 @@ describe Mongoid::Commands::Save do
39
39
  context "when the document has a parent" do
40
40
 
41
41
  it "executes a save on the parent" do
42
- @parent_collection.expects(:save).with(@parent.attributes)
42
+ @parent_collection.expects(:save).with(@parent.attributes, :safe => false)
43
43
  Mongoid::Commands::Save.execute(@document)
44
44
  end
45
45
 
@@ -52,7 +52,7 @@ describe Mongoid::Commands::Save do
52
52
  end
53
53
 
54
54
  it "calls save on the document collection" do
55
- @doc_collection.expects(:save).with(@document.attributes)
55
+ @doc_collection.expects(:save).with(@document.attributes, :safe => false)
56
56
  Mongoid::Commands::Save.execute(@document)
57
57
  end
58
58
 
@@ -27,7 +27,7 @@ describe Mongoid::Commands do
27
27
  describe "#save" do
28
28
 
29
29
  it "delegates to the Save command" do
30
- Mongoid::Commands::Save.expects(:execute).with(@person, true).returns(true)
30
+ Mongoid::Commands::Save.expects(:execute).with(@person, true, false).returns(true)
31
31
  @person.save
32
32
  end
33
33
 
@@ -38,13 +38,13 @@ describe Mongoid::Commands do
38
38
  end
39
39
 
40
40
  it "delegates to the save command" do
41
- Mongoid::Commands::Save.expects(:execute).with(@person, true).returns(true)
41
+ Mongoid::Commands::Save.expects(:execute).with(@person, true, false).returns(true)
42
42
  @person.save
43
43
  end
44
44
 
45
45
  it "runs the before and after create callbacks" do
46
46
  @person.expects(:run_callbacks).with(:before_create)
47
- Mongoid::Commands::Save.expects(:execute).with(@person, true).returns(true)
47
+ Mongoid::Commands::Save.expects(:execute).with(@person, true, false).returns(true)
48
48
  @person.expects(:run_callbacks).with(:after_create)
49
49
  @person.save
50
50
  end
@@ -58,7 +58,7 @@ describe Mongoid::Commands do
58
58
  end
59
59
 
60
60
  it "passes the validate param to the command" do
61
- Mongoid::Commands::Save.expects(:execute).with(@person, false).returns(true)
61
+ Mongoid::Commands::Save.expects(:execute).with(@person, false, false).returns(true)
62
62
  @person.save(false)
63
63
  end
64
64
 
@@ -71,7 +71,7 @@ describe Mongoid::Commands do
71
71
  context "when validation passes" do
72
72
 
73
73
  it "it returns the person" do
74
- Mongoid::Commands::Save.expects(:execute).with(@person, true).returns(true)
74
+ Mongoid::Commands::Save.expects(:execute).with(@person, true, true).returns(true)
75
75
  @person.save!
76
76
  end
77
77
 
@@ -80,7 +80,7 @@ describe Mongoid::Commands do
80
80
  context "when validation fails" do
81
81
 
82
82
  it "it raises an error" do
83
- Mongoid::Commands::Save.expects(:execute).with(@person, true).returns(false)
83
+ Mongoid::Commands::Save.expects(:execute).with(@person, true, true).returns(false)
84
84
  lambda { @person.save! }.should raise_error
85
85
  end
86
86
 
@@ -93,14 +93,14 @@ describe Mongoid::Commands do
93
93
  end
94
94
 
95
95
  it "delegates to the save command" do
96
- Mongoid::Commands::Save.expects(:execute).with(@person, true).returns(true)
96
+ Mongoid::Commands::Save.expects(:execute).with(@person, true, true).returns(true)
97
97
  @person.save!
98
98
  end
99
99
 
100
100
  context "when validation fails" do
101
101
 
102
102
  it "it raises an error " do
103
- Mongoid::Commands::Save.expects(:execute).with(@person, true).returns(false)
103
+ Mongoid::Commands::Save.expects(:execute).with(@person, true, true).returns(false)
104
104
  lambda { @person.save! }.should raise_error
105
105
  end
106
106
 
@@ -108,7 +108,7 @@ describe Mongoid::Commands do
108
108
 
109
109
  it "runs the before and after create callbacks" do
110
110
  @person.expects(:run_callbacks).with(:before_create)
111
- Mongoid::Commands::Save.expects(:execute).with(@person, true).returns(true)
111
+ Mongoid::Commands::Save.expects(:execute).with(@person, true, true).returns(true)
112
112
  @person.expects(:run_callbacks).with(:after_create)
113
113
  @person.save!
114
114
  end
@@ -120,7 +120,7 @@ describe Mongoid::Commands do
120
120
  describe "#update_attributes" do
121
121
 
122
122
  it "delegates to the Save command" do
123
- Mongoid::Commands::Save.expects(:execute).with(@person, true).returns(true)
123
+ Mongoid::Commands::Save.expects(:execute).with(@person, true, false).returns(true)
124
124
  @person.update_attributes({})
125
125
  end
126
126
 
@@ -131,7 +131,7 @@ describe Mongoid::Commands do
131
131
  context "when validation passes" do
132
132
 
133
133
  it "it returns the person" do
134
- Mongoid::Commands::Save.expects(:execute).with(@person, true).returns(true)
134
+ Mongoid::Commands::Save.expects(:execute).with(@person, true, false).returns(true)
135
135
  @person.update_attributes({}).should be_true
136
136
  end
137
137
 
@@ -140,7 +140,7 @@ describe Mongoid::Commands do
140
140
  context "when validation fails" do
141
141
 
142
142
  it "it raises an error" do
143
- Mongoid::Commands::Save.expects(:execute).with(@person, true).returns(false)
143
+ Mongoid::Commands::Save.expects(:execute).with(@person, true, true).returns(false)
144
144
  lambda { @person.update_attributes!({}) }.should raise_error
145
145
  end
146
146
 
@@ -0,0 +1,208 @@
1
+ require "spec_helper"
2
+
3
+ describe Mongoid::Contexts::Enumerable do
4
+
5
+ before do
6
+ @london = Address.new(:number => 1, :street => "Bond Street")
7
+ @shanghai = Address.new(:number => 10, :street => "Nan Jing Dong Lu")
8
+ @melbourne = Address.new(:number => 20, :street => "Bourke Street")
9
+ @new_york = Address.new(:number => 20, :street => "Broadway")
10
+ @docs = [ @london, @shanghai, @melbourne, @new_york ]
11
+ @selector = { :street => "Bond Street" }
12
+ @options = { :fields => [ :number ] }
13
+ @context = Mongoid::Contexts::Enumerable.new(@selector, @options, @docs)
14
+ end
15
+
16
+ describe "#aggregate" do
17
+
18
+ before do
19
+ @counts = @context.aggregate
20
+ end
21
+
22
+ it "groups by the fields provided in the options" do
23
+ @counts.size.should == 3
24
+ end
25
+
26
+ it "stores the counts in proper groups" do
27
+ @counts[1].should == 1
28
+ @counts[10].should == 1
29
+ @counts[20].should == 2
30
+ end
31
+ end
32
+
33
+ describe "#count" do
34
+
35
+ it "returns the size of the enumerable" do
36
+ @context.count.should == 4
37
+ end
38
+
39
+ end
40
+
41
+ describe "#execute" do
42
+
43
+ it "returns the matching documents from the array" do
44
+ @context.execute.should == [ @london ]
45
+ end
46
+
47
+ end
48
+
49
+ describe "#first" do
50
+
51
+ it "returns the first in the enumerable" do
52
+ @context.first.should == @london
53
+ end
54
+
55
+ end
56
+
57
+ describe "#group" do
58
+
59
+ before do
60
+ @group = @context.group
61
+ end
62
+
63
+ it "groups by the fields provided in the options" do
64
+ @group.size.should == 3
65
+ end
66
+
67
+ it "stores the documents in proper groups" do
68
+ @group[1].should == [ @london ]
69
+ @group[10].should == [ @shanghai ]
70
+ @group[20].should == [ @melbourne, @new_york ]
71
+ end
72
+
73
+ end
74
+
75
+ describe ".initialize" do
76
+
77
+ let(:selector) { { :field => "value" } }
78
+ let(:options) { { :skip => 20 } }
79
+ let(:documents) { [stub] }
80
+
81
+ before do
82
+ @context = Mongoid::Contexts::Enumerable.new(selector, options, documents)
83
+ end
84
+
85
+ it "sets the selector" do
86
+ @context.selector.should == selector
87
+ end
88
+
89
+ it "sets the options" do
90
+ @context.options.should == options
91
+ end
92
+
93
+ it "sets the documents" do
94
+ @context.documents.should == documents
95
+ end
96
+
97
+ end
98
+
99
+ describe "#last" do
100
+
101
+ it "returns the last in the enumerable" do
102
+ @context.last.should == @new_york
103
+ end
104
+
105
+ end
106
+
107
+ describe "#max" do
108
+
109
+ it "returns the max value for the supplied field" do
110
+ @context.max(:number).should == 20
111
+ end
112
+
113
+ end
114
+
115
+ describe "#min" do
116
+
117
+ it "returns the min value for the supplied field" do
118
+ @context.min(:number).should == 1
119
+ end
120
+
121
+ end
122
+
123
+ describe "#one" do
124
+
125
+ it "returns the first in the enumerable" do
126
+ @context.one.should == @london
127
+ end
128
+
129
+ end
130
+
131
+ describe "#page" do
132
+
133
+ context "when the page option exists" do
134
+
135
+ before do
136
+ @criteria = Mongoid::Criteria.new(Person).extras({ :page => 5 })
137
+ @context = Mongoid::Contexts::Enumerable.new({}, @criteria.options, [])
138
+ end
139
+
140
+ it "returns the page option" do
141
+ @context.page.should == 5
142
+ end
143
+
144
+ end
145
+
146
+ context "when the page option does not exist" do
147
+
148
+ before do
149
+ @criteria = Mongoid::Criteria.new(Person)
150
+ @context = Mongoid::Contexts::Enumerable.new({}, @criteria.options, [])
151
+ end
152
+
153
+ it "returns 1" do
154
+ @context.page.should == 1
155
+ end
156
+
157
+ end
158
+
159
+ end
160
+
161
+ describe "#paginate" do
162
+
163
+ before do
164
+ @criteria = Person.where(:_id => "1").skip(60).limit(20)
165
+ @context = Mongoid::Contexts::Enumerable.new(@criteria.selector, @criteria.options, [])
166
+ @results = @context.paginate
167
+ end
168
+
169
+ it "executes and paginates the results" do
170
+ @results.current_page.should == 4
171
+ @results.per_page.should == 20
172
+ end
173
+
174
+ end
175
+
176
+ describe "#per_page" do
177
+
178
+ context "when a limit option exists" do
179
+
180
+ it "returns 20" do
181
+ @context.per_page.should == 20
182
+ end
183
+
184
+ end
185
+
186
+ context "when a limit option does not exist" do
187
+
188
+ before do
189
+ @context = Mongoid::Contexts::Enumerable.new({}, { :limit => 50 }, [])
190
+ end
191
+
192
+ it "returns the limit" do
193
+ @context.per_page.should == 50
194
+ end
195
+
196
+ end
197
+
198
+ end
199
+
200
+ describe "#sum" do
201
+
202
+ it "returns the sum of all the field values" do
203
+ @context.sum(:number).should == 51
204
+ end
205
+
206
+ end
207
+
208
+ end
@@ -0,0 +1,370 @@
1
+ require "spec_helper"
2
+
3
+ describe Mongoid::Contexts::Mongo do
4
+
5
+ describe "#aggregate" do
6
+
7
+ before do
8
+ @selector = {}
9
+ @options = { :fields => [:field1] }
10
+ @context = Mongoid::Contexts::Mongo.new(@selector, @options, Person)
11
+ end
12
+
13
+ context "when klass not provided" do
14
+
15
+ before do
16
+ @reduce = "function(obj, prev) { prev.count++; }"
17
+ @collection = mock
18
+ Person.expects(:collection).returns(@collection)
19
+ end
20
+
21
+ it "calls group on the collection with the aggregate js" do
22
+ @collection.expects(:group).with([:field1], {}, {:count => 0}, @reduce, true)
23
+ @context.aggregate
24
+ end
25
+
26
+ end
27
+
28
+ end
29
+
30
+ describe "#count" do
31
+
32
+ before do
33
+ @selector = { :_type => { "$in" => ["Doctor", "Person"] }, :test => "Testing" }
34
+ @options = {}
35
+ @context = Mongoid::Contexts::Mongo.new(@selector, @options, Person)
36
+ end
37
+
38
+ context "when criteria has not been executed" do
39
+
40
+ before do
41
+ @context.instance_variable_set(:@count, 34)
42
+ end
43
+
44
+ it "returns a count from the cursor" do
45
+ @context.count.should == 34
46
+ end
47
+
48
+ end
49
+
50
+ context "when criteria has been executed" do
51
+
52
+ before do
53
+ @collection = mock
54
+ @cursor = mock
55
+ Person.expects(:collection).returns(@collection)
56
+ end
57
+
58
+ it "returns the count from the cursor without creating the documents" do
59
+ @collection.expects(:find).with(@selector, {}).returns(@cursor)
60
+ @cursor.expects(:count).returns(10)
61
+ @context.count.should == 10
62
+ end
63
+
64
+ end
65
+
66
+ end
67
+
68
+ describe "#group" do
69
+
70
+ before do
71
+ @selector = { :_type => { "$in" => ["Doctor", "Person"] } }
72
+ @options = { :fields => [ :field1 ] }
73
+ @grouping = [{ "title" => "Sir", "group" => [{ "title" => "Sir", "age" => 30, "_type" => "Person" }] }]
74
+ @context = Mongoid::Contexts::Mongo.new(@selector, @options, Person)
75
+ end
76
+
77
+ context "when klass provided" do
78
+
79
+ before do
80
+ @reduce = "function(obj, prev) { prev.group.push(obj); }"
81
+ @collection = mock
82
+ Person.expects(:collection).returns(@collection)
83
+ end
84
+
85
+ it "calls group on the collection with the aggregate js" do
86
+ @collection.expects(:group).with([:field1], {:_type => { "$in" => ["Doctor", "Person"] }}, {:group => []}, @reduce, true).returns(@grouping)
87
+ @context.group
88
+ end
89
+
90
+ end
91
+
92
+ end
93
+
94
+ describe "#execute" do
95
+
96
+ let(:selector) { { :field => "value" } }
97
+ let(:options) { { :skip => 20 } }
98
+
99
+ before do
100
+ @cursor = stub(:count => 500)
101
+ @collection = mock
102
+ @person = mock
103
+ @klass = stub(:collection => @collection, :hereditary => false, :instantiate => @person)
104
+ @context = Mongoid::Contexts::Mongo.new(selector, options, @klass)
105
+ end
106
+
107
+ it "calls find on the collection" do
108
+ @collection.expects(:find).with(selector, options).returns(@cursor)
109
+ @cursor.expects(:collect).yields({ :title => "Sir" }).returns([@person])
110
+ @context.execute.should == [@person]
111
+ end
112
+
113
+ context "when field options are supplied" do
114
+
115
+ context "when _type not in the field list" do
116
+
117
+ before do
118
+ options[:fields] = [ :title ]
119
+ @expected_options = { :skip => 20, :fields => [ :title, :_type ] }
120
+ end
121
+
122
+ it "adds _type to the fields" do
123
+ @collection.expects(:find).with(selector, @expected_options).returns(@cursor)
124
+ @cursor.expects(:collect).yields({ :title => "Sir" }).returns([@person])
125
+ @context.execute.should == [@person]
126
+ end
127
+
128
+ end
129
+
130
+ end
131
+
132
+ end
133
+
134
+ describe ".initialize" do
135
+
136
+ let(:selector) { { :field => "value" } }
137
+ let(:options) { { :skip => 20 } }
138
+ let(:klass) { Person }
139
+
140
+ before do
141
+ @context = Mongoid::Contexts::Mongo.new(selector, options, klass)
142
+ end
143
+
144
+ it "sets the selector" do
145
+ @context.selector.should == selector
146
+ end
147
+
148
+ it "sets the options" do
149
+ @context.options.should == options
150
+ end
151
+
152
+ it "sets the klass" do
153
+ @context.klass.should == klass
154
+ end
155
+
156
+ end
157
+
158
+ describe "#last" do
159
+
160
+ before do
161
+ @collection = mock
162
+ Person.expects(:collection).returns(@collection)
163
+ end
164
+
165
+ context "when documents exist" do
166
+
167
+ before do
168
+ @selector = {}
169
+ @options = { :sort => [[:title, :asc]] }
170
+ @context = Mongoid::Contexts::Mongo.new(@selector, @options, Person)
171
+ @collection.expects(:find_one).with(@selector, { :sort => [[:title, :desc]] }).returns(
172
+ { "title" => "Sir", "_type" => "Person" }
173
+ )
174
+ end
175
+
176
+ it "calls find on the collection with the selector and sort options reversed" do
177
+ @context.last.should be_a_kind_of(Person)
178
+ end
179
+
180
+ end
181
+
182
+ context "when no documents exist" do
183
+
184
+ before do
185
+ @selector = {}
186
+ @options = { :sort => [[:_id, :asc]] }
187
+ @context = Mongoid::Contexts::Mongo.new(@selector, @options, Person)
188
+ @collection.expects(:find_one).with(@selector, { :sort => [[:_id, :desc]] }).returns(nil)
189
+ end
190
+
191
+ it "returns nil" do
192
+ @context.last.should be_nil
193
+ end
194
+
195
+ end
196
+
197
+ context "when no sorting options provided" do
198
+
199
+ before do
200
+ @selector = {}
201
+ @options = { :sort => [[:_id, :asc]] }
202
+ @context = Mongoid::Contexts::Mongo.new(@selector, @options, Person)
203
+ @collection.expects(:find_one).with(@selector, { :sort => [[:_id, :desc]] }).returns(
204
+ { "title" => "Sir", "_type" => "Person" }
205
+ )
206
+ end
207
+
208
+ it "defaults to sort by id" do
209
+ @context.last
210
+ end
211
+
212
+ end
213
+
214
+ end
215
+
216
+ describe "#max" do
217
+
218
+ before do
219
+ @reduce = Mongoid::Contexts::Mongo::MAX_REDUCE.gsub("[field]", "age")
220
+ @collection = mock
221
+ Person.expects(:collection).returns(@collection)
222
+ @context = Mongoid::Contexts::Mongo.new({}, {}, Person)
223
+ end
224
+
225
+ it "calls group on the collection with the aggregate js" do
226
+ @collection.expects(:group).with(
227
+ nil,
228
+ {},
229
+ {:max => "start"},
230
+ @reduce,
231
+ true
232
+ ).returns([{"max" => 200.0}])
233
+ @context.max(:age).should == 200.0
234
+ end
235
+
236
+ end
237
+
238
+ describe "#min" do
239
+
240
+ before do
241
+ @reduce = Mongoid::Contexts::Mongo::MIN_REDUCE.gsub("[field]", "age")
242
+ @collection = mock
243
+ Person.expects(:collection).returns(@collection)
244
+ @context = Mongoid::Contexts::Mongo.new({}, {}, Person)
245
+ end
246
+
247
+ it "calls group on the collection with the aggregate js" do
248
+ @collection.expects(:group).with(
249
+ nil,
250
+ {},
251
+ {:min => "start"},
252
+ @reduce,
253
+ true
254
+ ).returns([{"min" => 4.0}])
255
+ @context.min(:age).should == 4.0
256
+ end
257
+
258
+ end
259
+
260
+ describe "#one" do
261
+
262
+ context "when documents exist" do
263
+
264
+ before do
265
+ @collection = mock
266
+ Person.expects(:collection).returns(@collection)
267
+ @context = Mongoid::Contexts::Mongo.new({}, {}, Person)
268
+ @collection.expects(:find_one).with({}, {}).returns(
269
+ { "title"=> "Sir", "_type" => "Person" }
270
+ )
271
+ end
272
+
273
+ it "calls find on the collection with the selector and options" do
274
+ @context.one.should be_a_kind_of(Person)
275
+ end
276
+
277
+ end
278
+
279
+ context "when no documents exist" do
280
+
281
+ before do
282
+ @collection = mock
283
+ Person.expects(:collection).returns(@collection)
284
+ @context = Mongoid::Contexts::Mongo.new({}, {}, Person)
285
+ @collection.expects(:find_one).with({}, {}).returns(nil)
286
+ end
287
+
288
+ it "returns nil" do
289
+ @context.one.should be_nil
290
+ end
291
+
292
+ end
293
+
294
+ end
295
+
296
+ describe "#page" do
297
+
298
+ context "when the page option exists" do
299
+
300
+ before do
301
+ @criteria = Mongoid::Criteria.new(Person).extras({ :page => 5 })
302
+ @context = Mongoid::Contexts::Mongo.new({}, @criteria.options, Person)
303
+ end
304
+
305
+ it "returns the page option" do
306
+ @context.page.should == 5
307
+ end
308
+
309
+ end
310
+
311
+ context "when the page option does not exist" do
312
+
313
+ before do
314
+ @criteria = Mongoid::Criteria.new(Person)
315
+ @context = Mongoid::Contexts::Mongo.new({}, @criteria.options, Person)
316
+ end
317
+
318
+ it "returns 1" do
319
+ @context.page.should == 1
320
+ end
321
+
322
+ end
323
+
324
+ end
325
+
326
+ describe "#paginate" do
327
+
328
+ before do
329
+ @collection = mock
330
+ Person.expects(:collection).returns(@collection)
331
+ @criteria = Person.where(:_id => "1").skip(60).limit(20)
332
+ @context = Mongoid::Contexts::Mongo.new(@criteria.selector, @criteria.options, Person)
333
+ @collection.expects(:find).with({:_type => { "$in" => ["Doctor", "Person"] }, :_id => "1"}, :skip => 60, :limit => 20).returns([])
334
+ @results = @context.paginate
335
+ end
336
+
337
+ it "executes and paginates the results" do
338
+ @results.current_page.should == 4
339
+ @results.per_page.should == 20
340
+ end
341
+
342
+ end
343
+
344
+ describe "#sum" do
345
+
346
+ context "when klass not provided" do
347
+
348
+ before do
349
+ @reduce = Mongoid::Contexts::Mongo::SUM_REDUCE.gsub("[field]", "age")
350
+ @collection = mock
351
+ @context = Mongoid::Contexts::Mongo.new({}, {}, Person)
352
+ Person.expects(:collection).returns(@collection)
353
+ end
354
+
355
+ it "calls group on the collection with the aggregate js" do
356
+ @collection.expects(:group).with(
357
+ nil,
358
+ {},
359
+ {:sum => "start"},
360
+ @reduce,
361
+ true
362
+ ).returns([{"sum" => 50.0}])
363
+ @context.sum(:age).should == 50.0
364
+ end
365
+
366
+ end
367
+
368
+ end
369
+
370
+ end