mongodoc 0.2.1 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. data/README.textile +42 -12
  2. data/Rakefile +4 -4
  3. data/TODO +26 -0
  4. data/VERSION +1 -1
  5. data/examples/simple_document.rb +1 -1
  6. data/examples/simple_object.rb +0 -2
  7. data/features/mongodb.yml +6 -5
  8. data/features/removing_documents.feature +68 -0
  9. data/features/step_definitions/collection_steps.rb +3 -3
  10. data/features/step_definitions/document_steps.rb +2 -2
  11. data/features/step_definitions/removing_documents_steps.rb +14 -0
  12. data/features/support/support.rb +2 -2
  13. data/lib/mongodoc.rb +4 -7
  14. data/lib/mongodoc/associations/collection_proxy.rb +103 -0
  15. data/lib/mongodoc/associations/document_proxy.rb +53 -0
  16. data/lib/mongodoc/associations/hash_proxy.rb +96 -0
  17. data/lib/mongodoc/associations/proxy_base.rb +51 -0
  18. data/lib/mongodoc/attributes.rb +49 -17
  19. data/lib/mongodoc/collection.rb +15 -5
  20. data/lib/mongodoc/connection.rb +83 -20
  21. data/lib/mongodoc/criteria.rb +9 -4
  22. data/lib/mongodoc/cursor.rb +9 -3
  23. data/lib/mongodoc/document.rb +37 -24
  24. data/lib/mongodoc/validations/macros.rb +11 -0
  25. data/lib/mongodoc/validations/validates_embedded.rb +13 -0
  26. data/mongodb.example.yml +13 -5
  27. data/mongodoc.gemspec +33 -23
  28. data/spec/associations/collection_proxy_spec.rb +200 -0
  29. data/spec/associations/document_proxy_spec.rb +42 -0
  30. data/spec/associations/hash_proxy_spec.rb +163 -0
  31. data/spec/attributes_spec.rb +113 -47
  32. data/spec/bson_spec.rb +24 -24
  33. data/spec/collection_spec.rb +67 -86
  34. data/spec/connection_spec.rb +98 -150
  35. data/spec/criteria_spec.rb +4 -3
  36. data/spec/cursor_spec.rb +33 -27
  37. data/spec/document_spec.rb +173 -156
  38. data/spec/embedded_save_spec.rb +8 -3
  39. data/spec/new_record_spec.rb +33 -121
  40. metadata +80 -39
  41. data/lib/mongodoc/parent_proxy.rb +0 -44
  42. data/lib/mongodoc/proxy.rb +0 -83
  43. data/spec/parent_proxy_spec.rb +0 -44
  44. data/spec/proxy_spec.rb +0 -80
@@ -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
@@ -66,7 +66,7 @@ describe "MongoDoc::Attributes" do
66
66
  class TestDoc
67
67
  include MongoDoc::Document
68
68
 
69
- has_one :subdoc
69
+ has_one :sub_doc
70
70
  end
71
71
 
72
72
  class SubDoc
@@ -75,39 +75,33 @@ describe "MongoDoc::Attributes" do
75
75
  key :data
76
76
  end
77
77
 
78
- it "sets the subdocuments parent to the parent proxy" do
79
- subdoc = SubDoc.new
80
- doc = TestDoc.new(:subdoc => subdoc)
81
- MongoDoc::ParentProxy.should === subdoc._parent
82
- subdoc._parent._parent.should == doc
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
83
  end
84
84
 
85
- it "set the subdocuments root" do
86
- subdoc = SubDoc.new
87
- middoc = TestDoc.new
88
- doc = TestDoc.new(:subdoc => middoc)
89
- middoc.subdoc = subdoc
90
- subdoc._root.should == doc
85
+ it "sets the subdocuments parent to the proxy" do
86
+ doc.sub_doc.should == subdoc._parent
91
87
  end
92
88
 
93
- it "sets the subdocuments root no matter how when it is inserted" do
94
- subdoc = SubDoc.new
95
- middoc = TestDoc.new(:subdoc => subdoc)
96
- doc = TestDoc.new(:subdoc => middoc)
97
- subdoc._root.should == doc
89
+ it "set the subdocuments root" do
90
+ doc.should == subdoc._root
98
91
  end
99
92
 
100
- class HasOneValidationTest
101
- include MongoDoc::Document
93
+ context "validations" do
94
+ class HasOneValidationTest
95
+ include MongoDoc::Document
102
96
 
103
- key :data
104
- validates_presence_of :data
105
- end
97
+ key :data
98
+ validates_presence_of :data
99
+ end
106
100
 
