mongodoc 0.0.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 (56) hide show
  1. data/.document +5 -0
  2. data/.gitignore +7 -0
  3. data/LICENSE +20 -0
  4. data/README.rdoc +18 -0
  5. data/Rakefile +80 -0
  6. data/VERSION +1 -0
  7. data/data/.gitignore +2 -0
  8. data/features/mongodoc_base.feature +117 -0
  9. data/features/saving_an_object.feature +17 -0
  10. data/features/step_definitions/collection_steps.rb +14 -0
  11. data/features/step_definitions/connect_steps.rb +4 -0
  12. data/features/step_definitions/document_steps.rb +88 -0
  13. data/features/step_definitions/json_steps.rb +9 -0
  14. data/features/step_definitions/object_steps.rb +43 -0
  15. data/features/step_definitions/util_steps.rb +7 -0
  16. data/features/support/support.rb +9 -0
  17. data/lib/mongodoc.rb +17 -0
  18. data/lib/mongodoc/attributes.rb +97 -0
  19. data/lib/mongodoc/base.rb +163 -0
  20. data/lib/mongodoc/bson.rb +45 -0
  21. data/lib/mongodoc/connection.rb +20 -0
  22. data/lib/mongodoc/ext/array.rb +5 -0
  23. data/lib/mongodoc/ext/binary.rb +7 -0
  24. data/lib/mongodoc/ext/boolean_class.rb +11 -0
  25. data/lib/mongodoc/ext/date.rb +16 -0
  26. data/lib/mongodoc/ext/date_time.rb +13 -0
  27. data/lib/mongodoc/ext/dbref.rb +7 -0
  28. data/lib/mongodoc/ext/hash.rb +7 -0
  29. data/lib/mongodoc/ext/nil_class.rb +5 -0
  30. data/lib/mongodoc/ext/numeric.rb +17 -0
  31. data/lib/mongodoc/ext/object.rb +17 -0
  32. data/lib/mongodoc/ext/object_id.rb +7 -0
  33. data/lib/mongodoc/ext/regexp.rb +5 -0
  34. data/lib/mongodoc/ext/string.rb +5 -0
  35. data/lib/mongodoc/ext/symbol.rb +5 -0
  36. data/lib/mongodoc/ext/time.rb +5 -0
  37. data/lib/mongodoc/parent_proxy.rb +37 -0
  38. data/lib/mongodoc/proxy.rb +76 -0
  39. data/lib/mongodoc/query.rb +7 -0
  40. data/lib/mongodoc/value_equals.rb +8 -0
  41. data/mongod.example.yml +2 -0
  42. data/mongodoc.gemspec +117 -0
  43. data/script/console +8 -0
  44. data/spec/attributes_spec.rb +159 -0
  45. data/spec/base_ext.rb +9 -0
  46. data/spec/base_spec.rb +273 -0
  47. data/spec/bson_matchers.rb +54 -0
  48. data/spec/bson_spec.rb +316 -0
  49. data/spec/connection_spec.rb +81 -0
  50. data/spec/parent_proxy_spec.rb +42 -0
  51. data/spec/query_spec.rb +12 -0
  52. data/spec/spec.opts +2 -0
  53. data/spec/spec_helper.rb +13 -0
  54. data/spec/test_classes.rb +19 -0
  55. data/spec/test_documents.rb +35 -0
  56. metadata +159 -0
