mongodoc 0.0.0

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