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,42 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
2
+
3
+ describe "MongoDoc::Associations::DocumentProxy" do
4
+ class Parent
5
+ include MongoDoc::Document
6
+ end
7
+
8
+ class Child
9
+ include MongoDoc::Document
10
+ end
11
+
12
+ let(:parent) { Parent.new }
13
+ let(:name) {'association'}
14
+
15
+ subject do
16
+ MongoDoc::Associations::DocumentProxy.new(:assoc_name => name, :root => parent, :parent => parent, :assoc_class => Child)
17
+ end
18
+
19
+ it "has the association name" do
20
+ subject.assoc_name.should == name
21
+ end
22
+
23
+ it "has the parent" do
24
+ subject._parent.should == parent
25
+ end
26
+
27
+ it "has the root" do
28
+ subject._root.should == parent
29
+ end
30
+
31
+ it "has the association class" do
32
+ subject.assoc_class.should == Child
33
+ end
34
+
35
+ it "inserts the association name the _path_to_root" do
36
+ subject._path_to_root(Child.new, :name1 => 'value1', :name2 => 'value2').should == {"#{name}.name1" => 'value1', "#{name}.name2" => "value2"}
37
+ end
38
+
39
+ it "#build builds a new object" do
40
+ Child.should === subject.build({})
41
+ end
42
+ end
@@ -0,0 +1,163 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', '/spec_helper'))
2
+
3
+ describe MongoDoc::Associations::HashProxy do
4
+ class HashProxyTest
5
+ include MongoDoc::Document
6
+
7
+ key :name
8
+ end
9
+
10
+ let(:root) { stub('root', :register_save_observer => nil) }
11
+ let(:proxy) { MongoDoc::Associations::HashProxy.new(:assoc_name => 'has_many_name', :assoc_class => HashProxyTest, :root => root, :parent => root) }
12
+ let(:item) { HashProxyTest.new }
13
+ let(:other_item) {[1,2]}
14
+
15
+ context "#[]=" do
16
+ it "adds the item to the hash" do
17
+ proxy['new'] = item
18
+ proxy['new'].should == item
19
+ end
20
+
21
+ context "key names must be a string or symbol constrained by BSON element name" do
22
+ ['$invalid', 'in.valid', :_id, 'query', 1, Object.new].each do |name|
23
+ it "#{name} is invalid" do
24
+ expect do
25
+ proxy[name] = other_item
26
+ end.to raise_error(MongoDoc::InvalidEmbeddedHashKey)
27
+ end
28
+ end
29
+
30
+ [:key, 'key'].each do |name|
31
+ it "#{name} is a valid name" do
32
+ expect do
33
+ proxy[name] = other_item
34
+ end.to_not raise_error(MongoDoc::InvalidEmbeddedHashKey)
35
+ end
36
+ end
37
+ end
38
+
39
+ context "when the item is not a MongoDoc::Document" do
40
+
41
+ it "does not register a save observer" do
42
+ root.should_not_receive(:register_save_observer)
43
+ proxy['new'] = other_item
44
+ end
45
+
46
+ it "does not set the root" do
47
+ other_item.should_not_receive(:_root=)
48
+ proxy['new'] = other_item
49
+ end
50
+ end
51
+
52
+ context "when the item is a MongoDoc::Document" do
53
+ it "registers a save observer" do
54
+ root.should_receive(:register_save_observer)
55
+ proxy['new'] = item
56
+ end
57
+
58
+ it "sets the root" do
59
+ proxy['new'] = item
60
+ item._root.should == root
61
+ end
62
+ end
63
+ end
64
+
65
+ context "#merge!" do
66
+ context "when the key value is not a MongoDoc::Document" do
67
+
68
+ it "does not register a save observer" do
69
+ root.should_not_receive(:register_save_observer)
70
+ proxy.merge!(:new => other_item)
71
+ end
72
+
73
+ it "does not set the root" do
74
+ other_item.should_not_receive(:_root=)
75
+ proxy.merge!(:new => other_item)
76
+ end
77
+ end
78
+
79
+ context "when the key value is a MongoDoc::Document" do
80
+ it "registers a save observer" do
81
+ root.should_receive(:register_save_observer)
82
+ proxy.merge!(:new => item)
83
+ end
84
+
85
+ it "sets the root" do
86
+ proxy.merge!(:new => item)
87
+ item._root.should == root
88
+ end
89
+ end
90
+
91
+ context "with a block" do
92
+ it "calls into the block" do
93
+ proxy.merge!(:new => other_item) {|k, v1, v2| @result = v2}
94
+ @result.should == other_item
95
+ end
96
+
97
+ context "when the key value is not a MongoDoc::Document" do
98
+
99
+ it "does not register a save observer" do
100
+ root.should_not_receive(:register_save_observer)
101
+ proxy.merge!(:new => other_item) {|k, v1, v2| v2}
102
+ end
103
+
104
+ it "does not set the root" do
105
+ other_item.should_not_receive(:_root=)
106
+ proxy.merge!(:new => other_item) {|k, v1, v2| v2}
107
+ end
108
+ end
109
+
110
+ context "when the key value is a MongoDoc::Document" do
111
+ it "registers a save observer" do
112
+ root.should_receive(:register_save_observer)
113
+ proxy.merge!(:new => item) {|k, v1, v2| v2}
114
+ end
115
+
116
+ it "sets the root" do
117
+ proxy.merge!(:new => item) {|k, v1, v2| v2}
118
+ item._root.should == root
119
+ end
120
+ end
121
+ end
122
+ end
123
+
124
+ context "#replace" do
125
+ it "clears any existing data" do
126
+ proxy.should_receive(:clear)
127
+ proxy.replace(:new => other_item)
128
+ end
129
+
130
+ context "when the key value is not a MongoDoc::Document" do
131
+
132
+ it "does not register a save observer" do
133
+ root.should_not_receive(:register_save_observer)
134
+ proxy.replace(:new => other_item)
135
+ end
136
+
137
+ it "does not set the root" do
138
+ other_item.should_not_receive(:_root=)
139
+ proxy.replace(:new => other_item)
140
+ end
141
+ end
142
+
143
+ context "when the key value is a MongoDoc::Document" do
144
+ it "registers a save observer" do
145
+ root.should_receive(:register_save_observer)
146
+ proxy.replace(:new => item)
147
+ end
148
+
149
+ it "sets the root" do
150
+ proxy.replace(:new => item)
151
+ item._root.should == root
152
+ end
153
+ end
154
+
155
+ end
156
+
157
+ context "#build" do
158
+ it "builds an object of the collection class from the hash attrs" do
159
+ name = 'built'
160
+ proxy.build(:key, {:name => name}).name.should == name
161
+ end
162
+ end
163
+ end
@@ -0,0 +1,273 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe "MongoDoc::Attributes" do
4
+ class AttributesTest
5
+ include MongoDoc::Attributes
6
+ end
7
+
8
+ it "defines _id attribute" do
9
+ AttributesTest.new.should respond_to(:_id)
10
+ AttributesTest.new.should respond_to(:_id=)
11
+ end
12
+
13
+ context ".key" do
14
+ class TestKeys
15
+ include MongoDoc::Document
16
+ end
17
+
18
+ it "adds its arguments to _keys" do
19
+ TestKeys.key :attr1, :attr2
20
+ TestKeys._keys.should == [:attr1, :attr2]
21
+ end
22
+
23
+ describe "accessors" do
24
+ before do
25
+ TestKeys.key :attr1
26
+ end
27
+
28
+ subject do
29
+ TestKeys.new
30
+ end
31
+ it "has an attr1 reader" do
32
+ should respond_to(:attr1)
33
+ end
34
+
35
+ it "has an attr1 writer" do
36
+ should respond_to(:attr1=)
37
+ end
38
+ end
39
+
40
+ describe "used with inheritance" do
41
+ class TestParent
42
+ include MongoDoc::Document
43
+
44
+ key :parent_attr
45
+ end
46
+
47
+ class TestChild < TestParent
48
+ key :child_attr
49
+ end
50
+
51
+ it "has its own keys" do
52
+ TestChild._keys.should include(:child_attr)
53
+ end
54
+
55
+ it "has the keys from the parent class" do
56
+ TestChild._keys.should include(*TestParent._keys)
57
+ end
58
+
59
+ it "does not add keys to the parent class" do
60
+ TestParent._keys.should_not include(:child_attr)
61
+ end
62
+ end
63
+ end
64
+
65
+ context ".has_one" do
66
+ class TestDoc
67
+ include MongoDoc::Document
68
+
69
+ has_one :sub_doc
70
+ end
71
+
72
+ class SubDoc
73
+ include MongoDoc::Document
74
+
75
+ key :data
76
+ end
77
+
78
+ let(:subdoc) { SubDoc.new }
79
+ let(:doc) { TestDoc.new(:sub_doc => subdoc) }
80
+
81
+ it "uses a proxy" do
82
+ MongoDoc::Associations::DocumentProxy.should === doc.sub_doc
83
+ end
84
+
85
+ it "sets the subdocuments parent to the proxy" do
86
+ doc.sub_doc.should == subdoc._parent
87
+ end
88
+
89
+ it "set the subdocuments root" do
90
+ doc.should == subdoc._root
91
+ end
92
+
93
+ context "validations" do
94
+ class HasOneValidationTest
95
+ include MongoDoc::Document
96
+
97
+ key :data
98
+ validates_presence_of :data
99
+ end
100
+
101
+ it "cascades validations down" do
102
+ invalid = HasOneValidationTest.new
103
+ TestDoc.new(:sub_doc => invalid).should have(1).error_on(:sub_doc)
104
+ end
105
+ end
106
+ end
107
+
108
+ context "._attributes" do
109
+ class TestHasOneDoc
110
+ include MongoDoc::Document
111
+
112
+ key :key
113
+ has_one :has_one
114
+ end
115
+
116
+ it "is _keys + _associations" do
117
+ TestHasOneDoc._attributes.should == TestHasOneDoc._keys + TestHasOneDoc._associations
118
+ end
119
+ end
120
+
121
+ context ".has_many" do
122
+
123
+ class SubHasManyDoc
124
+ include MongoDoc::Document
125
+
126
+ key :data
127
+ end
128
+
129
+ class TestHasManyDoc
130
+ include MongoDoc::Document
131
+
132
+ has_many :sub_docs, :class_name => 'SubHasManyDoc'
133
+ end
134
+
135
+ class TestHasManyDoc2
136
+ include MongoDoc::Document
137
+
138
+ has_many :sub_docs, :class_name => :sub_has_many_doc
139
+ end
140
+
141
+ class TestImplicitHasManyDoc
142
+ include MongoDoc::Document
143
+
144
+ has_many :sub_has_many_docs
145
+ end
146
+
147
+ let(:subdoc) { SubHasManyDoc.new }
148
+ let(:doc) { TestHasManyDoc.new(:sub_docs => [subdoc]) }
149
+
150
+ it "uses a proxy" do
151
+ MongoDoc::Associations::CollectionProxy.should === TestHasManyDoc.new.sub_docs
152
+ end
153
+
154
+ it "sets the subdocuments parent to the proxy" do
155
+ doc.sub_docs.should == subdoc._parent
156
+ end
157
+
158
+ it "set the subdocuments root to the root" do
159
+ doc.should == subdoc._root
160
+ end
161
+
162
+ it "uses the association name to find the children's class name" do
163
+ TestImplicitHasManyDoc.new.sub_has_many_docs.assoc_class.should == SubHasManyDoc
164
+ end
165
+
166
+ it "uses class_name attribute for the children's class name" do
167
+ TestHasManyDoc.new.sub_docs.assoc_class.should == SubHasManyDoc
168
+ end
169
+
170
+ it "uses class_name attribute for the children's class name" do
171
+ TestHasManyDoc2.new.sub_docs.assoc_class.should == SubHasManyDoc
172
+ end
173
+
174
+ context "validations" do
175
+ class HasManyValidationChild
176
+ include MongoDoc::Document
177
+
178
+ key :data
179
+ validates_presence_of :data
180
+ end
181
+
182
+ class HasManyValidationTest
183
+ include MongoDoc::Document
184
+
185
+ has_many :subdocs, :class_name => 'HasManyValidationChild'
186
+ end
187
+
188
+ let(:invalid_child) { HasManyValidationChild.new }
189
+ let(:doc) { HasManyValidationTest.new(:subdocs => [invalid_child]) }
190
+
191
+ it "cascades validations and marks it in the parent" do
192
+ doc.should have(1).error_on(:subdocs)
193
+ end
194
+
195
+ it "cascades validations and marks it in the child" do
196
+ invalid_child.should have(1).error_on(:data)
197
+ end
198
+
199
+ it "ignores non-document children" do
200
+ HasManyValidationTest.new(:subdocs => ['not a doc']).should be_valid
201
+ end
202
+ end
203
+ end
204
+
205
+ context ".has_hash" do
206
+ class SubHasHashDoc
207
+ include MongoDoc::Document
208
+
209
+ key :data
210
+ end
211
+
212
+ class TestHasHashDoc
213
+ include MongoDoc::Document
214
+
215
+ has_hash :sub_docs, :class_name => 'SubHasHashDoc'
216
+ end
217
+
218
+ class TestImplicitHasHashDoc
219
+ include MongoDoc::Document
220
+
221
+ has_hash :sub_has_hash_docs
222
+ end
223
+
224
+ let(:subdoc) { SubHasHashDoc.new }
225
+ let(:doc) { TestHasHashDoc.new(:sub_docs => {:key => subdoc}) }
226
+
227
+ it "uses a proxy" do
228
+ MongoDoc::Associations::HashProxy.should === TestHasHashDoc.new.sub_docs
229
+ end
230
+
231
+ it "sets the subdocuments parent to the proxy" do
232
+ doc.sub_docs.should == subdoc._parent
233
+ end
234
+
235
+ it "set the subdocuments root to the root" do
236
+ doc.should == subdoc._root
237
+ end
238
+
239
+ it "uses the association name to find the children's class name" do
240
+ TestImplicitHasHashDoc.new.sub_has_hash_docs.assoc_class.should == SubHasHashDoc
241
+ end
242
+
243
+ context "validations" do
244
+ class HasHashValidationChild
245
+ include MongoDoc::Document
246
+
247
+ key :data
248
+ validates_presence_of :data
249
+ end
250
+
251
+ class HasHashValidationTest
252
+ include MongoDoc::Document
253
+
254
+ has_hash :subdocs, :class_name => 'HasHashValidationChild'
255
+ end
256
+
257
+ let(:invalid_child) { HasHashValidationChild.new }
258
+ let(:doc) { HasHashValidationTest.new(:subdocs => {:key => invalid_child}) }
259
+
260
+ it "cascades validations and marks it in the parent" do
261
+ doc.should have(1).error_on(:subdocs)
262
+ end
263
+
264
+ it "cascades validations and marks it in the child" do
265
+ invalid_child.should have(1).error_on(:data)
266
+ end
267
+
268
+ it "ignores non-document children" do
269
+ HasHashValidationTest.new(:subdocs => {:key => 'data'}).should be_valid
270
+ end
271
+ end
272
+ end
273
+ end
@@ -0,0 +1,54 @@
1
+ module BsonMatchers
2
+ class BeBsonEql
3
+ def initialize(expected)
4
+ @expected = expected
5
+ end
6
+
7
+ def matches?(target)
8
+ @target = target
9
+ @target == @expected
10
+ end
11
+
12
+ def failure_message
13
+ "expected\...#{@target.inspect}\n" +
14
+ "to be BSON code equivalent to\...#{@expected.inspect}\n" +
15
+ "Difference:\...#{@expected.diff(@target).inspect}"
16
+ end
17
+
18
+ def negative_failure_message
19
+ "expected\...#{@target.inspect}\n" +
20
+ "to be BSON code different from\...#{@expected.inspect}"
21
+ end
22
+ end
23
+
24
+ class BeMongoEql
25
+ def initialize(expected, include_id)
26
+ @include_id = include_id
27
+ @expected = include_id ? expected : expected.except('_id')
28
+ end
29
+
30
+ def matches?(target)
31
+ @target = @include_id ? target : target.except('_id')
32
+ @target == @expected
33
+ end
34
+
35
+ def failure_message
36
+ "expected\...#{@target.inspect}\n" +
37
+ "to be BSON code equivalent to\...#{@expected.inspect}\n" +
38
+ "Difference:\...#{@expected.diff(@target).inspect}"
39
+ end
40
+
41
+ def negative_failure_message
42
+ "expected\...#{@target.inspect}\n" +
43
+ "to be BSON code different from\...#{@expected.inspect}"
44
+ end
45
+ end
46
+
47
+ def be_bson_eql(expected)
48
+ BeBsonEql.new(expected)
49
+ end
50
+
51
+ def be_mongo_eql(expected, include_id = true)
52
+ BeMongoEql.new(expected, include_id)
53
+ end
54
+ end