iotaz 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/doc/PERSISTENT +137 -0
- data/doc/README +470 -0
- data/examples/example01.rb +105 -0
- data/examples/example02.rb +105 -0
- data/lib/iotaz.rb +35 -0
- data/lib/iotaz/DatabaseInterfaceFactory.rb +92 -0
- data/lib/iotaz/FirebirdInterface.rb +788 -0
- data/lib/iotaz/IotazError.rb +81 -0
- data/lib/iotaz/MetaData.rb +646 -0
- data/lib/iotaz/ObjectPool.rb +299 -0
- data/lib/iotaz/Persistent.rb +155 -0
- data/lib/iotaz/Query.rb +566 -0
- data/lib/iotaz/Session.rb +379 -0
- data/lib/iotaz/WorkSet.rb +295 -0
- data/test/ActionTest.rb +31 -0
- data/test/AtomTest.rb +90 -0
- data/test/AttributeTest.rb +64 -0
- data/test/DatabaseInterfaceFactoryTest.rb +41 -0
- data/test/FirebirdInterfaceTest.rb +276 -0
- data/test/GeneratedAttributeTest.rb +124 -0
- data/test/IotazErrorTest.rb +23 -0
- data/test/IotazMetaDataTest.rb +87 -0
- data/test/ObjectPoolTest.rb +106 -0
- data/test/PersistentTest.rb +102 -0
- data/test/QueryFieldTest.rb +152 -0
- data/test/QueryRowTest.rb +42 -0
- data/test/QueryTest.rb +59 -0
- data/test/SessionTest.rb +162 -0
- data/test/UnitTest.rb +17 -0
- data/test/ValueCallbackTest.rb +44 -0
- data/test/dbinfo.rb +7 -0
- metadata +75 -0
@@ -0,0 +1,299 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
#--
|
4
|
+
# Copyright � Peter Wood, 2005
|
5
|
+
#
|
6
|
+
# The contents of this file are subject to the Mozilla Public License Version
|
7
|
+
# 1.1 (the "License"); you may not use this file except in compliance with the
|
8
|
+
# License. You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.mozilla.org/MPL/
|
11
|
+
#
|
12
|
+
# Software distributed under the License is distributed on an "AS IS" basis,
|
13
|
+
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
|
14
|
+
# the specificlanguage governing rights and limitations under the License.
|
15
|
+
#
|
16
|
+
# The Original Code is the FireRuby extension for the Ruby language.
|
17
|
+
#
|
18
|
+
# The Initial Developer of the Original Code is Peter Wood. All Rights
|
19
|
+
# Reserved.
|
20
|
+
#++
|
21
|
+
#
|
22
|
+
|
23
|
+
require 'thread'
|
24
|
+
|
25
|
+
module Iotaz
|
26
|
+
#
|
27
|
+
# This class represents an object within an object pool and shouldn't really
|
28
|
+
# be used outside of this context.
|
29
|
+
#
|
30
|
+
class PoolObject
|
31
|
+
#
|
32
|
+
# This is the constructor for the PoolObject class.
|
33
|
+
#
|
34
|
+
# ==== Parameters
|
35
|
+
# object:: The object to be held within the pool.
|
36
|
+
#
|
37
|
+
def initialize(object)
|
38
|
+
@object = object
|
39
|
+
@reserved = false
|
40
|
+
@timestamp = Time.new
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
#
|
45
|
+
# This method reserves a pool object.
|
46
|
+
#
|
47
|
+
def reserve
|
48
|
+
if @reserve
|
49
|
+
raise IotazError.new('Reserve called on reserved pool object.')
|
50
|
+
end
|
51
|
+
@reserved = true
|
52
|
+
@timestamp = Time.new
|
53
|
+
end
|
54
|
+
|
55
|
+
|
56
|
+
#
|
57
|
+
# This method releases a previously reserved pool object.
|
58
|
+
#
|
59
|
+
def release
|
60
|
+
if @reserve == false
|
61
|
+
raise IotazError.new('Release called on unreserved pool object.')
|
62
|
+
end
|
63
|
+
@reserved = false
|
64
|
+
@timestamp = Time.new
|
65
|
+
end
|
66
|
+
|
67
|
+
|
68
|
+
# Class attribute accessor.
|
69
|
+
attr_reader :object, :reserved, :timestamp
|
70
|
+
end # End of the PoolObject class.
|
71
|
+
|
72
|
+
|
73
|
+
#
|
74
|
+
# This class represents a simple object pool. It should be noted that this
|
75
|
+
# class starts a thread of it's own to monitor for idle resources if an
|
76
|
+
# idle period is set. Note also that the idle period and the idle test
|
77
|
+
# interval cannot be reduced to less than 60 seconds each. The pool is
|
78
|
+
# considered immutable after it is created.
|
79
|
+
#
|
80
|
+
class ObjectPool
|
81
|
+
# A definition for the minimum idle period setting.
|
82
|
+
MIN_IDLE = 60
|
83
|
+
|
84
|
+
# A definition for the minimum idle checking interval.
|
85
|
+
MIN_INTERVAL = 60
|
86
|
+
|
87
|
+
|
88
|
+
#
|
89
|
+
# This is the constructor for the ObjectPool class.
|
90
|
+
#
|
91
|
+
# ==== Parameters
|
92
|
+
# factory:: A reference to the object that will be used to manufacture
|
93
|
+
# and destroy the resource objects. This object must have
|
94
|
+
# methods called create and destroy.
|
95
|
+
# maximum:: The maximum amount of objects the pool is permitted to have
|
96
|
+
# available.
|
97
|
+
# minimum:: The minimum amount of objects that the pool is permitted
|
98
|
+
# to have available. Defaults to zero.
|
99
|
+
# idle:: The minimum amount of time, in seconds, that a resource my
|
100
|
+
# be idle before being destroyed. If set to -1, the default
|
101
|
+
# value then the resource objects will never be destroyed.
|
102
|
+
# interval:: The period, in seconds between checks for idle entries.
|
103
|
+
# Defaults to 600 seconds (10 minutes).
|
104
|
+
#
|
105
|
+
# ==== Exceptions
|
106
|
+
# IotazError:: Generated whenever a problem occurs with the settings for
|
107
|
+
# the object pool.
|
108
|
+
#
|
109
|
+
def initialize(factory, maximum, minimum=0, idle=-1, interval=600)
|
110
|
+
if maximum.kind_of?(Integer) == false or maximum < minimum
|
111
|
+
raise IotazError.new('Invalid object pool maximum setting specified.')
|
112
|
+
end
|
113
|
+
|
114
|
+
@factory = factory
|
115
|
+
@objects = []
|
116
|
+
@maximum = maximum
|
117
|
+
@minimum = minimum
|
118
|
+
@mutex = Mutex.new
|
119
|
+
@thread = nil
|
120
|
+
@blocked = []
|
121
|
+
if idle >= 0
|
122
|
+
@idle = idle > MIN_IDLE ? idle : MIN_IDLE
|
123
|
+
else
|
124
|
+
@idle = -1
|
125
|
+
end
|
126
|
+
@interval = interval > MIN_INTERVAL ? interval : MIN_INTERVAL
|
127
|
+
|
128
|
+
start_thread if idle != -1
|
129
|
+
|
130
|
+
# Add the minimum number of resources.
|
131
|
+
@minimum.times do
|
132
|
+
@objects.push(PoolObject.new(@factory.create))
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
|
137
|
+
#
|
138
|
+
# This method obtains an object from the pool. If there isn't a free
|
139
|
+
# object in the pool one will be manufactured unless the pool has
|
140
|
+
# reached maximum capacity. The calling thread will block until a
|
141
|
+
# resource has become available. The method also accepts a block
|
142
|
+
# which delimits the lease time that the acquired object is given.
|
143
|
+
# The acquired object is passed as a parameter to the block and is
|
144
|
+
# automatically released when the block exits. If a block is given
|
145
|
+
# then the method always returns nil.
|
146
|
+
#
|
147
|
+
# ==== Parameters
|
148
|
+
# wait:: A boolean flag to indicate whether the thread will wait for
|
149
|
+
# the resource to become available if one isn't immediately
|
150
|
+
# available.
|
151
|
+
#
|
152
|
+
# ==== Exceptions
|
153
|
+
# IotazError:: Generated whenever the caller has indicated that they are
|
154
|
+
# not prepared to wait for a pool object and a pool object is
|
155
|
+
# not immediately available.
|
156
|
+
# Exception:: If a block is given and, during it's execution, an
|
157
|
+
# exception is raised it will be thrown out of the method
|
158
|
+
# call as is.
|
159
|
+
#
|
160
|
+
def acquire(wait=true)
|
161
|
+
resource = nil
|
162
|
+
while resource == nil
|
163
|
+
@mutex.synchronize do
|
164
|
+
resource = @objects.find {|object| object.reserved == false}
|
165
|
+
if resource == nil
|
166
|
+
if @objects.size < @maximum
|
167
|
+
resource = PoolObject.new(@factory.create)
|
168
|
+
resource.reserve
|
169
|
+
@objects.push(resource)
|
170
|
+
resource = resource.object
|
171
|
+
else
|
172
|
+
@blocked.push(Thread.current) if wait
|
173
|
+
end
|
174
|
+
else
|
175
|
+
resource.reserve
|
176
|
+
resource = resource.object
|
177
|
+
end
|
178
|
+
end
|
179
|
+
if resource == nil and wait == false
|
180
|
+
raise IotazError.new('Pooled object not available.')
|
181
|
+
end
|
182
|
+
Thread.stop if resource == nil and wait == true
|
183
|
+
end
|
184
|
+
|
185
|
+
if block_given?
|
186
|
+
begin
|
187
|
+
yield(resource)
|
188
|
+
rescue Exception => error
|
189
|
+
raise error
|
190
|
+
ensure
|
191
|
+
release(resource)
|
192
|
+
resource = nil
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
resource
|
197
|
+
end
|
198
|
+
|
199
|
+
|
200
|
+
#
|
201
|
+
# This method returns an object to the pool. This method will also
|
202
|
+
# restart any threads waiting for pool resources.
|
203
|
+
#
|
204
|
+
# ==== Parameters
|
205
|
+
# object:: A reference to the object instance being returned to the
|
206
|
+
# pool.
|
207
|
+
#
|
208
|
+
def release(object)
|
209
|
+
@mutex.synchronize do
|
210
|
+
resource = @objects.find {|entry| entry.object == object}
|
211
|
+
if resource != nil
|
212
|
+
resource.release
|
213
|
+
@blocked.each do |thread|
|
214
|
+
thread.run
|
215
|
+
end
|
216
|
+
@blocked = []
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
|
222
|
+
#
|
223
|
+
# This method initiates the thread that monitors the pool for idle
|
224
|
+
# resources.
|
225
|
+
#
|
226
|
+
def start_thread
|
227
|
+
@mutex.synchronize do
|
228
|
+
if @therad == nil
|
229
|
+
@thread = Thread.new do
|
230
|
+
idle_sweep
|
231
|
+
sleep(@interval)
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
|
238
|
+
#
|
239
|
+
# This method cleans up the resources associated with a pool and shuts
|
240
|
+
# down it's monitor thread. The method should be called before the
|
241
|
+
# application using the pool terminates.
|
242
|
+
#
|
243
|
+
def shutdown
|
244
|
+
@mutex.synchronize do
|
245
|
+
@thread.exit if @thread != nil
|
246
|
+
@objects.each do |entry|
|
247
|
+
@factory.destroy(entry.object)
|
248
|
+
end
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
|
253
|
+
#
|
254
|
+
# This method returns the number of objects currently held within a
|
255
|
+
# an object pool.
|
256
|
+
#
|
257
|
+
def size
|
258
|
+
@objects.size
|
259
|
+
end
|
260
|
+
|
261
|
+
|
262
|
+
#
|
263
|
+
# This method returns the number of objects currently available within
|
264
|
+
# an object pool.
|
265
|
+
#
|
266
|
+
def available
|
267
|
+
total = 0
|
268
|
+
@mutex.synchronize do
|
269
|
+
@objects.each {|entry| total += 1 if entry.reserved == false}
|
270
|
+
end
|
271
|
+
total
|
272
|
+
end
|
273
|
+
|
274
|
+
|
275
|
+
#
|
276
|
+
# This method is used internally by the pool to remove idle entries
|
277
|
+
#
|
278
|
+
def idle_sweep
|
279
|
+
@mutex.synchronize do
|
280
|
+
@objects.delete_if do |entry|
|
281
|
+
result = false
|
282
|
+
if Time.new.to_i - entry.timestamp.to_i >= @interval and
|
283
|
+
@objects.size > @minimum
|
284
|
+
@factory.destroy(entry.object)
|
285
|
+
result = true
|
286
|
+
end
|
287
|
+
result
|
288
|
+
end
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
|
293
|
+
# Class attribute accessor.
|
294
|
+
attr_reader :factory, :minimum, :maximum, :idle, :interval
|
295
|
+
|
296
|
+
# Method access alterations.
|
297
|
+
private :start_thread, :idle_sweep
|
298
|
+
end # End of the ObjectPool class.
|
299
|
+
end # End of the Iotaz module.
|
@@ -0,0 +1,155 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
#--
|
4
|
+
# Copyright (C) Peter Wood, 2005
|
5
|
+
#
|
6
|
+
# The contents of this file are subject to the Mozilla Public License Version
|
7
|
+
# 1.1 (the "License"); you may not use this file except in compliance with the
|
8
|
+
# License. You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.mozilla.org/MPL/
|
11
|
+
#
|
12
|
+
# Software distributed under the License is distributed on an "AS IS" basis,
|
13
|
+
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
|
14
|
+
# the specificlanguage governing rights and limitations under the License.
|
15
|
+
#
|
16
|
+
# The Original Code is the FireRuby extension for the Ruby language.
|
17
|
+
#
|
18
|
+
# The Initial Developer of the Original Code is Peter Wood. All Rights
|
19
|
+
# Reserved.
|
20
|
+
#++
|
21
|
+
#
|
22
|
+
|
23
|
+
module Iotaz
|
24
|
+
module Persistent
|
25
|
+
# Module constants.
|
26
|
+
@@code = [%q{@@iotaz_meta_data = Iotaz::IotazMetaData.new(self)},
|
27
|
+
%q{def self.class_basename(source)
|
28
|
+
name = source.instance_of?(String) ? source : source.name
|
29
|
+
index = name.rindex("::")
|
30
|
+
if index != nil
|
31
|
+
index += 2
|
32
|
+
name = name[index, name.length - index]
|
33
|
+
end
|
34
|
+
name
|
35
|
+
end},
|
36
|
+
%q{def self.iotaz_meta_data
|
37
|
+
@@iotaz_meta_data
|
38
|
+
end},
|
39
|
+
%q{def self.persistent_attr(name, column=nil, readonly=false)
|
40
|
+
attr :"#{name}", true
|
41
|
+
private :"#{name}=" if readonly
|
42
|
+
name = name.id2name if name.class == Symbol
|
43
|
+
attribute = Attribute.new(name, column)
|
44
|
+
@@iotaz_meta_data.add_attribute(attribute)
|
45
|
+
end},
|
46
|
+
%q{def self.sequence_attr(name, column=nil, key=true,
|
47
|
+
source=nil, readonly=true,
|
48
|
+
create=true, update=false)
|
49
|
+
attr :"#{name}", true
|
50
|
+
private :"#{name}=" if readonly
|
51
|
+
name = name.id2name if name.class == Symbol
|
52
|
+
events = ""
|
53
|
+
if create == true and update == false
|
54
|
+
events = 'INSERT'
|
55
|
+
elsif create == false and update == true
|
56
|
+
events = 'UPDATE'
|
57
|
+
else
|
58
|
+
events = 'INSERT,UPDATE'
|
59
|
+
end
|
60
|
+
generator = source
|
61
|
+
if source == nil
|
62
|
+
generator = "#{class_basename(self)}_ID_SQ".upcase
|
63
|
+
end
|
64
|
+
attribute = GeneratedAttribute.new(name, 'SEQUENCE',
|
65
|
+
events, generator,
|
66
|
+
column)
|
67
|
+
@@iotaz_meta_data.add_attribute(attribute, key)
|
68
|
+
end},
|
69
|
+
%q{def self.date_attr(name, column=nil, create=true,
|
70
|
+
update=false, value='TODAY',
|
71
|
+
readonly=true)
|
72
|
+
attr :"#{name}", true
|
73
|
+
private :"#{name}=" if readonly
|
74
|
+
name = name.id2name if name.class == Symbol
|
75
|
+
events = ""
|
76
|
+
if create == true and update == false
|
77
|
+
events = 'INSERT'
|
78
|
+
elsif create == false and update == true
|
79
|
+
events = 'UPDATE'
|
80
|
+
else
|
81
|
+
events = 'INSERT,UPDATE'
|
82
|
+
end
|
83
|
+
attribute = GeneratedAttribute.new(name, 'DATE', events,
|
84
|
+
value, column)
|
85
|
+
@@iotaz_meta_data.add_attribute(attribute)
|
86
|
+
end},
|
87
|
+
%q{def self.time_attr(name, column=nil, create=true,
|
88
|
+
update=false, readonly=true)
|
89
|
+
attr :"#{name}", true
|
90
|
+
private :"#{name}=" if readonly
|
91
|
+
name = name.id2name if name.class == Symbol
|
92
|
+
events = ""
|
93
|
+
if create == true and update == false
|
94
|
+
events = 'INSERT'
|
95
|
+
elsif create == false and update == true
|
96
|
+
events = 'UPDATE'
|
97
|
+
else
|
98
|
+
events = 'INSERT,UPDATE'
|
99
|
+
end
|
100
|
+
attribute = GeneratedAttribute.new(name, 'TIME', events,
|
101
|
+
'NOW', column)
|
102
|
+
@@iotaz_meta_data.add_attribute(attribute)
|
103
|
+
end},
|
104
|
+
%q{def self.timestamp_attr(name, column=nil, create=true,
|
105
|
+
update=false, readonly=true)
|
106
|
+
attr :"#{name}", true
|
107
|
+
private :"#{name}=" if readonly
|
108
|
+
name = name.id2name if name.class == Symbol
|
109
|
+
events = ""
|
110
|
+
if create == true and update == false
|
111
|
+
events = 'INSERT'
|
112
|
+
elsif create == false and update == true
|
113
|
+
events = 'UPDATE'
|
114
|
+
else
|
115
|
+
events = 'INSERT,UPDATE'
|
116
|
+
end
|
117
|
+
attribute = GeneratedAttribute.new(name, 'TIMESTAMP',
|
118
|
+
events, 'NOW', column)
|
119
|
+
@@iotaz_meta_data.add_attribute(attribute)
|
120
|
+
end},
|
121
|
+
%q{def self.table_name=(name)
|
122
|
+
@@iotaz_meta_data.table = name
|
123
|
+
end}]
|
124
|
+
|
125
|
+
|
126
|
+
#
|
127
|
+
# This method gets invoked whenever this module is used to extend an
|
128
|
+
# existing class or module.
|
129
|
+
#
|
130
|
+
# ==== Parameters
|
131
|
+
# target:: A reference to the class or module that is being extended.
|
132
|
+
#
|
133
|
+
def Persistent.extend_object(target)
|
134
|
+
@@code.each do |entry|
|
135
|
+
target.module_eval(entry)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
|
140
|
+
#
|
141
|
+
# This method gets invoked whenever this module is included into another
|
142
|
+
# module or class.
|
143
|
+
#
|
144
|
+
# ==== Parameters
|
145
|
+
# target:: A reference to the class or module including the Persistent
|
146
|
+
# module.
|
147
|
+
#
|
148
|
+
def Persistent.append_features(target)
|
149
|
+
super(target)
|
150
|
+
@@code.each do |entry|
|
151
|
+
target.module_eval(entry)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end # End of the Persistent module
|
155
|
+
end # End of the Iotaz module.
|