iotaz 0.1.0
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/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.
|