mongoid 1.1.4 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. data/HISTORY +69 -1
  2. data/Rakefile +1 -1
  3. data/VERSION +1 -1
  4. data/lib/mongoid.rb +39 -13
  5. data/lib/mongoid/associations.rb +1 -0
  6. data/lib/mongoid/associations/has_many.rb +19 -3
  7. data/lib/mongoid/attributes.rb +6 -1
  8. data/lib/mongoid/collection.rb +106 -0
  9. data/lib/mongoid/collections/cyclic_iterator.rb +34 -0
  10. data/lib/mongoid/collections/master.rb +28 -0
  11. data/lib/mongoid/collections/mimic.rb +46 -0
  12. data/lib/mongoid/collections/operations.rb +39 -0
  13. data/lib/mongoid/collections/slaves.rb +44 -0
  14. data/lib/mongoid/commands.rb +1 -0
  15. data/lib/mongoid/config.rb +61 -9
  16. data/lib/mongoid/contexts/enumerable.rb +24 -15
  17. data/lib/mongoid/contexts/mongo.rb +25 -31
  18. data/lib/mongoid/contexts/paging.rb +2 -2
  19. data/lib/mongoid/criteria.rb +48 -7
  20. data/lib/mongoid/criterion/exclusion.rb +2 -0
  21. data/lib/mongoid/criterion/optional.rb +2 -2
  22. data/lib/mongoid/cursor.rb +82 -0
  23. data/lib/mongoid/document.rb +12 -13
  24. data/lib/mongoid/errors.rb +35 -4
  25. data/lib/mongoid/extensions.rb +1 -0
  26. data/lib/mongoid/extensions/array/aliasing.rb +4 -0
  27. data/lib/mongoid/extensions/string/inflections.rb +44 -1
  28. data/lib/mongoid/factory.rb +17 -0
  29. data/lib/mongoid/finders.rb +7 -2
  30. data/lib/mongoid/matchers/default.rb +6 -0
  31. data/lib/mongoid/matchers/gt.rb +1 -1
  32. data/lib/mongoid/matchers/gte.rb +1 -1
  33. data/lib/mongoid/matchers/lt.rb +1 -1
  34. data/lib/mongoid/matchers/lte.rb +1 -1
  35. data/mongoid.gemspec +30 -5
  36. data/perf/benchmark.rb +5 -3
  37. data/spec/integration/mongoid/associations_spec.rb +12 -0
  38. data/spec/integration/mongoid/contexts/enumerable_spec.rb +20 -0
  39. data/spec/integration/mongoid/criteria_spec.rb +28 -0
  40. data/spec/integration/mongoid/document_spec.rb +1 -1
  41. data/spec/integration/mongoid/inheritance_spec.rb +2 -2
  42. data/spec/models/person.rb +1 -1
  43. data/spec/spec_helper.rb +9 -4
  44. data/spec/unit/mongoid/associations/has_many_spec.rb +19 -0
  45. data/spec/unit/mongoid/associations_spec.rb +9 -0
  46. data/spec/unit/mongoid/attributes_spec.rb +4 -4
  47. data/spec/unit/mongoid/collection_spec.rb +113 -0
  48. data/spec/unit/mongoid/collections/cyclic_iterator_spec.rb +75 -0
  49. data/spec/unit/mongoid/collections/master_spec.rb +41 -0
  50. data/spec/unit/mongoid/collections/mimic_spec.rb +43 -0
  51. data/spec/unit/mongoid/collections/slaves_spec.rb +81 -0
  52. data/spec/unit/mongoid/commands_spec.rb +7 -0
  53. data/spec/unit/mongoid/config_spec.rb +52 -1
  54. data/spec/unit/mongoid/contexts/enumerable_spec.rb +38 -12
  55. data/spec/unit/mongoid/contexts/mongo_spec.rb +38 -31
  56. data/spec/unit/mongoid/criteria_spec.rb +20 -12
  57. data/spec/unit/mongoid/criterion/exclusion_spec.rb +26 -0
  58. data/spec/unit/mongoid/criterion/optional_spec.rb +13 -0
  59. data/spec/unit/mongoid/cursor_spec.rb +74 -0
  60. data/spec/unit/mongoid/document_spec.rb +4 -5
  61. data/spec/unit/mongoid/errors_spec.rb +5 -9
  62. data/spec/unit/mongoid/extensions/string/inflections_spec.rb +21 -2
  63. data/spec/unit/mongoid/factory_spec.rb +16 -0
  64. data/spec/unit/mongoid_spec.rb +4 -4
  65. metadata +28 -3