107
- it "cascades validations down" do
108
- invalid = HasOneValidationTest.new
109
- doc = TestDoc.new(:subdoc => invalid)
110
- doc.should have(1).error_on(:subdoc)
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
111
105
  end
112
106
  end
113
107
 
@@ -144,50 +138,122 @@ describe "MongoDoc::Attributes" do
144
138
  has_many :sub_has_many_docs
145
139
  end
146
140
 
141
+ let(:subdoc) { SubHasManyDoc.new }
142
+ let(:doc) { TestHasManyDoc.new(:sub_docs => [subdoc]) }
143
+
147
144
  it "uses a proxy" do
148
- MongoDoc::Proxy.should === TestHasManyDoc.new.sub_docs
145
+ MongoDoc::Associations::CollectionProxy.should === TestHasManyDoc.new.sub_docs
149
146
  end
150
147
 
151
148
  it "sets the subdocuments parent to the proxy" do
152
- subdoc = SubHasManyDoc.new
153
- doc = TestHasManyDoc.new(:sub_docs => [subdoc])
154
- subdoc._parent.should == doc.sub_docs
149
+ doc.sub_docs.should == subdoc._parent
155
150
  end
156
151
 
157
152
  it "set the subdocuments root to the root" do
158
- subdoc = SubHasManyDoc.new
159
- doc = TestHasManyDoc.new(:sub_docs => [subdoc])
160
- subdoc._root.should == doc
153
+ doc.should == subdoc._root
161
154
  end
162
155
 
163
156
  it "uses the association name to find the children's class name" do
164
- subdoc = SubHasManyDoc.new
165
- doc = TestImplicitHasManyDoc.new(:sub_has_many_docs => [subdoc])
157
+ TestImplicitHasManyDoc.new.sub_has_many_docs.assoc_class.should == SubHasManyDoc
166
158
  end
167
159
 
168
- class HasManyValidationChild
160
+ context "validations" do
161
+ class HasManyValidationChild
162
+ include MongoDoc::Document
163
+
164
+ key :data
165
+ validates_presence_of :data
166
+ end
167
+
168
+ class HasManyValidationTest
169
+ include MongoDoc::Document
170
+
171
+ has_many :subdocs, :class_name => 'HasManyValidationChild'
172
+ end
173
+
174
+ let(:invalid_child) { HasHashValidationChild.new }
175
+ let(:doc) { HasHashValidationTest.new(:subdocs => {:key => invalid_child}) }
176
+
177
+ it "cascades validations and marks it in the parent" do
178
+ doc.should have(1).error_on(:subdocs)
179
+ end
180
+
181
+ it "cascades validations and marks it in the child" do
182
+ invalid_child.should have(1).error_on(:data)
183
+ end
184
+
185
+ it "ignores non-document children" do
186
+ HasManyValidationTest.new(:subdocs => ['not a doc']).should be_valid
187
+ end
188
+ end
189
+ end
190
+
191
+ context ".has_hash" do
192
+ class SubHasHashDoc
169
193
  include MongoDoc::Document
170
194
 
171
195
  key :data
172
- validates_presence_of :data
173
196
  end
174
197
 
175
- class HasManyValidationTest
198
+ class TestHasHashDoc
176
199
  include MongoDoc::Document
177
200
 
178
- has_many :subdocs, :class_name => 'HasManyValidationChild'
201
+ has_hash :sub_docs, :class_name => 'SubHasHashDoc'
179
202
  end
180
203
 
181
- it "cascades validations and marks it in the parent" do
182
- invalid = HasManyValidationChild.new
183
- doc = HasManyValidationTest.new(:subdocs => [invalid])
184
- doc.should have(1).error_on(:subdocs)
204
+ class TestImplicitHasHashDoc
205
+ include MongoDoc::Document
206
+
207
+ has_hash :sub_has_hash_docs
208
+ end
209
+
210
+ let(:subdoc) { SubHasHashDoc.new }
211
+ let(:doc) { TestHasHashDoc.new(:sub_docs => {:key => subdoc}) }
212
+
213
+ it "uses a proxy" do
214
+ MongoDoc::Associations::HashProxy.should === TestHasHashDoc.new.sub_docs
215
+ end
216
+
217
+ it "sets the subdocuments parent to the proxy" do
218
+ doc.sub_docs.should == subdoc._parent
219
+ end
220
+
221
+ it "set the subdocuments root to the root" do
222
+ doc.should == subdoc._root
223
+ end
224
+
225
+ it "uses the association name to find the children's class name" do
226
+ TestImplicitHasHashDoc.new.sub_has_hash_docs.assoc_class.should == SubHasHashDoc
185
227
  end
