data_objects 0.2.0 → 0.9.2

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.
@@ -0,0 +1,18 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), 'spec_helper'))
2
+
3
+ describe DataObjects::Reader do
4
+
5
+ it "should define a standard API" do
6
+ connection = DataObjects::Connection.new('mock://localhost')
7
+
8
+ command = connection.create_command("SELECT * FROM example")
9
+
10
+ reader = command.execute_reader
11
+
12
+ reader.should respond_to(:close)
13
+ reader.should respond_to(:next!)
14
+ reader.should respond_to(:values)
15
+ reader.should respond_to(:fields)
16
+ end
17
+
18
+ end
@@ -0,0 +1,23 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), 'spec_helper'))
2
+
3
+ describe DataObjects::Result do
4
+
5
+ it "should define a standard API" do
6
+ connection = DataObjects::Connection.new('mock://localhost')
7
+
8
+ command = connection.create_command("SELECT * FROM example")
9
+
10
+ result = command.execute_non_query
11
+
12
+ # In case the driver needs to access the command or connection to load additional data.
13
+ result.instance_variables.should include('@command')
14
+
15
+ # Affected Rows:
16
+ result.should respond_to(:to_i)
17
+ result.to_i.should == 0
18
+
19
+ # The id of the inserted row.
20
+ result.should respond_to(:insert_id)
21
+ end
22
+
23
+ end
@@ -0,0 +1,5 @@
1
+ require 'rubygems'
2
+ require 'spec'
3
+
4
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib', 'data_objects'))
5
+ require File.expand_path(File.join(File.dirname(__FILE__), 'do_mock'))
@@ -0,0 +1,374 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
2
+ require File.join(File.dirname(__FILE__), '..', '..', 'lib', 'data_objects', 'support', 'pooling')
3
+ require 'timeout'
4
+
5
+ # This implements dispose
6
+ # and works perfectly with
7
+ # pooling.
8
+ class DisposableResource
9
+ include Object::Pooling
10
+ attr_reader :name
11
+
12
+ def initialize(name = "")
13
+ @name = name
14
+ end
15
+
16
+ def dispose
17
+ @name = nil
18
+ end
19
+ end
20
+
21
+ # This baby causes exceptions
22
+ # to be raised when you use
23
+ # it with pooling.
24
+ class UndisposableResource
25
+ end
26
+
27
+ describe Object::Pooling::ResourcePool do
28
+ before :each do
29
+ @pool = Object::Pooling::ResourcePool.new(7, DisposableResource, :expiration_period => 50)
30
+ end
31
+
32
+ it "responds to flush!" do
33
+ @pool.should respond_to(:flush!)
34
+ end
35
+
36
+ it "responds to aquire" do
37
+ @pool.should respond_to(:aquire)
38
+ end
39
+
40
+ it "responds to release" do
41
+ @pool.should respond_to(:release)
42
+ end
43
+
44
+ it "responds to :available?" do
45
+ @pool.should respond_to(:available?)
46
+ end
47
+
48
+ it "has a size limit" do
49
+ @pool.size_limit.should == 7
50
+ end
51
+
52
+ it "has initial size of zero" do
53
+ @pool.size.should == 0
54
+ end
55
+
56
+ it "has a set of reserved resources" do
57
+ @pool.instance_variable_get("@reserved").should be_empty
58
+ end
59
+
60
+ it "has a set of available resources" do
61
+ @pool.instance_variable_get("@available").should be_empty
62
+ end
63
+
64
+ it "knows class of resources (objects) it works with" do
65
+ @pool.class_of_resources.should == DisposableResource
66
+ end
67
+
68
+ it "raises exception when given anything but class for resources class" do
69
+ lambda {
70
+ @pool = Object::Pooling::ResourcePool.new(7, "Hooray!", {})
71
+ }.should raise_error(ArgumentError, /class/)
72
+ end
73
+
74
+ it "requires class of resources (objects) it works with to have a dispose instance method" do
75
+ lambda {
76
+ @pool = Object::Pooling::ResourcePool.new(3, UndisposableResource, {})
77
+ }.should raise_error(ArgumentError, /dispose/)
78
+ end
79
+
80
+ it "may take initialization arguments" do
81
+ @pool = Object::Pooling::ResourcePool.new(7, DisposableResource, { :initialization_args => ["paper"] })
82
+ @pool.instance_variable_get("@initialization_args").should == ["paper"]
83
+ end
84
+
85
+ it "may take expiration period option" do
86
+ @pool = Object::Pooling::ResourcePool.new(7, DisposableResource, { :expiration_period => 100 })
87
+ @pool.expiration_period.should == 100
88
+ end
89
+
90
+ it "has default expiration period of one minute" do
91
+ @pool = Object::Pooling::ResourcePool.new(7, DisposableResource, {})
92
+ @pool.expiration_period.should == 60
93
+ end
94
+
95
+ it "spawns a thread to dispose objects haven't been used for a while" do
96
+ @pool = Object::Pooling::ResourcePool.new(7, DisposableResource, {})
97
+ @pool.instance_variable_get("@pool_expiration_thread").should be_an_instance_of(Thread)
98
+ end
99
+ end
100
+
101
+
102
+
103
+ describe "Aquire from contant size pool" do
104
+ before :each do
105
+ DisposableResource.initialize_pool(2)
106
+ end
107
+
108
+ after :each do
109
+ DisposableResource.instance_variable_set("@__pool", nil)
110
+ end
111
+
112
+ it "increased size of the pool" do
113
+ @time = DisposableResource.pool.aquire
114
+ DisposableResource.pool.size.should == 1
115
+ end
116
+
117
+ it "places initialized instance in the reserved set" do
118
+ @time = DisposableResource.pool.aquire
119
+ DisposableResource.pool.instance_variable_get("@reserved").size.should == 1
120
+ end
121
+
122
+ it "raises an exception when pool size limit is hit" do
123
+ @t1 = DisposableResource.pool.aquire
124
+ @t2 = DisposableResource.pool.aquire
125
+
126
+ lambda { DisposableResource.pool.aquire }.should raise_error(RuntimeError)
127
+ end
128
+
129
+ it "returns last released resource" do
130
+ @t1 = DisposableResource.pool.aquire
131
+ @t2 = DisposableResource.pool.aquire
132
+ DisposableResource.pool.release(@t1)
133
+
134
+ DisposableResource.pool.aquire.should == @t1
135
+ end
136
+
137
+ it "really truly returns last released resource" do
138
+ @t1 = DisposableResource.pool.aquire
139
+ DisposableResource.pool.release(@t1)
140
+
141
+ @t2 = DisposableResource.pool.aquire
142
+ DisposableResource.pool.release(@t2)
143
+
144
+ @t3 = DisposableResource.pool.aquire
145
+ DisposableResource.pool.release(@t3)
146
+
147
+ DisposableResource.pool.aquire.should == @t1
148
+ @t1.should == @t3
149
+ end
150
+
151
+ it "sets allocation timestamp on resource instance" do
152
+ @t1 = DisposableResource.new
153
+ @t1.instance_variable_get("@__pool_aquire_timestamp").should be_close(Time.now, 2)
154
+ end
155
+ end
156
+
157
+
158
+
159
+ describe "Releasing from contant size pool" do
160
+ before :each do
161
+ DisposableResource.initialize_pool(2)
162
+ end
163
+
164
+ after :each do
165
+ DisposableResource.instance_variable_set("@__pool", nil)
166
+ end
167
+
168
+ it "decreases size of the pool" do
169
+ @t1 = DisposableResource.pool.aquire
170
+ @t2 = DisposableResource.pool.aquire
171
+ DisposableResource.pool.release(@t1)
172
+
173
+ DisposableResource.pool.size.should == 1
174
+ end
175
+
176
+ it "raises an exception on attempt to releases object not in pool" do
177
+ @t1 = DisposableResource.new
178
+ @t2 = Set.new
179
+
180
+ DisposableResource.pool.release(@t1)
181
+ lambda { DisposableResource.pool.release(@t2) }.should raise_error(RuntimeError)
182
+ end
183
+
184
+ it "disposes released object" do
185
+ @t1 = DisposableResource.pool.aquire
186
+
187
+ @t1.should_receive(:dispose)
188
+ DisposableResource.pool.release(@t1)
189
+ end
190
+
191
+ it "removes released object from reserved set" do
192
+ @t1 = DisposableResource.pool.aquire
193
+
194
+ lambda {
195
+ DisposableResource.pool.release(@t1)
196
+ }.should change(DisposableResource.pool.instance_variable_get("@reserved"), :size).by(-1)
197
+ end
198
+
199
+ it "returns released object back to available set" do
200
+ @t1 = DisposableResource.pool.aquire
201
+
202
+ lambda {
203
+ DisposableResource.pool.release(@t1)
204
+ }.should change(DisposableResource.pool.instance_variable_get("@available"), :size).by(1)
205
+ end
206
+
207
+ it "updates aquire timestamp on already allocated resource instance" do
208
+ # aquire it once
209
+ @t1 = DisposableResource.new
210
+ # wait a bit
211
+ sleep 3
212
+
213
+ # check old timestamp
214
+ @t1.instance_variable_get("@__pool_aquire_timestamp").should be_close(Time.now, 4)
215
+
216
+ # re-aquire
217
+ DisposableResource.pool.release(@t1)
218
+ @t1 = DisposableResource.new
219
+ # see timestamp is updated
220
+ @t1.instance_variable_get("@__pool_aquire_timestamp").should be_close(Time.now, 2)
221
+ end
222
+ end
223
+
224
+
225
+
226
+ describe Object::Pooling::ResourcePool, "#available?" do
227
+ before :each do
228
+ DisposableResource.initialize_pool(2)
229
+ DisposableResource.new
230
+ end
231
+
232
+ after :each do
233
+ DisposableResource.instance_variable_set("@__pool", nil)
234
+ end
235
+
236
+ it "returns true when pool has available instances" do
237
+ DisposableResource.pool.should be_available
238
+ end
239
+
240
+ it "returns false when pool is exhausted" do
241
+ # aquires the last available resource
242
+ DisposableResource.new
243
+ DisposableResource.pool.should_not be_available
244
+ end
245
+ end
246
+
247
+
248
+
249
+ describe "Flushing of contant size pool" do
250
+ before :each do
251
+ DisposableResource.initialize_pool(2)
252
+
253
+ @t1 = DisposableResource.new
254
+ @t2 = DisposableResource.new
255
+
256
+ # sanity check
257
+ DisposableResource.pool.instance_variable_get("@reserved").should_not be_empty
258
+ end
259
+
260
+ after :each do
261
+ DisposableResource.instance_variable_set("@__pool", nil)
262
+ end
263
+
264
+ it "disposes all pooled objects" do
265
+ [@t1, @t2].each { |instance| instance.should_receive(:dispose) }
266
+
267
+ DisposableResource.pool.flush!
268
+ end
269
+
270
+ it "empties reserved set" do
271
+ DisposableResource.pool.flush!
272
+
273
+ DisposableResource.pool.instance_variable_get("@reserved").should be_empty
274
+ end
275
+
276
+ it "returns all instances to available set" do
277
+ DisposableResource.pool.flush!
278
+
279
+ DisposableResource.pool.instance_variable_get("@available").size.should == 2
280
+ end
281
+ end
282
+
283
+
284
+
285
+ describe "Poolable resource class" do
286
+ before :each do
287
+ DisposableResource.initialize_pool(3, :initialization_args => ["paper"])
288
+ end
289
+
290
+ after :each do
291
+ DisposableResource.instance_variable_set("@__pool", nil)
292
+ end
293
+
294
+ it "aquires new instances from pool" do
295
+ @instance_one = DisposableResource.new
296
+
297
+ DisposableResource.pool.aquired?(@instance_one).should be(true)
298
+ end
299
+
300
+ it "flushed existing pool on re-initialization" do
301
+ DisposableResource.pool.should_receive(:flush!)
302
+ DisposableResource.initialize_pool(5)
303
+ end
304
+
305
+ it "replaces pool on re-initialization" do
306
+ DisposableResource.initialize_pool(5)
307
+ DisposableResource.pool.size_limit.should == 5
308
+ end
309
+
310
+ it "passes initialization parameters to newly created resource instances" do
311
+ DisposableResource.new.name.should == "paper"
312
+ end
313
+ end
314
+
315
+
316
+
317
+ describe "Pooled object", "on initialization" do
318
+ after :each do
319
+ DisposableResource.instance_variable_set("@__pool", nil)
320
+ end
321
+
322
+ it "does not flush pool" do
323
+ # using pool here initializes the pool first
324
+ # so we use instance variable directly
325
+ DisposableResource.instance_variable_get("@__pool").should_not_receive(:flush!)
326
+ DisposableResource.initialize_pool(23)
327
+ end
328
+
329
+ it "flushes pool first when re-initialized" do
330
+ DisposableResource.initialize_pool(5)
331
+ DisposableResource.pool.should_receive(:flush!)
332
+ DisposableResource.initialize_pool(23)
333
+ end
334
+ end
335
+
336
+
337
+
338
+ describe Object::Pooling::ResourcePool, "#time_to_dispose?" do
339
+ before :each do
340
+ DisposableResource.initialize_pool(7, :expiration_period => 2)
341
+ end
342
+
343
+ after :each do
344
+ DisposableResource.instance_variable_set("@__pool", nil)
345
+ end
346
+
347
+ it "returns true when object's last aquisition time is greater than limit" do
348
+ @t1 = DisposableResource.new
349
+ DisposableResource.pool.time_to_release?(@t1).should be(false)
350
+
351
+ sleep 3
352
+ DisposableResource.pool.time_to_release?(@t1).should be(true)
353
+ end
354
+ end
355
+
356
+
357
+
358
+ describe Object::Pooling::ResourcePool, "#dispose_outdated" do
359
+ before :each do
360
+ DisposableResource.initialize_pool(7, :expiration_period => 2)
361
+ end
362
+
363
+ after :each do
364
+ DisposableResource.instance_variable_set("@__pool", nil)
365
+ end
366
+
367
+ it "releases and thus disposes outdated instances" do
368
+ @t1 = DisposableResource.new
369
+ DisposableResource.pool.should_receive(:time_to_release?).with(@t1).and_return(true)
370
+ DisposableResource.pool.should_receive(:release).with(@t1)
371
+
372
+ DisposableResource.pool.release_outdated
373
+ end
374
+ end
@@ -0,0 +1,39 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), 'spec_helper'))
2
+
3
+ describe DataObjects::Transaction do
4
+
5
+ before :each do
6
+ @connection = mock("connection")
7
+ DataObjects::Connection.should_receive(:new).with("mock://mock/mock").once.and_return(@connection)
8
+ @transaction = DataObjects::Transaction.new("mock://mock/mock")
9
+ end
10
+
11
+ it "should have a HOST constant" do
12
+ DataObjects::Transaction::HOST.should_not == nil?
13
+ end
14
+
15
+ describe "#initialize" do
16
+ it "should provide a connection" do
17
+ @transaction.connection.should == @connection
18
+ end
19
+ it "should provide an id" do
20
+ @transaction.id.should_not == nil
21
+ end
22
+ it "should provide a unique id" do
23
+ DataObjects::Connection.should_receive(:new).with("mock://mock/mock2").once.and_return(@connection)
24
+ @transaction.id.should_not == DataObjects::Transaction.new("mock://mock/mock2").id
25
+ end
26
+ end
27
+ describe "#close" do
28
+ it "should close its connection" do
29
+ @connection.should_receive(:close).once
30
+ @transaction.close
31
+ end
32
+ end
33
+ [:begin, :commit, :rollback, :rollback_prepared, :prepare].each do |meth|
34
+ it "should raise NotImplementedError on #{meth}" do
35
+ lambda do @transaction.send(meth) end.should raise_error(NotImplementedError)
36
+ end
37
+ end
38
+
39
+ end