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.
- data/.document +5 -0
- data/.gitignore +7 -0
- data/LICENSE +20 -0
- data/README.rdoc +18 -0
- data/Rakefile +80 -0
- data/VERSION +1 -0
- data/data/.gitignore +2 -0
- data/features/mongodoc_base.feature +117 -0
- data/features/saving_an_object.feature +17 -0
- data/features/step_definitions/collection_steps.rb +14 -0
- data/features/step_definitions/connect_steps.rb +4 -0
- data/features/step_definitions/document_steps.rb +88 -0
- data/features/step_definitions/json_steps.rb +9 -0
- data/features/step_definitions/object_steps.rb +43 -0
- data/features/step_definitions/util_steps.rb +7 -0
- data/features/support/support.rb +9 -0
- data/lib/mongodoc.rb +17 -0
- data/lib/mongodoc/attributes.rb +97 -0
- data/lib/mongodoc/base.rb +163 -0
- data/lib/mongodoc/bson.rb +45 -0
- data/lib/mongodoc/connection.rb +20 -0
- data/lib/mongodoc/ext/array.rb +5 -0
- data/lib/mongodoc/ext/binary.rb +7 -0
- data/lib/mongodoc/ext/boolean_class.rb +11 -0
- data/lib/mongodoc/ext/date.rb +16 -0
- data/lib/mongodoc/ext/date_time.rb +13 -0
- data/lib/mongodoc/ext/dbref.rb +7 -0
- data/lib/mongodoc/ext/hash.rb +7 -0
- data/lib/mongodoc/ext/nil_class.rb +5 -0
- data/lib/mongodoc/ext/numeric.rb +17 -0
- data/lib/mongodoc/ext/object.rb +17 -0
- data/lib/mongodoc/ext/object_id.rb +7 -0
- data/lib/mongodoc/ext/regexp.rb +5 -0
- data/lib/mongodoc/ext/string.rb +5 -0
- data/lib/mongodoc/ext/symbol.rb +5 -0
- data/lib/mongodoc/ext/time.rb +5 -0
- data/lib/mongodoc/parent_proxy.rb +37 -0
- data/lib/mongodoc/proxy.rb +76 -0
- data/lib/mongodoc/query.rb +7 -0
- data/lib/mongodoc/value_equals.rb +8 -0
- data/mongod.example.yml +2 -0
- data/mongodoc.gemspec +117 -0
- data/script/console +8 -0
- data/spec/attributes_spec.rb +159 -0
- data/spec/base_ext.rb +9 -0
- data/spec/base_spec.rb +273 -0
- data/spec/bson_matchers.rb +54 -0
- data/spec/bson_spec.rb +316 -0
- data/spec/connection_spec.rb +81 -0
- data/spec/parent_proxy_spec.rb +42 -0
- data/spec/query_spec.rb +12 -0
- data/spec/spec.opts +2 -0
- data/spec/spec_helper.rb +13 -0
- data/spec/test_classes.rb +19 -0
- data/spec/test_documents.rb +35 -0
- metadata +159 -0
data/spec/base_ext.rb
ADDED
data/spec/base_spec.rb
ADDED
@@ -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
|
data/spec/bson_spec.rb
ADDED
@@ -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
|