light_mongo 0.0.1
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/.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
|