mongo_doc 0.3.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 (122) hide show
  1. data/.document +5 -0
  2. data/.gitignore +7 -0
  3. data/LICENSE +20 -0
  4. data/README.textile +174 -0
  5. data/Rakefile +135 -0
  6. data/TODO +31 -0
  7. data/VERSION +1 -0
  8. data/data/.gitignore +2 -0
  9. data/examples/simple_document.rb +35 -0
  10. data/examples/simple_object.rb +30 -0
  11. data/features/finders.feature +76 -0
  12. data/features/mongodb.yml +7 -0
  13. data/features/mongodoc_base.feature +128 -0
  14. data/features/new_record.feature +36 -0
  15. data/features/partial_updates.feature +105 -0
  16. data/features/removing_documents.feature +68 -0
  17. data/features/saving_an_object.feature +15 -0
  18. data/features/scopes.feature +66 -0
  19. data/features/step_definitions/collection_steps.rb +14 -0
  20. data/features/step_definitions/document_steps.rb +149 -0
  21. data/features/step_definitions/documents.rb +30 -0
  22. data/features/step_definitions/finder_steps.rb +15 -0
  23. data/features/step_definitions/json_steps.rb +9 -0
  24. data/features/step_definitions/object_steps.rb +50 -0
  25. data/features/step_definitions/objects.rb +24 -0
  26. data/features/step_definitions/partial_update_steps.rb +32 -0
  27. data/features/step_definitions/query_steps.rb +54 -0
  28. data/features/step_definitions/removing_documents_steps.rb +14 -0
  29. data/features/step_definitions/scope_steps.rb +18 -0
  30. data/features/step_definitions/util_steps.rb +7 -0
  31. data/features/support/support.rb +10 -0
  32. data/features/using_criteria.feature +128 -0
  33. data/lib/mongo_doc/associations/collection_proxy.rb +105 -0
  34. data/lib/mongo_doc/associations/document_proxy.rb +56 -0
  35. data/lib/mongo_doc/associations/hash_proxy.rb +98 -0
  36. data/lib/mongo_doc/associations/proxy_base.rb +53 -0
  37. data/lib/mongo_doc/attributes.rb +140 -0
  38. data/lib/mongo_doc/bson.rb +45 -0
  39. data/lib/mongo_doc/collection.rb +55 -0
  40. data/lib/mongo_doc/connection.rb +88 -0
  41. data/lib/mongo_doc/contexts/enumerable.rb +128 -0
  42. data/lib/mongo_doc/contexts/ids.rb +41 -0
  43. data/lib/mongo_doc/contexts/mongo.rb +232 -0
  44. data/lib/mongo_doc/contexts.rb +25 -0
  45. data/lib/mongo_doc/criteria.rb +38 -0
  46. data/lib/mongo_doc/cursor.rb +32 -0
  47. data/lib/mongo_doc/document.rb +216 -0
  48. data/lib/mongo_doc/ext/array.rb +5 -0
  49. data/lib/mongo_doc/ext/binary.rb +7 -0
  50. data/lib/mongo_doc/ext/boolean_class.rb +11 -0
  51. data/lib/mongo_doc/ext/date.rb +16 -0
  52. data/lib/mongo_doc/ext/date_time.rb +13 -0
  53. data/lib/mongo_doc/ext/dbref.rb +7 -0
  54. data/lib/mongo_doc/ext/hash.rb +7 -0
  55. data/lib/mongo_doc/ext/nil_class.rb +5 -0
  56. data/lib/mongo_doc/ext/numeric.rb +17 -0
  57. data/lib/mongo_doc/ext/object.rb +17 -0
  58. data/lib/mongo_doc/ext/object_id.rb +7 -0
  59. data/lib/mongo_doc/ext/regexp.rb +5 -0
  60. data/lib/mongo_doc/ext/string.rb +5 -0
  61. data/lib/mongo_doc/ext/symbol.rb +5 -0
  62. data/lib/mongo_doc/ext/time.rb +5 -0
  63. data/lib/mongo_doc/finders.rb +49 -0
  64. data/lib/mongo_doc/matchers.rb +35 -0
  65. data/lib/mongo_doc/query.rb +7 -0
  66. data/lib/mongo_doc/scope.rb +64 -0
  67. data/lib/mongo_doc/validations/macros.rb +11 -0
  68. data/lib/mongo_doc/validations/validates_embedded.rb +13 -0
  69. data/lib/mongo_doc.rb +19 -0
  70. data/lib/mongoid/contexts/paging.rb +42 -0
  71. data/lib/mongoid/criteria.rb +247 -0
  72. data/lib/mongoid/criterion/complex.rb +21 -0
  73. data/lib/mongoid/criterion/exclusion.rb +65 -0
  74. data/lib/mongoid/criterion/inclusion.rb +92 -0
  75. data/lib/mongoid/criterion/optional.rb +136 -0
  76. data/lib/mongoid/extensions/hash/criteria_helpers.rb +20 -0
  77. data/lib/mongoid/extensions/symbol/inflections.rb +36 -0
  78. data/lib/mongoid/matchers/all.rb +11 -0
  79. data/lib/mongoid/matchers/default.rb +26 -0
  80. data/lib/mongoid/matchers/exists.rb +13 -0
  81. data/lib/mongoid/matchers/gt.rb +11 -0
  82. data/lib/mongoid/matchers/gte.rb +11 -0
  83. data/lib/mongoid/matchers/in.rb +11 -0
  84. data/lib/mongoid/matchers/lt.rb +11 -0
  85. data/lib/mongoid/matchers/lte.rb +11 -0
  86. data/lib/mongoid/matchers/ne.rb +11 -0
  87. data/lib/mongoid/matchers/nin.rb +11 -0
  88. data/lib/mongoid/matchers/size.rb +11 -0
  89. data/mongo_doc.gemspec +205 -0
  90. data/mongod.example.yml +2 -0
  91. data/mongodb.example.yml +14 -0
  92. data/perf/mongo_doc_runner.rb +90 -0
  93. data/perf/ruby_driver_runner.rb +64 -0
  94. data/script/console +8 -0
  95. data/spec/associations/collection_proxy_spec.rb +200 -0
  96. data/spec/associations/document_proxy_spec.rb +42 -0
  97. data/spec/associations/hash_proxy_spec.rb +163 -0
  98. data/spec/attributes_spec.rb +273 -0
  99. data/spec/bson_matchers.rb +54 -0
  100. data/spec/bson_spec.rb +196 -0
  101. data/spec/collection_spec.rb +161 -0
  102. data/spec/connection_spec.rb +147 -0
  103. data/spec/contexts/enumerable_spec.rb +274 -0
  104. data/spec/contexts/ids_spec.rb +49 -0
  105. data/spec/contexts/mongo_spec.rb +198 -0
  106. data/spec/contexts_spec.rb +28 -0
  107. data/spec/criteria_spec.rb +33 -0
  108. data/spec/cursor_spec.rb +91 -0
  109. data/spec/document_ext.rb +9 -0
  110. data/spec/document_spec.rb +664 -0
  111. data/spec/embedded_save_spec.rb +109 -0
  112. data/spec/finders_spec.rb +73 -0
  113. data/spec/hash_matchers.rb +27 -0
  114. data/spec/matchers_spec.rb +342 -0
  115. data/spec/mongodb.yml +6 -0
  116. data/spec/mongodb_pairs.yml +8 -0
  117. data/spec/new_record_spec.rb +128 -0
  118. data/spec/query_spec.rb +12 -0
  119. data/spec/scope_spec.rb +79 -0
  120. data/spec/spec.opts +2 -0
  121. data/spec/spec_helper.rb +13 -0
  122. metadata +290 -0