186
228
 
187
- it "cascades validations and marks it in the child" do
188
- invalid = HasManyValidationChild.new
189
- doc = HasManyValidationTest.new(:subdocs => [invalid])
190
- invalid.should have(1).error_on(:data)
229
+ context "validations" do
230
+ class HasHashValidationChild
231
+ include MongoDoc::Document
232
+
233
+ key :data
234
+ validates_presence_of :data
235
+ end
236
+
237
+ class HasHashValidationTest
238
+ include MongoDoc::Document
239
+
240
+ has_hash :subdocs, :class_name => 'HasHashValidationChild'
241
+ end
242
+
243
+ let(:invalid_child) { HasHashValidationChild.new }
244
+ let(:doc) { HasHashValidationTest.new(:subdocs => {:key => invalid_child}) }
245
+
246
+ it "cascades validations and marks it in the parent" do
247
+ doc.should have(1).error_on(:subdocs)
248
+ end
249
+
250
+ it "cascades validations and marks it in the child" do
251
+ invalid_child.should have(1).error_on(:data)
252
+ end
253
+
254
+ it "ignores non-document children" do
255
+ HasHashValidationTest.new(:subdocs => {:key => 'data'}).should be_valid
256
+ end
191
257
  end
192
258
  end
193
259
  end
@@ -21,7 +21,7 @@ describe "BSON for Mongo (BSON)" do
21
21
  MongoDoc::BSON.should_receive(:array_create).with(array, options)
22
22
  MongoDoc::BSON.decode(array, options)
23
23
  end
24
-
24
+
25
25
  it "returns the json value as is if the parameter is not a hash or array" do
26
26
  ["", 1, 1.5, true, false, nil].each do |type_value|
27
27
  MongoDoc::BSON.decode(type_value).should == type_value
@@ -37,7 +37,7 @@ describe "BSON for Mongo (BSON)" do
37
37
  MongoDoc::BSON.should_receive(:decode).with(first, options)
38
38
  MongoDoc::BSON.array_create(array, options)
39
39
  end
40
-
40
+
41
41
  it "just returns the array if the :raw_json option is used" do
42
42
  hash = {'key' => 'value', MongoDoc::BSON::CLASS_KEY => 'Date'}
43
43
  array = [hash]
@@ -45,32 +45,32 @@ describe "BSON for Mongo (BSON)" do
45
45
  MongoDoc::BSON.array_create(array, :raw_json => true).should == array
46
46
  end
47
47
  end
48
-
48
+
49
49
  describe "#bson_create" do
50
50
  it "leaves a simple hash intact" do
51
51
  hash = {}
52
52
  MongoDoc::BSON.bson_create(hash).should == hash
53
53
  end
54
-
54
+
55
55
  it "a class hash extracts the class, and calls class.bson_create" do
56
56
  base_hash = {'key' => 'value'}
57
57
  bson_hash = base_hash.merge(MongoDoc::BSON::CLASS_KEY => 'Date')
58
58
  Date.should_receive(:bson_create).with(base_hash, {})
59
59
  MongoDoc::BSON.bson_create(bson_hash)
60
60
  end
61
-
61
+
62
62
  it "ignores a class hash when the :raw_json option is used" do
63
63
  hash = {'key' => 'value', MongoDoc::BSON::CLASS_KEY => 'Date'}
64
64
  MongoDoc::BSON.bson_create(hash, :raw_json => true).should == hash
65
65
  end
66
66
  end
67
-
67
+
68
68
  describe "Hash" do
69
69
  it "#to_bson returns the hash" do
70
70
  hash = {'key' => 1}
71
71
  hash.to_bson.should == hash
72
72
  end
73
-
73
+
74
74
  it "#to_bson returns the hash with symbol keys as strings" do
75
75
  {:key => 1}.to_bson.should == {"key" => 1}
76
76
  end
@@ -79,45 +79,45 @@ describe "BSON for Mongo (BSON)" do
79
79
  hash = {'key' => 1}
80
80
  MongoDoc::BSON.decode(hash.to_bson).should == hash
81
81
  end
82
-
82
+
83
83
  it "decodes the values of the hash" do
84
84
  hash = {'key' => {'subkey' => Date.today}}
85
85
  MongoDoc::BSON.decode(hash.to_bson).should == hash
86
86
  end
87
87
  end
88
-
88
+
89
89
  describe "Array" do