@@ -0,0 +1,9 @@
1
+ module MongoDoc
2
+ class Base
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
@@ -0,0 +1,273 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe "MongoDoc::Base" do
4
+
5
+ context "satisfies form_for requirements" do
6
+ before do
7
+ @address = Address.new
8
+ @address._id = '1'
9
+ end
10
+
11
+ it "#id returns the _id" do
12
+ @address.id.should == @address._id
13
+ end
14
+
15
+ it "#to_param returns the _id" do
16
+ @address.to_param.should == @address._id
17
+ end
18
+
19
+ context "#new_record?" do
20
+ it "is true when the object does not have an _id" do
21
+ @address._id = nil
22
+ @address.new_record?.should be_true
23
+ end
24
+
25
+ it "is false when the object has an id" do
26
+ @address.new_record?.should be_false
27
+ end
28
+ end
29
+ end
30
+
31
+ context "validations" do
32
+ class SimpleValidationTest < MongoDoc::Base
33
+ key :data
34
+ validates_presence_of :data
35
+ end
36
+
37
+ it "are part of Base" do
38
+ Validatable.should === MongoDoc::Base.new
39
+ end
40
+
41
+ it "valid? fails when a document is invalid" do
42
+ doc = SimpleValidationTest.new
43
+ doc.should_not be_valid
44
+ doc.should have(1).error_on(:data)
45
+ end
46
+ end
47
+
48
+ context "#save" do
49
+ before do
50
+ @id = Mongo::ObjectID.new([1])
51
+ @address = Address.new
52
+ @collection = stub('collection')
53
+ Address.stub(:collection).and_return(@collection)
54
+ end
55
+
56
+ it "saves a #to_bson on the collection" do
57
+ bson = stub('bson')
58
+ @address.should_receive(:to_bson).and_return(bson)
59
+ @collection.should_receive(:save).with(bson, anything).and_return(Mongo::ObjectID.new([1]))
60
+ @address.save
61
+ end
62
+
63
+ it "sets the _id of the document" do
64
+ @collection.stub(:save).and_return(@id)
65
+ @address.save
66
+ @address._id.should == @id
67
+ end
68
+
69
+ it "returns the _id of the document" do
70
+ @collection.stub(:save).and_return(@id)
71
+ @address.save.should == @id
72
+ end
73
+
74
+ it "ignores validates if asked to" do
75
+ @address.stub(:valid?).and_return(false)
76
+ @collection.stub(:save).and_return(@id)
77
+ @address.save(false).should == @id
78
+ end
79
+
80
+ it "returns false if the object is not valid" do
81
+ @address.stub(:valid?).and_return(false)
82
+ @address.save.should be_false
83
+ end
84
+ end
85
+
86
+ context ".create" do
87
+ it "calls insert with the :safe => false option" do
88
+ collection = stub('collection')
89
+ Address.stub(:collection).and_return(collection)
90
+ collection.should_receive(:insert).with(anything, hash_including(:safe => false))
91
+ Address.create
92
+ end
93
+
94
+ it "is false if the object is not valid" do
95
+ class CreateValidationTest < MongoDoc::Base
96
+ key :data
97
+ validates_presence_of :data
98
+ end
99
+
100
+ CreateValidationTest.create.should be_false
101
+ end
102
+ end
103
+
104
+ context "#bang methods!" do
105
+ before do
106
+ @address = Address.new
107
+ @collection = stub('collection')
108
+ Address.stub(:collection).and_return(@collection)
109
+ end
110
+
111
+ it "create! calls insert with the :safe => true option" do
112
+ @collection.should_receive(:insert).with(anything, hash_including(:safe => true))
113
+ Address.create!
114
+ end
115
+
116
+ it "create! raises if not valid" do
117
+ class CreateBangValidationTest < MongoDoc::Base
118
+ key :data
119
+ validates_presence_of :data
120
+ end
121
+
122
+ expect do
123
+ CreateBangValidationTest.create!
124
+ end.should raise_error(MongoDoc::Document::DocumentInvalidError)
125
+ end
126
+
127
+ it "save! call insert with the :safe => true option" do
128
+ @collection.should_receive(:save).with(anything, hash_including(:safe => true))
129
+ @address.save!
130
+ end
131
+
132
+ it "save! raises if not valid" do
133
+ @address.stub(:valid?).and_return(false)
134
+ expect do
135
+ @address.save!
136
+ end.should raise_error(MongoDoc::Document::DocumentInvalidError)
137
+ end
138
+ end
139
+
140
+ context "#update_attributes" do
141
+ before do
142
+ @attrs = {:state => 'FL'}
143
+ @spec = {'_id' => 1}
144
+ @address = Address.new(@spec)
145
+ @address.stub(:_update_attributes).and_return(true)
146
+ end
147
+
148
+ it "returns true on success" do
149
+ @address.update_attributes(@attrs).should be_true
150
+ end
151
+
152
+ it "sets the attributes" do
153
+ @address.update_attributes(@attrs)
154
+ @address.state.should == 'FL'
155
+ end
156
+
157
+ it "calls _update_attributes" do
158
+ @address.should_receive(:_update_attributes).with(@attrs, false)
159
+ @address.update_attributes(@attrs)
160
+ end
161
+
162
+ it "returns false if the object is not valid" do
163
+ @address.stub(:valid?).and_return(false)
164
+ @address.update_attributes(@attrs).should be_false
165
+ end
166
+
167
+ context "with a bang" do
168
+ it "with a bang, updates the document with the :safe => true option" do
169
+ @address.should_receive(:_update_attributes).with(@attrs, true)
170
+ @address.update_attributes!(@attrs)
171
+ end
172
+
173
+ it "raises if not valid" do
174
+ @address.stub(:valid?).and_return(false)
175
+ expect do
176
+ @address.update_attributes!(@attrs)
177
+ end.should raise_error(MongoDoc::Document::DocumentInvalidError)
178
+ end
179
+ end
180
+ end
181
+
182
+ context "from a nested document" do
183
+ class NestedDocsRoot < MongoDoc::Base
184
+ has_many :nested_children
185
+ end
186
+
187
+ class NestedChild < MongoDoc::Base
188
+ has_one :leaf
189
+ end
190
+
191
+ class LeafDoc < MongoDoc::Base
192
+ key :data
193
+ end
194
+
195
+ context "#save" do
196
+ before do
197
+ @leaf = LeafDoc.new
198
+ @root = NestedDocsRoot.new(:nested_children => [NestedChild.new(:leaf => @leaf)])
199
+ end
200
+
201
+ it "calls the root document's save" do
202
+ @root.should_receive(:save).with(true)
203
+ @leaf.save
204
+ end
205
+
206
+ it "(with bang!) calls the root documents save!" do
207
+ @root.should_receive(:save!)
208
+ @leaf.save!
209
+ end
210
+ end
211
+
212
+ context "with no has_many, update_attributes" do
213
+ before do
214
+ @leaf = LeafDoc.new
215
+ @root = NestedChild.new(:leaf => @leaf)
216
+ end
217
+
218
+ it "calls the root document's _update_attributes with a full attribute path and not safe" do
219
+ @root.should_receive(:_update_attributes).with({'leaf.data' => 'data'}, false)
220
+ @leaf.update_attributes(:data => 'data')
221
+ end
222
+
223
+ it "(with bang!) calls the root document's _update_attributes with a full attribute path and safe" do
224
+ @root.should_receive(:_update_attributes).with({'leaf.data' => 'data'}, true)
225
+ @leaf.update_attributes!(:data => 'data')
226
+ end
227
+ end
228
+
229
+ context "with has_many, update_attributes" do
230
+ before do
231
+ @leaf = LeafDoc.new
232
+ NestedDocsRoot.new(:nested_children => [NestedChild.new(:leaf => @leaf)])
233
+ end
234
+
235
+ it "returns false" do
236
+ @leaf.update_attributes(:data => 'data').should be_false
237
+ end
238
+
239
+ it "sets an error on base" do
240
+ @leaf.update_attributes(:data => 'data')
241
+ @leaf.errors[:base].should_not be_nil
242
+ end
243
+
244
+ it "(with bang!) returns false" do
245
+ @leaf.update_attributes!(:data => 'data').should be_false
246
+ end
247
+
248
+ it "(with bang!) sets an error on base" do
249
+ @leaf.update_attributes(:data => 'data')
250
+ @leaf.errors[:base].should_not be_nil
251
+ end
252
+ end
253
+
254
+ end
255
+
256
+ it ".count calls the collection count" do
257
+ collection = stub('collection')
258
+ MongoDoc::Base.stub(:collection).and_return(collection)
259
+ collection.should_receive(:count).and_return(1)
260
+ MongoDoc::Base.count
261
+ end
262
+
263
+ it ".collection_name returns the name of the collection for this class" do
264
+ Address.collection_name.should == Address.to_s.tableize.gsub('/', '.')
265
+ end
266
+
267
+ it ".collection calls through MongoDoc.database using the class name" do
268
+ db = stub('db')
269
+ db.should_receive(:collection).with(MongoDoc::Base.to_s.tableize.gsub('/', '.'))
270
+ MongoDoc.should_receive(:database).and_return(db)
271
+ MongoDoc::Base.collection
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
@@ -0,0 +1,316 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe "BSON for Mongo (BSON)" do
4
+ describe "#decode" do
5
+ it "just returns the json if the :raw_json option is used" do
6
+ hash = {}
7
+ MongoDoc::BSON.should_not_receive(:bson_create)
8
+ MongoDoc::BSON.decode(hash, :raw_json => true).should == hash
9
+ end
10
+
11
+ it "calls bson_create if parameter is a hash" do
12
+ hash = {}
13
+ options = {:option => true}
14
+ MongoDoc::BSON.should_receive(:bson_create).with(hash, options)
15
+ MongoDoc::BSON.decode(hash, options)
16
+ end
17
+
18
+ it "if parameter is an array, it calls array_create" do
19
+ array = []
20
+ options = {:option => true}
21
+ MongoDoc::BSON.should_receive(:array_create).with(array, options)
22
+ MongoDoc::BSON.decode(array, options)
23
+ end
24
+
25
+ it "returns the json value as is if the parameter is not a hash or array" do
26
+ ["", 1, 1.5, true, false, nil].each do |type_value|
27
+ MongoDoc::BSON.decode(type_value).should == type_value
28
+ end
29
+ end
30
+ end
31
+
32
+ describe "#array_create" do
33
+ it "calls decode for each element" do
34
+ first = 1
35
+ array = [first]
36
+ options = {:option => true}
37
+ MongoDoc::BSON.should_receive(:decode).with(first, options)
38
+ MongoDoc::BSON.array_create(array, options)
39
+ end
40
+
41
+ it "just returns the array if the :raw_json option is used" do
42
+ hash = {'key' => 'value', MongoDoc::BSON::CLASS_KEY => 'Date'}
43
+ array = [hash]
44
+ MongoDoc::BSON.should_not_receive(:decode)
45
+ MongoDoc::BSON.array_create(array, :raw_json => true).should == array
46
+ end
47
+ end
48
+
49
+ describe "#bson_create" do
50
+ it "leaves a simple hash intact" do
51
+ hash = {}
52
+ MongoDoc::BSON.bson_create(hash).should == hash
53
+ end
54
+
55
+ it "a class hash extracts the class, and calls class.bson_create" do
56
+ base_hash = {'key' => 'value'}
57
+ bson_hash = base_hash.merge(MongoDoc::BSON::CLASS_KEY => 'Date')
58
+ Date.should_receive(:bson_create).with(base_hash, {})
59
+ MongoDoc::BSON.bson_create(bson_hash)
60
+ end
61
+
62
+ it "ignores a class hash when the :raw_json option is used" do
63
+ hash = {'key' => 'value', MongoDoc::BSON::CLASS_KEY => 'Date'}
64
+ MongoDoc::BSON.bson_create(hash, :raw_json => true).should == hash
65
+ end
66
+ end
67
+
68
+ describe "Hash" do
69
+ it "#to_bson returns the hash" do
70
+ hash = {'key' => 1}
71
+ hash.to_bson.should == hash
72
+ end
73
+
74
+ it "#to_bson returns the hash with symbol keys as strings" do
75
+ {:key => 1}.to_bson.should == {"key" => 1}
76
+ end
77
+
78
+ it "decodes to a hash" do
79
+ hash = {'key' => 1}
80
+ MongoDoc::BSON.decode(hash.to_bson).should == hash
81
+ end
82
+
83
+ it "decodes the values of the hash" do
84
+ hash = {'key' => {'subkey' => Date.today}}
85
+ MongoDoc::BSON.decode(hash.to_bson).should == hash
86
+ end
87
+ end
88
+
89
+ describe "Array" do
90
+ it "#to_bson returns the array" do
91
+ array = ['string', 1]
92
+ array.to_bson.should == array
93
+ end
94
+
95
+ it "#to_bson iterates over its elements" do
96
+ array = []
97
+ array.should_receive(:map)
98
+ array.to_bson
99
+ end
100
+
101
+ it "decodes to an array" do
102
+ array = ['string', 1]
103
+ MongoDoc::BSON.decode(array.to_bson).should == array
104
+ end
105
+ end
106
+
107
+ describe "Extensions to core classes" do
108
+ it "#to_bson for objects that are BSON native return themselves" do
109
+ [true, false, nil, 1.0, 1, /regexp/, 'string', :symbol, Time.now].each do |native|
110
+ native.to_bson.should == native
111
+ end
112
+ end
113
+
114
+ it "objects that are BSON native decode to themselves" do
115
+ [true, false, nil, 1.0, 1, /regexp/, 'string', :symbol, Time.now].each do |native|
116
+ hash = {'native' => native}
117
+ MongoDoc::BSON.decode(hash.to_bson).should == hash
118
+ end
119
+ end
120
+
121
+ it "Date#to_bson returns a date hash" do
122
+ date = Date.today
123
+ date.to_bson.should == {MongoDoc::BSON::CLASS_KEY => "Date", "dt" => date.strftime, "sg" => date.respond_to?(:start) ? date.start : date.sg}
124
+ end
125
+
126
+ it "roundtrips Date" do
127
+ date = Date.today
128
+ MongoDoc::BSON.decode(date.to_bson).should == date
129
+ end
130
+
131
+ it "DateTime#to_bson returns a datetime hash" do
132
+ datetime = DateTime.now
133
+ datetime.to_bson.should == {MongoDoc::BSON::CLASS_KEY => "DateTime", "dt" => datetime.strftime, "sg" => datetime.respond_to?(:start) ? datetime.start : datetime.sg}
134
+ end
135
+
136
+ it "roundtrips DateTime" do
137
+ datetime = DateTime.now
138
+ MongoDoc::BSON.decode(datetime.to_bson).to_s.should == datetime.to_s
139
+ end
140
+ end
141
+
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|
144
+ it "#to_bson for #{obj.class.name} returns self" do
145
+ obj.to_bson.should == obj
146
+ end
147
+
148
+ it "objects of type #{obj.class.name} decode to themselves" do
149
+ hash = {"mongo" => obj}
150
+ MongoDoc::BSON.decode(hash.to_bson)["mongo"].should == obj
151
+ end
152
+ end
153
+ end
154
+
155
+ describe "Extensions to Object" do
156
+ before do
157
+ @movie = Movie.new
158
+ @movie.title = 'Gone with the Wind'
159
+ @movie.director = 'Victor Fleming'
160
+ @movie.writers = ['Sidney Howard']
161
+ @director = Director.new
162
+ @director.name = 'Victor Fleming'
163
+ @director.awards = ['1940 - Best Director']
164
+ @movie.director = @director
165
+ end
166
+
167
+ it "renders a json representation of a simple object" do
168
+ @director.to_bson.should be_bson_eql({"name" => "Victor Fleming", MongoDoc::BSON::CLASS_KEY => "Director", "awards" => ["1940 - Best Director"]})
169
+ end
170
+
171
+ it "renders a json representation of an object with embedded objects" do
172
+ @movie.to_bson.should be_bson_eql({"title" => "Gone with the Wind", MongoDoc::BSON::CLASS_KEY => "Movie", "writers" => ["Sidney Howard"], "director" => {"name" => "Victor Fleming", MongoDoc::BSON::CLASS_KEY => "Director", "awards" => ["1940 - Best Director"]}})
173
+ end
174
+
175
+ it "ignores a class hash when the :raw_json option is used" do
176
+ Movie.bson_create(@movie.to_bson.except(MongoDoc::BSON::CLASS_KEY), :raw_json => true).director.should == @director.to_bson
177
+ end
178
+
179
+ it "roundtrips the object" do
180
+ MongoDoc::BSON.decode(@movie.to_bson).should be_kind_of(Movie)
181
+ end
182
+
183
+ it "allows for embedded objects" do
184
+ movie_from_bson = MongoDoc::BSON.decode(@movie.to_bson)
185
+ movie_from_bson.director.should be_kind_of(Director)
186
+ end
187
+
188
+ it "allows for embedded arrays of objects" do
189
+ award = AcademyAward.new
190
+ award.year = '1940'
191
+ award.category = 'Best Director'
192
+ @director.awards = [award]
193
+ director_from_bson = MongoDoc::BSON.decode(@director.to_bson)
194
+ director_from_bson.awards.each {|award| award.should be_kind_of(AcademyAward)}
195
+ end
196
+ end
197
+
198
+ describe "MongoDoc::Base" do
199
+ before do
200
+ @address = Address.new
201
+ @address.street = '320 1st Street North'
202
+ @address.city = 'Jacksonville Beach'
203
+ @address.state = 'FL'
204
+ @address.zip_code = '32250'
205
+ @location = Location.new
206
+ @location.address = @address
207
+ end
208
+
209
+ it "encodes the class for the object" do
210
+ atom = Automobile::Ariel.new
211
+ atom.to_bson[MongoDoc::BSON::CLASS_KEY].should == Automobile::Ariel.name
212
+ end
213
+
214
+ it "renders a json representation of the object" do
215
+ @location.to_bson.should be_bson_eql({MongoDoc::BSON::CLASS_KEY => "Location", "website" => nil, "address" => {"state" => "FL", MongoDoc::BSON::CLASS_KEY => "Address", "zip_code" => "32250", "street" => "320 1st Street North", "city" => "Jacksonville Beach"}})
216
+ end
217
+
218
+ it "includes the _id of the object" do
219
+ @location._id = Mongo::ObjectID.new
220
+ @location.to_bson.should be_bson_eql({MongoDoc::BSON::CLASS_KEY => "Location", "_id" => @location._id.to_bson, "website" => nil, "address" => {"state" => "FL", MongoDoc::BSON::CLASS_KEY => "Address", "zip_code" => "32250", "street" => "320 1st Street North", "city" => "Jacksonville Beach"}})
221
+ end
222
+
223
+ it "roundtrips the object" do
224
+ MongoDoc::BSON.decode(@location.to_bson).should be_kind_of(Location)
225
+ end
226
+
227
+ it "ignores the class hash when the :raw_json option is used" do
228
+ MongoDoc::BSON.decode(@location.to_bson.except(MongoDoc::BSON::CLASS_KEY), :raw_json => true)['address'].should == @address.to_bson
229
+ end
230
+
231
+ it "allows for embedded MongoDoc objects" do
232
+ company_from_bson = MongoDoc::BSON.decode(@location.to_bson)
233
+ company_from_bson.should be_kind_of(Location)
234
+ company_from_bson.address.should be_kind_of(Address)
235
+ end
236
+
237
+ it "allows for derived classes" do
238
+ wifi = WifiAccessible.new
239
+ wifi.address = @address
240
+ wifi.network_name = 'hashrocket'
241
+ wifi_from_bson = MongoDoc::BSON.decode(wifi.to_bson)
242
+ wifi_from_bson.should be_kind_of(WifiAccessible)
243
+ wifi_from_bson.address.should be_kind_of(Address)
244
+ end
245
+
246
+ it "allows for embedded ruby objects" do
247
+ website = WebSite.new
248
+ website.url = 'http://hashrocket.com'
249
+ wifi = WifiAccessible.new
250
+ wifi.website = website
251
+ wifi_from_bson = MongoDoc::BSON.decode(wifi.to_bson)
252
+ wifi_from_bson.should be_kind_of(WifiAccessible)
253
+ wifi_from_bson.website.should be_kind_of(WebSite)
254
+ end
255
+
256
+ context "associations" do
257
+ context "has_one" do
258
+ class TestHasOneBsonDoc < MongoDoc::Base
259
+ has_one :subdoc
260
+ end
261
+
262
+ class SubHasOneBsonDoc < MongoDoc::Base
263
+ key :attr
264
+ end
265
+
266
+ it "#to_bson renders a bson representation of the document" do
267
+ doc = TestHasOneBsonDoc.new
268
+ subdoc = SubHasOneBsonDoc.new(:attr => "value")
269
+ bson = doc.to_bson
270
+ bson["subdoc"] = subdoc.to_bson
271
+ doc.subdoc = subdoc
272
+ doc.to_bson.should == bson
273
+ end
274
+
275
+ it "roundtrips" do
276
+ doc = TestHasOneBsonDoc.new
277
+ subdoc = SubHasOneBsonDoc.new(:attr => "value")
278
+ doc.subdoc = subdoc
279
+ MongoDoc::BSON.decode(doc.to_bson).should == doc
280
+ end
281
+ end
282
+
283
+ context "has_many" do
284
+
285
+ class SubHasManyBsonDoc < MongoDoc::Base
286
+ key :attr
287
+ end
288
+
289
+ class TestHasManyBsonDoc < MongoDoc::Base
290
+ has_many :subdoc, :class_name => 'SubHasManyBsonDoc'
291
+ end
292
+
293
+ it "#to_bson renders a bson representation of the document" do
294
+ doc = TestHasManyBsonDoc.new
295
+ subdoc = SubHasManyBsonDoc.new(:attr => "value")
296
+ bson = doc.to_bson
297
+ bson["subdoc"] = [subdoc].to_bson
298
+ doc.subdoc = subdoc
299
+ doc.to_bson.should == bson
300
+ end
301
+
302
+ it "roundtrips" do
303
+ doc = TestHasManyBsonDoc.new
304
+ subdoc = SubHasManyBsonDoc.new(:attr => "value")
305
+ doc.subdoc = subdoc
306
+ MongoDoc::BSON.decode(doc.to_bson).should == doc
307
+ end
308
+
309
+ it "roundtrips the proxy" do
310
+ doc = TestHasManyBsonDoc.new(:subdoc => SubHasManyBsonDoc.new(:attr => "value"))
311
+ MongoDoc::Document::Proxy.should === MongoDoc::BSON.decode(doc.to_bson).subdoc
312
+ end
313
+ end
314
+ end
315
+ end
316
+ end