@@ -0,0 +1,43 @@
1
+ require "spec_helper"
2
+
3
+ describe Mongoid::Collections::Mimic do
4
+
5
+ let(:mimic) do
6
+ klass = Class.new do
7
+ include Mongoid::Collections::Mimic
8
+ end
9
+ klass.new
10
+ end
11
+
12
+ describe "#attempt" do
13
+
14
+ before do
15
+ @operation = stub.quacks_like(Proc.new {})
16
+ end
17
+
18
+ context "when the call succeeds" do
19
+
20
+ it "returns the value" do
21
+ @operation.expects(:call).returns([])
22
+ mimic.attempt(@operation, Time.now).should == []
23
+ end
24
+ end
25
+
26
+ context "when the call fails" do
27
+
28
+ before do
29
+ Mongoid.reconnect_time = 0.10
30
+ end
31
+
32
+ after do
33
+ Mongoid.reconnect_time = 3
34
+ end
35
+
36
+ it "retries the call" do
37
+ @operation.expects(:call).at_least_once.raises(Mongo::ConnectionFailure.new)
38
+ lambda { mimic.attempt(@operation, Time.now) }.should raise_error
39
+ end
40
+ end
41
+
42
+ end
43
+ end
@@ -0,0 +1,81 @@
1
+ require "spec_helper"
2
+
3
+ describe Mongoid::Collections::Master do
4
+
5
+ let(:collection) do
6
+ stub.quacks_like(Mongo::Collection.allocate)
7
+ end
8
+
9
+ let(:db) do
10
+ stub.quacks_like(Mongo::DB.allocate)
11
+ end
12
+
13
+ describe "#empty?" do
14
+
15
+ context "when the slaves exist" do
16
+
17
+ let(:slave) do
18
+ Mongoid::Collections::Slaves.new([ db ], "people")
19
+ end
20
+
21
+ before do
22
+ db.expects(:collection).with("people").returns(collection)
23
+ end
24
+
25
+ it "returns false" do
26
+ slave.should_not be_empty
27
+ end
28
+ end
29
+
30
+ context "when the slaves do not exist" do
31
+
32
+ let(:slave) do
33
+ Mongoid::Collections::Slaves.new([], "people")
34
+ end
35
+
36
+ it "returns true" do
37
+ slave.should be_empty
38
+ end
39
+ end
40
+
41
+ context "when the slaves are nil" do
42
+
43
+ let(:slave) do
44
+ Mongoid::Collections::Slaves.new(nil, "people")
45
+ end
46
+
47
+ it "returns true" do
48
+ slave.should be_empty
49
+ end
50
+ end
51
+ end
52
+
53
+ context "Mongo::Collection operations" do
54
+
55
+ let(:slave) do
56
+ Mongoid::Collections::Slaves.new([ db ], "people")
57
+ end
58
+
59
+ before do
60
+ db.expects(:collection).with("people").returns(collection)
61
+ end
62
+
63
+ Mongoid::Collections::Operations::READ.each do |name|
64
+
65
+ it "defines #{name}" do
66
+ slave.should respond_to(name)
67
+ end
68
+
69
+ describe "##{name}" do
70
+
71
+ before do
72
+ collection.expects(name)
73
+ end
74
+
75
+ it "delegates to the collection" do
76
+ slave.send(name)
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
@@ -138,6 +138,13 @@ describe Mongoid::Commands do
138
138
  @person.update_attributes({})
139
139
  end
140
140
 
141
+ it "executes the before and after update callbacks" do
142
+ @person.expects(:run_callbacks).with(:before_update)
143
+ Mongoid::Commands::Save.expects(:execute).with(@person, true).returns(true)
144
+ @person.expects(:run_callbacks).with(:after_update)
145
+ @person.update_attributes({})
146
+ end
147
+
141
148
  end
142
149
 
143
150
  describe "#update_attributes!" do
@@ -8,7 +8,7 @@ describe Mongoid::Config do
8
8
 
9
9
  let(:config) { Mongoid::Config.instance }
10
10
 
11
- describe ".database=" do
11
+ describe "#database=" do
12
12
 
13
13
  context "when object provided is not a Mongo::DB" do
