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/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