light_mongo 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +4 -0
- data/LICENSE +20 -0
- data/README.md +134 -0
- data/Rakefile +45 -0
- data/VERSION +1 -0
- data/integration/basic_storage_cycle_spec.rb +21 -0
- data/integration/cross_collection_relationships_spec.rb +29 -0
- data/integration/dynamic_finders_spec.rb +33 -0
- data/integration/meta_spec.rb +20 -0
- data/integration/object_embedding_spec.rb +42 -0
- data/integration/support/integration_helper.rb +9 -0
- data/lib/document/persistence.rb +84 -0
- data/lib/document/serialization/hash_serializer.rb +74 -0
- data/lib/document/serialization/serializer.rb +76 -0
- data/lib/document/serialization.rb +45 -0
- data/lib/document.rb +20 -0
- data/lib/light_mongo.rb +3 -0
- data/lib/util.rb +20 -0
- data/spec/document/persistence_spec.rb +219 -0
- data/spec/document/serialization/hash_serializer_spec.rb +153 -0
- data/spec/document/serialization/serializer_spec.rb +237 -0
- data/spec/document/serialization_spec.rb +75 -0
- data/spec/document_spec.rb +19 -0
- metadata +91 -0
data/lib/util.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
# Pulled with vigour from Rails 2.3.5
|
2
|
+
# and rewritten to be carbon neutral.
|
3
|
+
module LightMongo
|
4
|
+
class Util
|
5
|
+
def self.blank?(object)
|
6
|
+
return true if object.is_a? NilClass
|
7
|
+
return true if object.is_a? FalseClass
|
8
|
+
return false if object.is_a? TrueClass
|
9
|
+
return object.empty? if object.is_a? Array
|
10
|
+
return object.empty? if object.is_a? Hash
|
11
|
+
return object !~ /\S/ if object.is_a? String
|
12
|
+
return false if object.is_a? Numeric
|
13
|
+
return respond_to?(:empty?) ? empty? : !self
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.present?(object)
|
17
|
+
!blank?(object)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,219 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../../lib/light_mongo'
|
2
|
+
|
3
|
+
describe LightMongo::Document::Persistence do
|
4
|
+
before(:all) do
|
5
|
+
LightMongo.stub!(:database => mock(:database, :connection => nil))
|
6
|
+
@test_class_collection = mock(:test_class_collection)
|
7
|
+
Mongo::Collection.stub!(:new => @test_class_collection)
|
8
|
+
|
9
|
+
class TestClass
|
10
|
+
include LightMongo::Document
|
11
|
+
attr_accessor :name
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "the module's inclusion" do
|
16
|
+
it "sets up the collection on a class-level" do
|
17
|
+
TestClass.collection.should == @test_class_collection
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe "#save" do
|
22
|
+
before(:each) do
|
23
|
+
@test_object_hash = mock(:test_object_hash)
|
24
|
+
@test_object = TestClass.new
|
25
|
+
@test_object.stub!(:to_hash => @test_object_hash)
|
26
|
+
end
|
27
|
+
|
28
|
+
it "saves the Document to the collection for that class" do
|
29
|
+
@test_class_collection.should_receive(:save).with(@test_object_hash)
|
30
|
+
@test_object.save
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe ".create(params)" do
|
35
|
+
before(:each) do
|
36
|
+
@name = mock(:name)
|
37
|
+
@test_object = TestClass.new(:name => @name)
|
38
|
+
@test_object.stub!(:save)
|
39
|
+
TestClass.stub!(:new).with(:name => @name).and_return(@test_object)
|
40
|
+
end
|
41
|
+
|
42
|
+
it "spawns a new Ruby object from the params" do
|
43
|
+
TestClass.create(:name => @name).name.should == @name
|
44
|
+
end
|
45
|
+
|
46
|
+
it "saves the Ruby object to the database" do
|
47
|
+
@test_object.should_receive(:save)
|
48
|
+
TestClass.create(:name => @name)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe "#id" do
|
53
|
+
before(:each) do
|
54
|
+
@id = mock(:id)
|
55
|
+
@test_object = TestClass.new(:_id => @id)
|
56
|
+
end
|
57
|
+
|
58
|
+
it "delegates to the Mongo _id" do
|
59
|
+
@test_object.id.should == @id
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
describe "#==(other)" do
|
64
|
+
before(:each) do
|
65
|
+
@id = mock(:id)
|
66
|
+
@object_1 = TestClass.new(:_id => @id)
|
67
|
+
@object_2 = TestClass.new(:_id => @id)
|
68
|
+
end
|
69
|
+
|
70
|
+
it "compares on id" do
|
71
|
+
@object_1.should == @object_2
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
describe ".find_by_<index>(value)" do
|
76
|
+
before(:each) do
|
77
|
+
@test_class_collection.stub!(:create_index)
|
78
|
+
TestClass.index(:name)
|
79
|
+
@name = mock(:name)
|
80
|
+
@id = mock(:id)
|
81
|
+
|
82
|
+
@bson_hash = {:class_name => 'TestClass', :name => @name, :_id => @id}
|
83
|
+
end
|
84
|
+
|
85
|
+
it "finds all objects which match the given index pattern" do
|
86
|
+
@test_class_collection.should_receive(:find).with(:name => @name).and_return([@bson_hash])
|
87
|
+
test_object = TestClass.find_by_name(@name).first
|
88
|
+
test_object.id.should == @id
|
89
|
+
test_object.name.should == @name
|
90
|
+
test_object.class.should == TestClass
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
describe ".find(query=nil)" do
|
95
|
+
before(:each) do
|
96
|
+
@no_query_results = mock(:no_query_results)
|
97
|
+
@query_results = mock(:query_results)
|
98
|
+
TestClass.stub!(:new).with(@no_query_results).and_return(@no_query_object = mock(:no_query_object))
|
99
|
+
TestClass.stub!(:new).with(@query_results).and_return(@query_object = mock(:query_object))
|
100
|
+
end
|
101
|
+
|
102
|
+
it "maps MongoDB::Collection.find" do
|
103
|
+
@test_class_collection.should_receive(:find).and_return([@no_query_results])
|
104
|
+
TestClass.find.should == [@no_query_object]
|
105
|
+
|
106
|
+
@test_class_collection.should_receive(:find).with({'_id' => query = mock(:query)}).and_return([@query_results])
|
107
|
+
TestClass.find(query).should == [@query_object]
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
describe ".index(key, :as => (name | nil))" do
|
112
|
+
def self.it_sets_up_the_index_verbatim
|
113
|
+
it "sets up the index with key #{@key} and name #{@name}" do
|
114
|
+
@test_class_collection.should_receive(:create_index).with(@key)
|
115
|
+
set_up_class(@key, @name)
|
116
|
+
TestClass.should respond_to(('find_by_'+@name.to_s).to_sym)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def set_up_class(key, name=nil)
|
121
|
+
TestClass.index(key, :as => name)
|
122
|
+
|
123
|
+
@indexable_object = TestClass.new(:top_level_attribute => 'test')
|
124
|
+
end
|
125
|
+
|
126
|
+
context "when given no key" do
|
127
|
+
it "creates no index and no method" do
|
128
|
+
@test_class_collection.should_not_receive(:create_index)
|
129
|
+
set_up_class(nil)
|
130
|
+
set_up_class('')
|
131
|
+
TestClass.should_not respond_to(:find_by_)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
context "when the given key is usable as a method name" do
|
136
|
+
before(:each) do
|
137
|
+
@key = :top_level_attribute
|
138
|
+
end
|
139
|
+
|
140
|
+
context "and no name is provided" do
|
141
|
+
it "uses the key as the lookup name" do
|
142
|
+
@test_class_collection.should_receive(:create_index).with(@key)
|
143
|
+
set_up_class(@key)
|
144
|
+
TestClass.should respond_to(('find_by_'+@key.to_s).to_sym)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
context "and a name is provided" do
|
149
|
+
before(:each) do
|
150
|
+
@name = :environment
|
151
|
+
end
|
152
|
+
|
153
|
+
it_sets_up_the_index_verbatim
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
context "when the given key is unusuable as a method name" do
|
158
|
+
before(:each) do
|
159
|
+
@key = 'top_level_attribute.sub_attribute'
|
160
|
+
end
|
161
|
+
|
162
|
+
context "and no name is provided" do
|
163
|
+
it "adds an index on the key" do
|
164
|
+
@test_class_collection.should_receive(:create_index).with(@key)
|
165
|
+
set_up_class(@key)
|
166
|
+
end
|
167
|
+
|
168
|
+
it "does not create a method using the key" do
|
169
|
+
@test_class_collection.stub(:create_index)
|
170
|
+
set_up_class(@key)
|
171
|
+
TestClass.should_not respond_to(('find_by_'+@key.to_s).to_sym)
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
context "and a name is provided" do
|
176
|
+
before(:each) do
|
177
|
+
@name = :sub_environment
|
178
|
+
end
|
179
|
+
|
180
|
+
it_sets_up_the_index_verbatim
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
describe ".viable_method_name(method_name)" do
|
186
|
+
context "contains only alphanums and underscores" do
|
187
|
+
it "is valid" do
|
188
|
+
TestClass.viable_method_name('_jkdsf328_32').should be_true
|
189
|
+
end
|
190
|
+
|
191
|
+
context "and ends in an exclamation mark" do
|
192
|
+
it 'is valid' do
|
193
|
+
TestClass.viable_method_name('_jkdsf328_32!').should be_true
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
context "and ends in an question mark" do
|
198
|
+
it 'is valid' do
|
199
|
+
TestClass.viable_method_name('_jkdsf328_32?').should be_true
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
context "contains a non [alphanum_!?] anywhere" do
|
205
|
+
it "is invalid" do
|
206
|
+
TestClass.viable_method_name('_jkdsf328_32*').should be_false
|
207
|
+
TestClass.viable_method_name('_jkdsf^328_32').should be_false
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
context "contains a [!?] anywhere but the end" do
|
212
|
+
it "is invalid" do
|
213
|
+
TestClass.viable_method_name('_jkds!f328_32').should be_false
|
214
|
+
TestClass.viable_method_name('_jkds?f328_32').should be_false
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
end
|
@@ -0,0 +1,153 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../../../lib/light_mongo'
|
2
|
+
|
3
|
+
describe LightMongo::Document::Serialization::HashSerializer do
|
4
|
+
HashSerializer = LightMongo::Document::Serialization::HashSerializer
|
5
|
+
|
6
|
+
before(:each) do
|
7
|
+
@current_depth = 0
|
8
|
+
end
|
9
|
+
|
10
|
+
describe ".serialize_object(object_to_serialize, current_depth)" do
|
11
|
+
before(:each) do
|
12
|
+
LightMongo.stub!(:database => mock(:database, :connection => nil), :slow_serialization => true)
|
13
|
+
@test_class_collection = mock(:test_class_collection)
|
14
|
+
Mongo::Collection.stub!(:new => @test_class_collection)
|
15
|
+
end
|
16
|
+
|
17
|
+
context "when a non-primitive" do
|
18
|
+
context "non-LightMongo::Document object" do
|
19
|
+
before(:each) do
|
20
|
+
class Other
|
21
|
+
attr_accessor :name
|
22
|
+
def initialize
|
23
|
+
@name = self.class.name + object_id.to_s
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
@object = Other.new
|
28
|
+
end
|
29
|
+
|
30
|
+
it "hashifies the object" do
|
31
|
+
HashSerializer.send(:serialize_object, @object, @current_depth+3).
|
32
|
+
should == {'_class_name' => 'Other', 'name' => @object.name}
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
context "LightMongo::Document object" do
|
37
|
+
before(:each) do
|
38
|
+
class TestClass
|
39
|
+
include LightMongo::Document
|
40
|
+
end
|
41
|
+
|
42
|
+
@object = TestClass.new
|
43
|
+
@test_class_collection.stub(:save => (@id = mock(:id)))
|
44
|
+
end
|
45
|
+
|
46
|
+
context "when below the top level" do
|
47
|
+
before(:each) do
|
48
|
+
@current_depth = 1
|
49
|
+
end
|
50
|
+
|
51
|
+
it "exports the object" do
|
52
|
+
HashSerializer.send(:serialize_object, @object, @current_depth).
|
53
|
+
should == {'_class_name' => 'TestClass', '_id' => @id, '_embed' => true}
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
context "when at the top level" do
|
58
|
+
before(:each) do
|
59
|
+
@current_depth = 0
|
60
|
+
end
|
61
|
+
|
62
|
+
it "hashifies the object" do
|
63
|
+
HashSerializer.should_receive(:hashify).with(@object, @current_depth)
|
64
|
+
HashSerializer.send(:serialize_object, @object, @current_depth)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
context "when a primitive object" do
|
71
|
+
before(:each) do
|
72
|
+
@object = "test string"
|
73
|
+
end
|
74
|
+
|
75
|
+
it "returns the raw object" do
|
76
|
+
HashSerializer.send(:serialize_object, @object, @current_depth).
|
77
|
+
should == @object
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
describe ".dump(object, current_depth)" do
|
83
|
+
context "when the object is a container" do
|
84
|
+
before(:each) do
|
85
|
+
|
86
|
+
@sub_object_1 = mock(:sub_object_1)
|
87
|
+
@sub_object_2 = mock(:sub_object_2)
|
88
|
+
|
89
|
+
LightMongo::Document::Serialization::Serializer.
|
90
|
+
stub!(:serialize).
|
91
|
+
with(@sub_object_1, anything).
|
92
|
+
and_return(@serial_1 = mock(:serial_1))
|
93
|
+
LightMongo::Document::Serialization::Serializer.
|
94
|
+
stub!(:serialize).
|
95
|
+
with(@sub_object_2, anything).
|
96
|
+
and_return(@serial_2 = mock(:serial_2))
|
97
|
+
end
|
98
|
+
|
99
|
+
context "and more specifically an Array" do
|
100
|
+
before(:each) do
|
101
|
+
@object = [@sub_object_1, @sub_object_2]
|
102
|
+
end
|
103
|
+
|
104
|
+
it "iterates over the array, serializing the contents." do
|
105
|
+
HashSerializer.dump(@object).should == [@serial_1, @serial_2]
|
106
|
+
end
|
107
|
+
|
108
|
+
it "notifies the generic Serializer of the new depth." do
|
109
|
+
LightMongo::Document::Serialization::Serializer.should_receive(:serialize).with(@sub_object_1, @current_depth + 1)
|
110
|
+
HashSerializer.dump(@object, @current_depth)
|
111
|
+
end
|
112
|
+
|
113
|
+
it "returns an array." do
|
114
|
+
HashSerializer.dump(@object).should be_an(Array)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
context "and more specifically a Hash" do
|
119
|
+
before(:each) do
|
120
|
+
@object = {
|
121
|
+
:sub_object_1 => @sub_object_1,
|
122
|
+
:sub_object_2 => @sub_object_2
|
123
|
+
}
|
124
|
+
end
|
125
|
+
|
126
|
+
it "iterates over the hash, serializing the contents but retaining the keys." do
|
127
|
+
HashSerializer.dump(@object).should == {:sub_object_1 => @serial_1, :sub_object_2 => @serial_2}
|
128
|
+
end
|
129
|
+
|
130
|
+
it "notifies the generic Serializer of the new depth." do
|
131
|
+
LightMongo::Document::Serialization::Serializer.should_receive(:serialize).with(@sub_object_1, @current_depth + 1)
|
132
|
+
HashSerializer.dump(@object, @current_depth)
|
133
|
+
end
|
134
|
+
|
135
|
+
it "returns a Hash." do
|
136
|
+
HashSerializer.dump(@object).should be_an(Hash)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
context "when the object is a custom object" do
|
142
|
+
before(:each) do
|
143
|
+
class TestClass; end
|
144
|
+
@object = TestClass.new
|
145
|
+
end
|
146
|
+
|
147
|
+
it "passes off to the object serializer" do
|
148
|
+
HashSerializer.should_receive(:serialize_object).with(@object, @current_depth)
|
149
|
+
HashSerializer.dump(@object, @current_depth)
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
@@ -0,0 +1,237 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../../../lib/document/serialization'
|
2
|
+
|
3
|
+
Serializer = LightMongo::Document::Serialization::Serializer
|
4
|
+
|
5
|
+
describe Serializer do
|
6
|
+
before(:each) do
|
7
|
+
@object = mock(:object)
|
8
|
+
end
|
9
|
+
|
10
|
+
describe ".array_deserialize(object)" do
|
11
|
+
before(:each) do
|
12
|
+
@object = [
|
13
|
+
@ivar1 = mock(:ivar1),
|
14
|
+
@ivar2 = mock(:ivar2)
|
15
|
+
]
|
16
|
+
end
|
17
|
+
|
18
|
+
it "map-deserializes each element in the array" do
|
19
|
+
Serializer.should_receive(:deserialize).with(@ivar1).
|
20
|
+
and_return(desel_ivar1 = mock(:desel_ivar1))
|
21
|
+
|
22
|
+
Serializer.should_receive(:deserialize).with(@ivar2).
|
23
|
+
and_return(desel_ivar2 = mock(:desel_ivar2))
|
24
|
+
|
25
|
+
Serializer.send(:array_deserialize, @object).
|
26
|
+
should == [desel_ivar1, desel_ivar2]
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe ".hash_deserialize(object)" do
|
31
|
+
before(:each) do
|
32
|
+
@object = {
|
33
|
+
:ivar1 => (@ivar1 = mock(:ivar1)),
|
34
|
+
:ivar2 => (@ivar2 = mock(:ivar2))
|
35
|
+
}
|
36
|
+
end
|
37
|
+
|
38
|
+
it "map-deserializes each element in the hash" do
|
39
|
+
Serializer.should_receive(:deserialize).with(@ivar1).
|
40
|
+
and_return(desel_ivar1 = mock(:desel_ivar1))
|
41
|
+
|
42
|
+
Serializer.should_receive(:deserialize).with(@ivar2).
|
43
|
+
and_return(desel_ivar2 = mock(:desel_ivar2))
|
44
|
+
|
45
|
+
Serializer.send(:hash_deserialize, @object).
|
46
|
+
should == {:ivar1 => desel_ivar1, :ivar2 => desel_ivar2}
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe ".deserialize(object)" do
|
51
|
+
context "when the object is an array" do
|
52
|
+
before(:each) do
|
53
|
+
@object = []
|
54
|
+
end
|
55
|
+
|
56
|
+
it "deserializes the array" do
|
57
|
+
Serializer.should_receive(:array_deserialize).with(@object).
|
58
|
+
and_return(desel_array = mock(:desel_array))
|
59
|
+
|
60
|
+
Serializer.deserialize(@object).should == desel_array
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
context "when the object is a hash" do
|
65
|
+
before(:each) do
|
66
|
+
@object = {}
|
67
|
+
end
|
68
|
+
|
69
|
+
context "and nothing more" do
|
70
|
+
it "deserializes the hash" do
|
71
|
+
Serializer.should_receive(:hash_deserialize).with(@object)
|
72
|
+
Serializer.deserialize(@object)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
context "and a Marshal dump" do
|
77
|
+
before(:each) do
|
78
|
+
@marshal_dump = mock(:marshal_dump)
|
79
|
+
@object['_data'] = @marshal_dump
|
80
|
+
end
|
81
|
+
|
82
|
+
it "unmarshals the dump" do
|
83
|
+
Marshal.should_receive(:load).with(@marshal_dump).
|
84
|
+
and_return(unmarshalled_data = mock(:unmarshalled_data))
|
85
|
+
|
86
|
+
Serializer.deserialize(@object).should == unmarshalled_data
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
context "and a LightMongo::Document" do
|
91
|
+
before(:each) do
|
92
|
+
class TestClass
|
93
|
+
end
|
94
|
+
|
95
|
+
@id = mock(:id)
|
96
|
+
@object = {'_id' => @id, '_class_name' => 'TestClass', '_embed' => true}
|
97
|
+
end
|
98
|
+
|
99
|
+
it "recovers the linked document" do
|
100
|
+
TestClass.stub!(:find).with(@id).and_return([test_instance = mock(:test_instance)])
|
101
|
+
|
102
|
+
Serializer.deserialize(@object).should == test_instance
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
context "when the object is anything else" do
|
108
|
+
before(:each) do
|
109
|
+
@object = 3
|
110
|
+
end
|
111
|
+
|
112
|
+
it "returns the object unharmed" do
|
113
|
+
Serializer.deserialize(@object).should == @object
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
describe ".serialize(object, current_depth)" do
|
119
|
+
before(:each) do
|
120
|
+
@depth = 0
|
121
|
+
@serializer = Serializer.new(@object, @depth)
|
122
|
+
@serializer.stub!(:hash_serialize => (@hash_serialized_object = mock(:hash_serialized_object)))
|
123
|
+
Serializer.stub!(:new).with(@object, anything).and_return(@serializer)
|
124
|
+
end
|
125
|
+
|
126
|
+
def self.it_creates_a_new_serializer_instance
|
127
|
+
it "creates a new serializer instance" do
|
128
|
+
Serializer.should_receive(:new).with(@object, @depth).and_return(@serializer)
|
129
|
+
Serializer.serialize(@object, @depth)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def self.it_hash_serializes_the_object
|
134
|
+
it "hash-serializes the object." do
|
135
|
+
@serializer.should_receive(:hash_serialize)
|
136
|
+
Serializer.serialize(@object, @depth)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def self.it_returns_the_hash_serialized_object
|
141
|
+
it "returns the hash-serialized object." do
|
142
|
+
Serializer.serialize(@object, @depth).should == @hash_serialized_object
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
context "when above the marshalling depth threshold" do
|
147
|
+
before(:each) do
|
148
|
+
@depth = 1
|
149
|
+
end
|
150
|
+
|
151
|
+
it_creates_a_new_serializer_instance
|
152
|
+
|
153
|
+
it_hash_serializes_the_object
|
154
|
+
|
155
|
+
it "does not marshal the object." do
|
156
|
+
@serializer.should_not_receive(:marshal)
|
157
|
+
Serializer.serialize(@object, @depth)
|
158
|
+
end
|
159
|
+
|
160
|
+
it_returns_the_hash_serialized_object
|
161
|
+
end
|
162
|
+
|
163
|
+
context "when below the marshalling depth threshold" do
|
164
|
+
before(:each) do
|
165
|
+
@depth = 5
|
166
|
+
end
|
167
|
+
|
168
|
+
context "and marshalling is turned on" do
|
169
|
+
before(:each) do
|
170
|
+
LightMongo.slow_serialization = false
|
171
|
+
end
|
172
|
+
|
173
|
+
it_creates_a_new_serializer_instance
|
174
|
+
|
175
|
+
it "marshals the object." do
|
176
|
+
@serializer.should_receive(:marshal)
|
177
|
+
Serializer.serialize(@object, @depth)
|
178
|
+
end
|
179
|
+
|
180
|
+
it "does not hash-serialize the object." do
|
181
|
+
@serializer.should_not_receive(:hash_serialize)
|
182
|
+
Serializer.serialize(@object, @depth)
|
183
|
+
end
|
184
|
+
|
185
|
+
it "returns the marshalled object." do
|
186
|
+
@serializer.stub!(:marshal => (@marshalled_object = mock(:marshalled_object)))
|
187
|
+
Serializer.serialize(@object, @depth).should == @marshalled_object
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
context "but marshalling is turned off" do
|
192
|
+
before(:each) do
|
193
|
+
LightMongo.slow_serialization = true
|
194
|
+
end
|
195
|
+
|
196
|
+
it_creates_a_new_serializer_instance
|
197
|
+
|
198
|
+
it_hash_serializes_the_object
|
199
|
+
|
200
|
+
it_returns_the_hash_serialized_object
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
describe "#marshal(object)" do
|
206
|
+
it "marshals the object." do
|
207
|
+
Marshal.should_receive(:dump).with(@object)
|
208
|
+
Serializer.new(@object).marshal
|
209
|
+
end
|
210
|
+
|
211
|
+
it "returns the marshalled object." do
|
212
|
+
Marshal.stub!(:dump).
|
213
|
+
with(@object).
|
214
|
+
and_return(marshalled_object = mock(:marshalled_object))
|
215
|
+
Serializer.new(@object).marshal.should == {'_data' => marshalled_object}
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
describe "#hash_serialize(object, current_depth)" do
|
220
|
+
before(:each) do
|
221
|
+
@current_depth = mock(:current_depth)
|
222
|
+
end
|
223
|
+
|
224
|
+
it "serializes the object into a set of nested hashes." do
|
225
|
+
LightMongo::Document::Serialization::HashSerializer.
|
226
|
+
should_receive(:dump).with(@object, @current_depth)
|
227
|
+
Serializer.new(@object, @current_depth).hash_serialize
|
228
|
+
end
|
229
|
+
|
230
|
+
it "returns the marshalled object." do
|
231
|
+
LightMongo::Document::Serialization::HashSerializer.stub!(:dump).
|
232
|
+
with(@object, @current_depth).
|
233
|
+
and_return(hashed_object = mock(:hashed_object))
|
234
|
+
Serializer.new(@object, @current_depth).hash_serialize.should == hashed_object
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../../lib/light_mongo'
|
2
|
+
|
3
|
+
describe LightMongo::Document::Serialization do
|
4
|
+
before(:each) do
|
5
|
+
LightMongo.stub!(:database => mock(:database, :connection => nil), :slow_serialization => true)
|
6
|
+
@test_class_collection = mock(:test_class_collection)
|
7
|
+
Mongo::Collection.stub!(:new => @test_class_collection)
|
8
|
+
|
9
|
+
class TestClass
|
10
|
+
include LightMongo::Document
|
11
|
+
attr_accessor :test_attribute
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "#export" do
|
16
|
+
before(:each) do
|
17
|
+
@test_object = TestClass.new
|
18
|
+
|
19
|
+
@id = mock(:id)
|
20
|
+
@test_class_collection.stub!(:save => @id)
|
21
|
+
end
|
22
|
+
|
23
|
+
context "if Persistence has been included" do
|
24
|
+
it "saves itself" do
|
25
|
+
@test_object.should_receive(:save)
|
26
|
+
@test_object.export
|
27
|
+
end
|
28
|
+
|
29
|
+
it "generates a hash of its class name, id, and embed status" do
|
30
|
+
@test_object.export.should == {'_class_name' => 'TestClass', '_id' => @id, '_embed' => true}
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context "if Persistence hasn't been included" do
|
35
|
+
before(:each) do
|
36
|
+
class NoPersistence
|
37
|
+
include LightMongo::Document::Serialization
|
38
|
+
end
|
39
|
+
@no_persistence = NoPersistence.new
|
40
|
+
end
|
41
|
+
|
42
|
+
it "returns self" do
|
43
|
+
@no_persistence.export.should == @no_persistence
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe "#initialize(params)" do
|
49
|
+
before(:each) do
|
50
|
+
@test_value = mock(:test_value).to_s
|
51
|
+
end
|
52
|
+
|
53
|
+
context "when given a hash" do
|
54
|
+
before(:each) do
|
55
|
+
@params = {:test_attribute => @test_value}
|
56
|
+
end
|
57
|
+
|
58
|
+
it "converts the hash to attributes" do
|
59
|
+
@test_object = TestClass.new(@params)
|
60
|
+
@test_object.test_attribute.should == @test_value
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe "#from_hash(hash)" do
|
66
|
+
before(:each) do
|
67
|
+
@test_object = TestClass.new
|
68
|
+
end
|
69
|
+
|
70
|
+
it "parses a hash into instance attributes" do
|
71
|
+
@test_object.from_hash(:test_attribute => 'Test value')
|
72
|
+
@test_object.test_attribute.should == 'Test value'
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|