90
90
  it "#to_bson returns the array" do
91
91
  array = ['string', 1]
92
92
  array.to_bson.should == array
93
93
  end
94
-
94
+
95
95
  it "#to_bson iterates over its elements" do
96
96
  array = []
97
97
  array.should_receive(:map)
98
98
  array.to_bson
99
99
  end
100
-
100
+
101
101
  it "decodes to an array" do
102
102
  array = ['string', 1]
103
103
  MongoDoc::BSON.decode(array.to_bson).should == array
104
104
  end
105
105
  end
106
-
106
+
107
107
  describe "Extensions to core classes" do
108
108
  it "#to_bson for objects that are BSON native return themselves" do
109
109
  [true, false, nil, 1.0, 1, /regexp/, 'string', :symbol, Time.now].each do |native|
110
110
  native.to_bson.should == native
111
111
  end
112
112
  end
113
-
113
+
114
114
  it "objects that are BSON native decode to themselves" do
115
115
  [true, false, nil, 1.0, 1, /regexp/, 'string', :symbol, Time.now].each do |native|
116
116
  hash = {'native' => native}
117
117
  MongoDoc::BSON.decode(hash.to_bson).should == hash
118
118
  end
119
119
  end
120
-
120
+
121
121
  it "Date#to_bson returns a date hash" do
122
122
  date = Date.today
123
123
  date.to_bson.should == {MongoDoc::BSON::CLASS_KEY => "Date", "dt" => date.strftime, "sg" => date.respond_to?(:start) ? date.start : date.sg}
@@ -127,40 +127,40 @@ describe "BSON for Mongo (BSON)" do
127
127
  date = Date.today
128
128
  MongoDoc::BSON.decode(date.to_bson).should == date
129
129
  end
130
-
130
+
131
131
  it "DateTime#to_bson returns a datetime hash" do
132
132
  datetime = DateTime.now
133
133
  datetime.to_bson.should == {MongoDoc::BSON::CLASS_KEY => "DateTime", "dt" => datetime.strftime, "sg" => datetime.respond_to?(:start) ? datetime.start : datetime.sg}
134
134
  end
135
-
135
+
136
136
  it "roundtrips DateTime" do
137
137
  datetime = DateTime.now
138
138
  MongoDoc::BSON.decode(datetime.to_bson).to_s.should == datetime.to_s
139
139
  end
140
140
  end
141
-
141
+
142
142
  describe "Mongo Classes" do
143
- [Mongo::ObjectID.new, Mongo::DBRef.new('ns', 1), Mongo::Code.new('code'), Mongo::Binary.new, Mongo::RegexpOfHolding.new('a', 'i', 'g')].each do |obj|
143
+ [Mongo::ObjectID.new, Mongo::DBRef.new('ns', 1), Mongo::Code.new('code'), Mongo::Binary.new].each do |obj|
144
144
  it "#to_bson for #{obj.class.name} returns self" do
145
145
  obj.to_bson.should == obj
146
146
  end
147
-
147
+
148
148
  it "objects of type #{obj.class.name} decode to themselves" do
149
149
  hash = {"mongo" => obj}
150
150
  MongoDoc::BSON.decode(hash.to_bson)["mongo"].should == obj
151
151
  end
152
152
  end
153
153
  end
154
-
154
+
155
155
  describe "Extensions to Object" do
156
156
  class Simple
157
157
  attr_accessor :value
158
158
  end
159
-
159
+
160
160
  class Complex
161
161
  attr_accessor :array_of_simple
162
162
  end
163
-
163
+
164
164
  before do
165
165
  @value1 = 'value1'
166
166
  @simple1 = Simple.new
@@ -175,7 +175,7 @@ describe "BSON for Mongo (BSON)" do
175
175
  it "renders a json representation of a simple object" do
176
176
  @simple1.to_bson.should be_bson_eql({MongoDoc::BSON::CLASS_KEY => Simple.name, "value" => @value1})
177
177
  end
178
-
178
+
179
179
  it "renders a json representation of an object with embedded objects" do
180
180
  @complex.to_bson.should be_bson_eql({MongoDoc::BSON::CLASS_KEY => Complex.name, "array_of_simple" => [@simple1.to_bson, @simple2.to_bson]})
181
181
  end
@@ -192,5 +192,5 @@ describe "BSON for Mongo (BSON)" do
192
192
  obj = MongoDoc::BSON.decode(@complex.to_bson)
193
193
  obj.array_of_simple.each {|o| o.should be_kind_of(Simple)}
194
194
  end
195
- end
195
+ end
196
196
  end