mongo_doc 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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