14
14
 
@@ -20,6 +20,25 @@ describe Mongoid::Config do
20
20
 
21
21
  end
22
22
 
23
+ describe "#master=" do
24
+
25
+ context "when object provided is not a Mongo::DB" do
26
+
27
+ it "raises an error" do
28
+ lambda { config.master = "Test" }.should raise_error
29
+ end
30
+
31
+ end
32
+
33
+ end
34
+
35
+ describe "#parameterize_keys" do
36
+
37
+ it "defaults to true" do
38
+ config.parameterize_keys.should == true
39
+ end
40
+ end
41
+
23
42
  describe "#persist_in_safe_mode=" do
24
43
 
25
44
  context "when setting to true" do
@@ -76,6 +95,38 @@ describe Mongoid::Config do
76
95
 
77
96
  end
78
97
 
98
+ describe "#reconnect_time" do
99
+
100
+ it "defaults to 3" do
101
+ config.reconnect_time.should == 3
102
+ end
103
+
104
+ end
105
+
106
+ describe "#reconnect_time=" do
107
+
108
+ after do
109
+ config.reconnect_time = 3
110
+ end
111
+
112
+ it "sets the time" do
113
+ config.reconnect_time = 5
114
+ config.reconnect_time.should == 5
115
+ end
116
+ end
117
+
118
+ describe "#slaves=" do
119
+
120
+ context "when object provided is not a Mongo::DB" do
121
+
122
+ it "raises an error" do
123
+ lambda { config.slaves = ["Test"] }.should raise_error
124
+ end
125
+
126
+ end
127
+
128
+ end
129
+
79
130
  describe "#allow_dynamic_fields=" do
80
131
 
81
132
  context "when setting to true" do
@@ -8,7 +8,7 @@ describe Mongoid::Contexts::Enumerable do
8
8
  @melbourne = Address.new(:number => 20, :street => "Bourke Street")
9
9
  @new_york = Address.new(:number => 20, :street => "Broadway")
10
10
  @docs = [ @london, @shanghai, @melbourne, @new_york ]
11
- @selector = { :street => "Bond Street" }
11
+ @selector = { :street => "Bourke Street" }
12
12
  @options = { :fields => [ :number ] }
13
13
  @context = Mongoid::Contexts::Enumerable.new(@selector, @options, @docs)
14
14
  end
@@ -41,15 +41,41 @@ describe Mongoid::Contexts::Enumerable do
41
41
  describe "#execute" do
42
42
 
43
43
  it "returns the matching documents from the array" do
44
- @context.execute.should == [ @london ]
44
+ @context.execute.should == [ @melbourne ]
45
+ end
46
+
47
+ context "when selector is empty" do
48
+
49
+ before do
50
+ @context = Mongoid::Contexts::Enumerable.new({}, @options, @docs)
51
+ end
52
+
53
+ it "returns all the documents" do
54
+ @context.execute.should == @docs
55
+ end
56
+ end
57
+
58
+ context "when skip and limit are in the options" do
59
+
60
+ before do
61
+ @options = { :skip => 2, :limit => 2 }
62
+ @context = Mongoid::Contexts::Enumerable.new({}, @options, @docs)
63
+ end
64
+
65
+ it "properly narrows down the matching results" do
66
+ @context.execute.should == [ @melbourne, @new_york ]
67
+ end
45
68
  end
46
69
 
47
70
  end
48
71
 
49
72
  describe "#first" do
50
73
 
51
- it "returns the first in the enumerable" do
52
- @context.first.should == @london
74
+ context "when a selector is present" do
75
+
76
+ it "returns the first that matches the selector" do
77
+ @context.first.should == @melbourne
78
+ end
53
79
  end
54
80
 
55
81
  end
@@ -98,8 +124,8 @@ describe Mongoid::Contexts::Enumerable do
98
124
 
99
125
  describe "#last" do
100
126
 
101
- it "returns the last in the enumerable" do
102
- @context.last.should == @new_york
127
+ it "returns the last matching in the enumerable" do
128
+ @context.last.should == @melbourne
103
129
  end
104
130
 
105
131
  end
@@ -122,8 +148,8 @@ describe Mongoid::Contexts::Enumerable do
122
148
 
123
149
  describe "#one" do
124
150
 
125
- it "returns the first in the enumerable" do
126
- @context.one.should == @london
151
+ it "returns the first matching in the enumerable" do
152
+ @context.one.should == @melbourne
127
153
  end