@@ -0,0 +1,274 @@
1
+ require "spec_helper"
2
+
3
+ describe MongoDoc::Contexts::Enumerable do
4
+
5
+ class Address
6
+ include MongoDoc::Document
7
+ include MongoDoc::Matchers
8
+
9
+ key :number
10
+ key :street
11
+ end
12
+
13
+ before do
14
+ @london = Address.new(:number => 1, :street => "Bond Street")
15
+ @shanghai = Address.new(:number => 10, :street => "Nan Jing Dong Lu")
16
+ @melbourne = Address.new(:number => 20, :street => "Bourke Street")
17
+ @new_york = Address.new(:number => 20, :street => "Broadway")
18
+ @docs = [ @london, @shanghai, @melbourne, @new_york ]
19
+ @criteria = Mongoid::Criteria.new(Address)
20
+ @criteria.documents = @docs
21
+ @criteria.where(:street => "Bourke Street").only(:number)
22
+ @context = MongoDoc::Contexts::Enumerable.new(@criteria)
23
+ end
24
+
25
+ describe "#aggregate" do
26
+
27
+ before do
28
+ @counts = @context.aggregate
29
+ end
30
+
31
+ it "groups by the fields provided in the options" do
32
+ @counts.size.should == 3
33
+ end
34
+
35
+ it "stores the counts in proper groups" do
36
+ @counts[1].should == 1
37
+ @counts[10].should == 1
38
+ @counts[20].should == 2
39
+ end
40
+ end
41
+
42
+ describe "#count" do
43
+
44
+ it "returns the size of the enumerable" do
45
+ @context.count.should == 4
46
+ end
47
+
48
+ end
49
+
50
+ describe "#execute" do
51
+
52
+ before do
53
+ @criteria = Mongoid::Criteria.new(Address)
54
+ @criteria.documents = @docs
55
+ end
56
+
57
+ it "returns the matching documents from the array" do
58
+ @context.execute.should == [ @melbourne ]
59
+ end
60
+
61
+ context "when selector is empty" do
62
+
63
+ before do
64
+ @criteria.only(:number)
65
+ @context = MongoDoc::Contexts::Enumerable.new(@criteria)
66
+ end
67
+
68
+ it "returns all the documents" do
69
+ @context.execute.should == @docs
70
+ end
71
+ end
72
+
73
+ context "when skip and limit are in the options" do
74
+
75
+ before do
76
+ @criteria.skip(2).limit(2)
77
+ @context = MongoDoc::Contexts::Enumerable.new(@criteria)
78
+ end
79
+
80
+ it "properly narrows down the matching results" do
81
+ @context.execute.should == [ @melbourne, @new_york ]
82
+ end
83
+ end
84
+
85
+ end
86
+
87
+ describe "#first" do
88
+
89
+ context "when a selector is present" do
90
+
91
+ it "returns the first that matches the selector" do
92
+ @context.first.should == @melbourne
93
+ end
94
+ end
95
+
96
+ end
97
+
98
+ describe "#group" do
99
+
100
+ before do
101
+ @group = @context.group
102
+ end
103
+
104
+ it "groups by the fields provided in the options" do
105
+ @group.size.should == 3
106
+ end
107
+
108
+ it "stores the documents in proper groups" do
109
+ @group[1].should == [ @london ]
110
+ @group[10].should == [ @shanghai ]
111
+ @group[20].should == [ @melbourne, @new_york ]
112
+ end
113
+
114
+ end
115
+
116
+ describe "#initialize" do
117
+
118
+ let(:selector) { { :field => "value" } }
119
+ let(:options) { { :skip => 20 } }
120
+ let(:documents) { [stub] }
121
+
122
+ before do
123
+ @criteria = Mongoid::Criteria.new(Address)
124
+ @criteria.documents = documents
125
+ @criteria.where(selector).skip(20)
126
+ @context = MongoDoc::Contexts::Enumerable.new(@criteria)
127
+ end
128
+
129
+ it "sets the selector" do
130
+ @context.selector.should == selector
131
+ end
132
+
133
+ it "sets the options" do
134
+ @context.options.should == options
135
+ end
136
+
137
+ it "sets the documents" do
138
+ @context.documents.should == documents
139
+ end
140
+
141
+ end
142
+
143
+ describe "#iterator" do
144
+
145
+ before do
146
+ @criteria.where(:street => "Bourke Street")
147
+ @criteria.documents = @docs
148
+ @context = MongoDoc::Contexts::Enumerable.new(@criteria)
149
+ end
150
+
151
+ it "executes the criteria" do
152
+ acc = []
153
+ @context.iterate do |doc|
154
+ acc << doc
155
+ end
156
+ acc.should == [@melbourne]
157
+ end
158
+
159
+ end
160
+
161
+ describe "#last" do
162
+
163
+ it "returns the last matching in the enumerable" do
164
+ @context.last.should == @melbourne
165
+ end
166
+
167
+ end
168
+
169
+ describe "#max" do
170
+
171
+ it "returns the max value for the supplied field" do
172
+ @context.max(:number).should == 20
173
+ end
174
+
175
+ end
176
+
177
+ describe "#min" do
178
+
179
+ it "returns the min value for the supplied field" do
180
+ @context.min(:number).should == 1
181
+ end
182
+
183
+ end
184
+
185
+ describe "#one" do
186
+
187
+ it "returns the first matching in the enumerable" do
188
+ @context.one.should == @melbourne
189
+ end
190
+
191
+ end
192
+
193
+ describe "#page" do
194
+
195
+ context "when the page option exists" do
196
+
197
+ before do
198
+ @criteria = Mongoid::Criteria.new(Address).extras({ :page => 5 })
199
+ @criteria.documents = []
200
+ @context = MongoDoc::Contexts::Enumerable.new(@criteria)
201
+ end
202
+
203
+ it "returns the page option" do
204
+ @context.page.should == 5
205
+ end
206
+
207
+ end
208
+
209
+ context "when the page option does not exist" do
210
+
211
+ before do
212
+ @criteria = Mongoid::Criteria.new(Address)
213
+ @criteria.documents = []
214
+ @context = MongoDoc::Contexts::Enumerable.new(@criteria)
215
+ end
216
+
217
+ it "returns 1" do
218
+ @context.page.should == 1
219
+ end
220
+
221
+ end
222
+
223
+ end
224
+
225
+ describe "#paginate" do
226
+
227
+ before do
228
+ @criteria = Mongoid::Criteria.new(Address).skip(2).limit(2)
229
+ @context = MongoDoc::Contexts::Enumerable.new(@criteria)
230
+ @results = @context.paginate
231
+ end
232
+
233
+ it "executes and paginates the results" do
234
+ @results.current_page.should == 2
235
+ @results.per_page.should == 2
236
+ end
237
+
238
+ end
239
+
240
+ describe "#per_page" do
241
+
242
+ context "when a limit option exists" do
243
+
244
+ it "returns 20" do
245
+ @context.per_page.should == 20
246
+ end
247
+
248
+ end
249
+
250
+ context "when a limit option does not exist" do
251
+
252
+ before do
253
+ @criteria = Mongoid::Criteria.new(Address).limit(50)
254
+ @criteria.documents = []
255
+ @context = MongoDoc::Contexts::Enumerable.new(@criteria)
256
+ end
257
+
258
+ it "returns the limit" do
259
+ @context.per_page.should == 50
260
+ end
261
+
262
+ end
263
+
264
+ end
265
+
266
+ describe "#sum" do
267
+
268
+ it "returns the sum of all the field values" do
269
+ @context.sum(:number).should == 51
270
+ end
271
+
272
+ end
273
+
274
+ end
@@ -0,0 +1,49 @@
1
+ require 'spec_helper'
2
+
3
+ describe "MongoDoc::Contexts::Ids" do
4
+
5
+ class Address
6
+ include MongoDoc::Document
7
+ include MongoDoc::Matchers
8
+
9
+ key :number
10
+ key :street
11
+ end
12
+
13
+ let(:criteria) { Mongoid::Criteria.new(Address) }
14
+ let(:context) { criteria.context }
15
+
16
+ context "#id_criteria" do
17
+ context "single id" do
18
+ let(:id) { 'a' * 24 }
19
+ let(:obj_id) { Mongo::ObjectID.from_string(id) }
20
+
21
+ it "converts strings to an object id" do
22
+ criteria.should_receive(:id).with(obj_id)
23
+ context.stub(:one)
24
+ context.id_criteria(id)
25
+ end
26
+
27
+ it "delegates to one if passed a string or ObjectID" do
28
+ context.should_receive(:one)
29
+ context.id_criteria(id)
30
+ end
31
+ end
32
+
33
+ context "mutliple ids" do
34
+ let(:ids) { ['a' * 24, 'b' * 24] }
35
+ let(:obj_ids) { [Mongo::ObjectID.from_string(ids.first), Mongo::ObjectID.from_string(ids.last)] }
36
+
37
+ it "converts strings to an object id" do
38
+ criteria.should_receive(:id).with(obj_ids)
39
+ criteria.stub(:entries)
40
+ context.id_criteria(ids)
41
+ end
42
+
43
+ it "delegates to entries if passed an array" do
44
+ criteria.should_receive(:entries)
45
+ context.id_criteria(ids)
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,198 @@
1
+ require 'spec_helper'
2
+
3
+ describe "MongoDoc::Contexts::MongoDoc" do
4
+
5
+ class Address
6
+ include MongoDoc::Document
7
+ include MongoDoc::Matchers
8
+
9
+ key :number
10
+ key :street
11
+ end
12
+
13
+ let(:criteria) { Mongoid::Criteria.new(Address) }
14
+ let(:context) { criteria.context }
15
+
16
+ context "#initialize" do
17
+ it "sets the criteria" do
18
+ context.criteria.should == criteria
19
+ end
20
+ end
21
+
22
+ context "#collection" do
23
+ it "delegates to klass" do
24
+ klass = mock('klass', :collection => true)
25
+ context.should_receive(:klass).and_return(klass)
26
+ context.collection
27
+ end
28
+ end
29
+
30
+ context "querying" do
31
+ let(:collection) { stub('collection') }
32
+
33
+ before { context.stub(:collection).and_return(collection) }
34
+
35
+ context "#aggregate" do
36
+ it "uses group with the appropriate JS" do
37
+ collection.should_receive(:group).with(nil, {}, {:count=>0}, MongoDoc::Contexts::Mongo::AGGREGATE_REDUCE, true)
38
+ context.aggregate
39
+ end
40
+ end
41
+
42
+ context "#count" do
43
+ it "uses find and count" do
44
+ result = stub('result')
45
+ result.should_receive(:count)
46
+ collection.should_receive(:find).and_return(result)
47
+ context.count
48
+ end
49
+ end
50
+
51
+ context "#execute" do
52
+ it "uses find" do
53
+ collection.should_receive(:find)
54
+ context.execute
55
+ end
56
+
57
+ it "returns [] if nothing returned from find" do
58
+ collection.stub(:find => nil)
59
+ context.execute.should == []
60
+ end
61
+
62
+ it "returns the cursor if one is returned from find" do
63
+ cursor = stub('cursor')
64
+ collection.stub(:find => cursor)
65
+ context.execute.should == cursor
66
+ end
67
+
68
+ it "memoizes the count if paginating" do
69
+ count = 20
70
+ cursor = stub('cursor', :count => count)
71
+ collection.stub(:find => cursor)
72
+ context.execute
73
+ context.count.should == count
74
+ end
75
+ end
76
+
77
+ context "#group" do
78
+ it "uses group with the appropriate JS" do
79
+ collection.should_receive(:group).with(nil, {}, {:group=>[]}, MongoDoc::Contexts::Mongo::GROUP_REDUCE, true).and_return([])
80
+ context.group
81
+ end
82
+
83
+ it "decodes returned documents" do
84
+ doc = stub('doc')
85
+ collection.stub(:group).and_return([{:group => [doc]}])
86
+ MongoDoc::BSON.should_receive(:decode).and_return(doc)
87
+ context.group
88
+ end
89
+ end
90
+
91
+ context "#iterate" do
92
+
93
+ it "delegates to caching if cached" do
94
+ context.should_receive(:caching)
95
+ criteria.cache
96
+ context.iterate
97
+ end
98
+
99
+ it "iterates over the results of execute" do
100
+ item = stub('item')
101
+ context.stub(:execute).and_return([item])
102
+ context.iterate do |doc|
103
+ doc.should == item
104
+ end
105
+ end
106
+
107
+ context "#caching" do
108
+ let(:item) { stub('item') }
109
+
110
+ before do
111
+ criteria.cache
112
+ end
113
+
114
+ context "when not previously cached" do
115
+ it "iterates over the results of execute" do
116
+ context.stub(:execute).and_return([item])
117
+ context.iterate do |doc|
118
+ doc.should == item
119
+ end
120
+ end
121
+
122
+ it "memoizes the result" do
123
+ context.stub(:execute).and_return([item])
124
+ context.iterate
125
+ context.cache.should == [item]
126
+ end
127
+ end
128
+
129
+ context "when previously cached" do
130
+ before do
131
+ context.instance_variable_set(:@cache, [item])
132
+ end
133
+
134
+ it "does not execute" do
135
+ context.should_not_receive(:execute)
136
+ context.iterate do |doc|
137
+ doc.should == item
138
+ end
139
+ end
140
+
141
+ it "iterates over the results of execute" do
142
+ context.should_not_receive(:execute)
143
+ acc = []
144
+ context.iterate do |doc|
145
+ acc << doc
146
+ end
147
+ acc.should == [item]
148
+ end
149
+ end
150
+ end
151
+ end
152
+
153
+ context "#last" do
154
+ it "delegates to find_one" do
155
+ collection.should_receive(:find_one).with({}, {:sort=>[[:_id, :desc]]})
156
+ context.last
157
+ end
158
+ end
159
+
160
+ context "#max" do
161
+ it "delegates to grouped" do
162
+ context.should_receive(:grouped).with(:max, "number", MongoDoc::Contexts::Mongo::MAX_REDUCE)
163
+ context.max(:number)
164
+ end
165
+ end
166
+
167
+ context "#min" do
168
+ it "delegates to grouped" do
169
+ context.should_receive(:grouped).with(:min, "number", MongoDoc::Contexts::Mongo::MIN_REDUCE)
170
+ context.min(:number)
171
+ end
172
+ end
173
+
174
+ context "#one" do
175
+ it "delegates to find_one" do
176
+ collection.should_receive(:find_one).with({}, {})
177
+ context.one
178
+ end
179
+ end
180
+
181
+ context "#sum" do
182
+ it "delegates to grouped" do
183
+ context.should_receive(:grouped).with(:sum, "number", MongoDoc::Contexts::Mongo::SUM_REDUCE)
184
+ context.sum(:number)
185
+ end
186
+ end
187
+
188
+ context "#grouped" do
189
+ it "delegates to group" do
190
+ op = 'op'
191
+ field = 'name'
192
+ reduce = '[field]'
193
+ collection.should_receive(:group).with(nil, {}, {op =>"start"}, field, true).and_return([])
194
+ context.send(:grouped, op, field, reduce)
195
+ end
196
+ end
197
+ end
198
+ end
@@ -0,0 +1,28 @@
1
+ require "spec_helper"
2
+
3
+ describe MongoDoc::Contexts do
4
+
5
+ context ".context_for" do
6
+ let(:criteria) { stub('criteria', :klass => klass) }
7
+
8
+ context "when criteria is for a top-level MongoDoc::Document" do
9
+ let(:klass) { stub('klass', :collection => stub('collection')) }
10
+
11
+ it "creates a MongoDoc context" do
12
+ MongoDoc::Contexts::Mongo.should_receive(:new).with(criteria)
13
+ Mongoid::Contexts.context_for(criteria)
14
+ end
15
+ end
16
+
17
+ context "when criteria is for an embedded MongoDoc::Document" do
18
+ let(:klass) { stub('klass') }
19
+
20
+ it "creates an Enumerable context" do
21
+ MongoDoc::Contexts::Enumerable.should_receive(:new).with(criteria)
22
+ Mongoid::Contexts.context_for(criteria)
23
+ end
24
+ end
25
+ end
26
+
27
+ end
28
+
@@ -0,0 +1,33 @@
1
+ require 'spec_helper'
2
+
3
+ describe MongoDoc::Criteria do
4
+
5
+ class CriteriaTest
6
+ extend MongoDoc::Criteria
7
+ end
8
+
9
+ context ".criteria" do
10
+ it "creates a new criteria for the document" do
11
+ CriteriaTest.criteria.should be_a_kind_of(Mongoid::Criteria)
12
+ end
13
+
14
+ it "sets the criteria klass" do
15
+ CriteriaTest.criteria.klass.should == CriteriaTest
16
+ end
17
+ end
18
+
19
+ context "criteria delegates" do
20
+ let(:criteria) { stub('criteria').as_null_object }
21
+
22
+ before do
23
+ CriteriaTest.stub(:criteria).and_return(criteria)
24
+ end
25
+
26
+ %w(and cache enslave excludes extras id in limit not_in offset only order_by page per_page skip where).each do |criteria_op|
27
+ it "#{criteria_op} delegates to the criteria" do
28
+ criteria.should_receive(criteria_op)
29
+ CriteriaTest.send(criteria_op)
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,91 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe "MongoDoc::Cursor" do
4
+ let(:mongo_cursor) { stub('cursor') }
5
+
6
+ let(:collection) { stub('collection') }
7
+
8
+ let(:cursor) { MongoDoc::Cursor.new(collection, mongo_cursor) }
9
+
10
+ it "is Enumerable" do
11
+ Enumerable.should === cursor
12
+ end
13
+
14
+ it ".new wraps a Mongo::Cursor" do
15
+ cursor._cursor.should == mongo_cursor
16
+ end
17
+
18
+ it "#collection returns the MongoDoc::Collection for this cursor" do
19
+ cursor.collection.should == collection
20
+ cursor._collection.should == collection
21
+ end
22
+
23
+ context "with the underlying cursor" do
24
+ %w(admin close closed? count explain fields full_collection_name hint limit order query_options_hash query_opts selector skip snapshot sort timeout).each do |delegated_method|
25
+ it "delegates #{delegated_method} to the Mongo::Cursor" do
26
+ mongo_cursor.should_receive(delegated_method)
27
+ cursor.send(delegated_method)
28
+ end
29
+ end
30
+ end
31
+
32
+ context "#each" do
33
+ it "delegates to the cursor" do
34
+ mongo_cursor.should_receive(:each)
35
+ cursor.each
36
+ end
37
+
38
+ it "decodes the return from the delegate" do
39
+ bson = stub('bson')
40
+ cursor.stub(:_cursor).and_return([bson])
41
+ MongoDoc::BSON.should_receive(:decode).with(bson)
42
+ cursor.each {}
43
+ end
44
+
45
+ it "calls the block with the decoded return" do
46
+ result = stub('bson')
47
+ cursor.stub(:_cursor).and_return([result])
48
+ MongoDoc::BSON.stub(:decode).and_return(result)
49
+ cursor.each {|obj| @obj = obj}
50
+ @obj.should == result
51
+ end
52
+ end
53
+
54
+ context "#next_document" do
55
+ it "delegates to the cursor" do
56
+ mongo_cursor.should_receive(:next_document)
57
+ cursor.next_document
58
+ end
59
+
60
+ it "decodes the return from the delegate" do
61
+ bson = stub('bson')
62
+ mongo_cursor.stub(:next_document).and_return(bson)
63
+ MongoDoc::BSON.should_receive(:decode).with(bson)
64
+ cursor.next_document
65
+ end
66
+
67
+ it "returns nil if the delegate returns nil" do
68
+ mongo_cursor.stub(:next_document)
69
+ cursor.next_document.should be_nil
70
+ end
71
+ end
72
+
73
+ context "#to_a" do
74
+ it "delegates to the cursor" do
75
+ mongo_cursor.should_receive(:to_a)
76
+ cursor.to_a
77
+ end
78
+
79
+ it "decodes the return from the delegate" do
80
+ array = stub('array')
81
+ mongo_cursor.stub(:to_a).and_return(array)
82
+ MongoDoc::BSON.should_receive(:decode).with(array)
83
+ cursor.to_a
84
+ end
85
+
86
+ it "returns [] if the delegate returns []" do
87
+ mongo_cursor.stub(:to_a).and_return([])
88
+ cursor.to_a.should == []
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,9 @@
1
+ module MongoDoc
2
+ module Document
3
+ def errors_on(attribute)
4
+ self.valid?
5
+ [self.errors.on(attribute)].flatten.compact
6
+ end
7
+ alias :error_on :errors_on
8
+ end
9
+ end