128
154
 
129
155
  end
@@ -161,14 +187,14 @@ describe Mongoid::Contexts::Enumerable do
161
187
  describe "#paginate" do
162
188
 
163
189
  before do
164
- @criteria = Person.where(:_id => "1").skip(60).limit(20)
165
- @context = Mongoid::Contexts::Enumerable.new(@criteria.selector, @criteria.options, [])
190
+ @criteria = Person.criteria.skip(2).limit(2)
191
+ @context = Mongoid::Contexts::Enumerable.new({}, @criteria.options, @docs)
166
192
  @results = @context.paginate
167
193
  end
168
194
 
169
195
  it "executes and paginates the results" do
170
- @results.current_page.should == 4
171
- @results.per_page.should == 20
196
+ @results.current_page.should == 2
197
+ @results.per_page.should == 2
172
198
  end
173
199
 
174
200
  end
@@ -65,32 +65,6 @@ describe Mongoid::Contexts::Mongo do
65
65
 
66
66
  end
67
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
68
  describe "#execute" do
95
69
 
96
70
  let(:selector) { { :field => "value" } }
@@ -99,15 +73,23 @@ describe Mongoid::Contexts::Mongo do
99
73
  before do
100
74
  @cursor = stub(:count => 500)
101
75
  @collection = mock
102
- @person = mock
103
76
  @klass = stub(:collection => @collection, :hereditary => false, :instantiate => @person)
104
77
  @context = Mongoid::Contexts::Mongo.new(selector, options, @klass)
105
78
  end
106
79
 
107
80
  it "calls find on the collection" do
108
81
  @collection.expects(:find).with(selector, options).returns(@cursor)
109
- @cursor.expects(:collect).yields({ :title => "Sir" }).returns([@person])
110
- @context.execute.should == [@person]
82
+ @context.execute.should == @cursor
83
+ end
84
+
85
+ context "when paginating" do
86
+
87
+ it "should find the count from the cursor" do
88
+ @collection.expects(:find).with(selector, options).returns(@cursor)
89
+ @context.execute(true).should == @cursor
90
+ @context.count.should == 500
91
+ end
92
+
111
93
  end
112
94
 
113
95
  context "when field options are supplied" do
@@ -121,8 +103,7 @@ describe Mongoid::Contexts::Mongo do
121
103
 
122
104
  it "adds _type to the fields" do
123
105
  @collection.expects(:find).with(selector, @expected_options).returns(@cursor)
124
- @cursor.expects(:collect).yields({ :title => "Sir" }).returns([@person])
125
- @context.execute.should == [@person]
106
+ @context.execute.should == @cursor
126
107
  end
127
108
 
128
109
  end
@@ -131,6 +112,32 @@ describe Mongoid::Contexts::Mongo do
131
112
 
132
113
  end
133
114
 
115
+ describe "#group" do
116
+
117
+ before do
118
+ @selector = { :_type => { "$in" => ["Doctor", "Person"] } }
119
+ @options = { :fields => [ :field1 ] }
120
+ @grouping = [{ "title" => "Sir", "group" => [{ "title" => "Sir", "age" => 30, "_type" => "Person" }] }]
121
+ @context = Mongoid::Contexts::Mongo.new(@selector, @options, Person)
122
+ end
123
+
124
+ context "when klass provided" do
125
+
126
+ before do
127
+ @reduce = "function(obj, prev) { prev.group.push(obj); }"
128
+ @collection = mock
129
+ Person.expects(:collection).returns(@collection)
130
+ end
131
+
132
+ it "calls group on the collection with the aggregate js" do
133
+ @collection.expects(:group).with([:field1], {:_type => { "$in" => ["Doctor", "Person"] }}, {:group => []}, @reduce, true).returns(@grouping)
134
+ @context.group
135
+ end
136
+
137
+ end
138
+
139
+ end
140
+
134
141
  describe ".initialize" do
135
142
 
136
143
  let(:selector) { { :field => "value" } }
@@ -11,7 +11,6 @@ describe Mongoid::Criteria do
11
11
 
12
12
  before do
13
13
  @sir = Person.new(:title => "Sir")
14
- @madam = Person.new(:title => "Madam")
15
14
  @canvas = Canvas.new
16
15
  end
17
16
 
@@ -19,14 +18,15 @@ describe Mongoid::Criteria do
19
18
 
20
19
  before do
21
20
  @collection = mock
22
- @cursor = stub(:count => 1, :collect => [ @sir, @madam ])
21
+ @cursor = stub(:count => 1)
22
+ @cursor.expects(:each).yields(@sir)
23
23
  Person.expects(:collection).returns(@collection)
24
24
  @collection.expects(:find).returns(@cursor)
25
25
  end
26
26
 
27
27
  it "executes the criteria and concats the results" do
28
28
  results = @criteria + [ @canvas ]
29
- results.should == [ @sir, @madam, @canvas ]
29
+ results.should == [ @sir, @canvas ]
30
30
  end
31
31
 
32
32
  end
@@ -71,7 +71,8 @@ describe Mongoid::Criteria do
71
71
 
72
72
  before do
73
73
  @collection = mock
74
- @cursor = stub(:count => 1, :collect => [ @sir, @sir, @madam ])
74
+ @cursor = stub(:count => 1)
75
+ @cursor.expects(:each).yields(@madam)
75
76
  Person.expects(:collection).returns(@collection)
76
77
  @collection.expects(:find).returns(@cursor)
77
78
  end
@@ -118,7 +119,8 @@ describe Mongoid::Criteria do
118
119
  @criteria.where(:title => "Sir")
119
120
  @collection = stub
120
121
  @person = Person.new(:title => "Sir")
121
- @cursor = stub(:count => 10, :collect => [@person])
122
+ @cursor = stub(:count => 10)
123
+ @cursor.expects(:each).yields(@person)
122
124
  end
123
125
 
124
126
  context "when the criteria has not been executed" do
@@ -227,13 +229,14 @@ describe Mongoid::Criteria do
227
229
  @collection = mock
228
230
  Person.expects(:collection).returns(@collection)
229
231
  @criteria = Mongoid::Criteria.new(Person).extras(:page => 1, :per_page => 20)
230
- @cursor = stub(:count => 44, :collect => [])
232
+ @cursor = stub(:count => 44)
233
+ @cursor.expects(:each)
231
234
  @collection.expects(:find).with(@criteria.selector, @criteria.options).returns(@cursor)
232
235
  end
233
236
 
234
- it "adds the count instance variable" do
237
+ it "does not add the count instance variable" do
235
238
  @criteria.entries.should == []
236
- @criteria.count.should == 44
239
+ @criteria.instance_variable_get(:@count).should be_nil
237
240
  end
238
241
 
239
242
  end
@@ -272,7 +275,7 @@ describe Mongoid::Criteria do
272
275
  @criteria.where(:title => "Sir")
273
276
  @collection = stub
274
277
  @person = Person.new(:title => "Sir")
275
- @cursor = stub(:count => 10, :collect => [@person])
278
+ @cursor = stub(:count => 10)
276
279
  end
277
280
 
278
281
  context "when the criteria has not been executed" do
@@ -280,11 +283,12 @@ describe Mongoid::Criteria do
280
283
  before do
281
284
  Person.expects(:collection).returns(@collection)
282
285
  @collection.expects(:find).with({ :_type => { "$in" => ["Doctor", "Person"] }, :title => "Sir" }, {}).returns(@cursor)
286
+ @cursor.expects(:each).yields(@person)
283
287
  end
284
288
 
285
289
  it "executes the criteria" do
286
- @criteria.each do |person|
287
- person.should == @person
290
+ @criteria.each do |doc|
291
+ doc.should == @person
288
292
  end
289
293
  end
290
294
 
@@ -295,10 +299,10 @@ describe Mongoid::Criteria do
295
299
  before do
296
300
  Person.expects(:collection).returns(@collection)
297
301
  @collection.expects(:find).with({ :_type => { "$in" => ["Doctor", "Person"] }, :title => "Sir" }, {}).returns(@cursor)
302
+ @cursor.expects(:each).yields(@person)
298
303
  end
299
304
 
300
305
  it "calls each on the existing results" do
301
- @criteria.each
302
306
  @criteria.each do |person|
303
307
  person.should == @person
304
308
  end
@@ -523,6 +527,10 @@ describe Mongoid::Criteria do
523
527
 
524
528
  end
525
529
 
530
+ describe "#offset" do
531
+
532
+ end
533
+
526
534
  describe "#one" do
527
535
 
528
536
  before do