Rubernate 0.1.6 → 0.1.7
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/rubernate.rb +6 -5
- data/lib/rubernate/impl/dbi_generic.rb +120 -54
- data/lib/rubernate/impl/dbi_genpk.rb +67 -0
- data/lib/rubernate/impl/dbi_mysql.rb +1 -0
- data/lib/rubernate/impl/dbi_oracle.rb +25 -16
- data/lib/rubernate/impl/dbi_pg.rb +16 -6
- data/lib/rubernate/init/init_oracle.rb +1 -1
- data/lib/rubernate/init/init_pg.rb +1 -1
- data/lib/rubernate/peer.rb +30 -1
- data/lib/rubernate/persistent.rb +11 -1
- data/lib/rubernate/queries.rb +1 -2
- data/tests/rubernate/impl/dbi_generic_stub.rb +111 -10
- data/tests/rubernate/impl/dbi_mysql_test.rb +2 -2
- data/tests/rubernate/impl/dbi_oracle_test.rb +2 -2
- data/tests/rubernate/impl/dbi_pg_test.rb +2 -2
- data/tests/rubernate/rubernate_test.rb +17 -0
- metadata +3 -2
data/lib/rubernate.rb
CHANGED
@@ -37,6 +37,7 @@ module Rubernate
|
|
37
37
|
require 'rubernate/peer'
|
38
38
|
require 'rubernate/runtime'
|
39
39
|
require 'rubernate/queries'
|
40
|
+
require 'rubernate/impl/dbi_genpk'
|
40
41
|
|
41
42
|
# Rubernate core singelton methods.
|
42
43
|
class << self
|
@@ -53,14 +54,14 @@ module Rubernate
|
|
53
54
|
# * user - databases user name
|
54
55
|
# * password - databases users password
|
55
56
|
def config db, url, user, password
|
56
|
-
impl, init = case db
|
57
|
+
impl, init, gen = case db
|
57
58
|
when :mysql : require_mysql
|
58
59
|
when :oracle: require_oracle
|
59
60
|
when :pg : require_pg
|
60
61
|
when :memory: require_memory
|
61
62
|
else raise "Database #{db} not supported"
|
62
63
|
end
|
63
|
-
self.configuration = DBI::Configuration.new impl, init, url, user, password
|
64
|
+
self.configuration = DBI::Configuration.new impl, init, gen, url, user, password
|
64
65
|
end
|
65
66
|
|
66
67
|
|
@@ -108,21 +109,21 @@ module Rubernate
|
|
108
109
|
def require_mysql # :nodoc:
|
109
110
|
require 'rubernate/impl/dbi_mysql'
|
110
111
|
require 'rubernate/init/init_mysql'
|
111
|
-
[DBI::MySqlRuntime, DBI::MySqlInit.new]
|
112
|
+
[DBI::MySqlRuntime, DBI::MySqlInit.new, nil]
|
112
113
|
end
|
113
114
|
|
114
115
|
# Loads Oracle module
|
115
116
|
def require_oracle # :nodoc:
|
116
117
|
require 'rubernate/impl/dbi_oracle'
|
117
118
|
require 'rubernate/init/init_oracle'
|
118
|
-
[DBI::OracleRuntime, DBI::OracleInit.new]
|
119
|
+
[DBI::OracleRuntime, DBI::OracleInit.new, DBI::OraPKChunkFactory.new]
|
119
120
|
end
|
120
121
|
|
121
122
|
# Loads Postgres module
|
122
123
|
def require_pg # :nodoc:
|
123
124
|
require 'rubernate/impl/dbi_pg'
|
124
125
|
require 'rubernate/init/init_pg'
|
125
|
-
[DBI::PgRuntime, DBI::PgInit.new]
|
126
|
+
[DBI::PgRuntime, DBI::PgInit.new, DBI::PgPKChunkFactory.new]
|
126
127
|
end
|
127
128
|
|
128
129
|
# Loads Memory module used only for testing :nodoc:
|
@@ -31,15 +31,16 @@ module DBI
|
|
31
31
|
P_TIME = 6
|
32
32
|
P_REF = 7
|
33
33
|
|
34
|
-
|
34
|
+
# Holds configuration information and serves as factory for Runtime objects.
|
35
35
|
class Configuration
|
36
36
|
# Accepts Runtime impl class, db initializer class, database url, user name, and user password
|
37
|
-
def initialize klass, init, db_url, db_user, db_password
|
37
|
+
def initialize klass, init, pkcf, db_url, db_user, db_password
|
38
38
|
@klass, @init, @db_url, @db_user, @db_password = klass, init, db_url, db_user, db_password
|
39
|
+
PKGenerator.chunk_factory = pkcf
|
39
40
|
# @params = {'AutoCommit' => false} #TODO: refine
|
40
41
|
end
|
41
|
-
|
42
|
-
# Creates initialized
|
42
|
+
|
43
|
+
# Creates initialized Runtime
|
43
44
|
def create
|
44
45
|
@klass.new connect
|
45
46
|
end
|
@@ -68,12 +69,16 @@ module DBI
|
|
68
69
|
class Runtime < Rubernate::Runtime
|
69
70
|
include DBI
|
70
71
|
|
71
|
-
|
72
|
-
|
73
|
-
DELETE FROM R_PARAMS WHERE OBJECT_PK = ?
|
72
|
+
DELETE_PARAMS = <<-SQL
|
73
|
+
DELETE FROM R_PARAMS WHERE OBJECT_PK = ? AND NAME = ?
|
74
74
|
SQL
|
75
|
-
|
76
|
-
|
75
|
+
INSERT_PARAMS = <<-SQL
|
76
|
+
INSERT INTO R_PARAMS VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
77
|
+
SQL
|
78
|
+
UPDATE_PARAMS = <<-SQL
|
79
|
+
UPDATE R_PARAMS SET FLAGS = ?, INT_VALUE = ?, FLT_VALUE = ?,
|
80
|
+
STR_VALUE = ?, DAT_VALUE = ?, REF_VALUE = ?
|
81
|
+
WHERE OBJECT_PK = ? AND NAME = ?
|
77
82
|
SQL
|
78
83
|
DELETE_OBJECT = <<-SQL
|
79
84
|
DELETE FROM R_OBJECTS WHERE OBJECT_PK = ?
|
@@ -81,9 +86,6 @@ module DBI
|
|
81
86
|
CREATE_PEER = <<-SQL
|
82
87
|
INSERT INTO R_OBJECTS (OBJECT_PK, OBJECT_CLASS) values (?, ?)
|
83
88
|
SQL
|
84
|
-
SAVE_PARAMS = <<-SQL
|
85
|
-
INSERT INTO R_PARAMS VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
86
|
-
SQL
|
87
89
|
SELECT_PARAMS = <<-SQL
|
88
90
|
SELECT P.*, R.OBJECT_CLASS, O.OBJECT_CLASS
|
89
91
|
FROM R_PARAMS P JOIN R_OBJECTS O ON (O.OBJECT_PK = P.OBJECT_PK)
|
@@ -113,21 +115,13 @@ module DBI
|
|
113
115
|
# Updates object state in database
|
114
116
|
def save objects
|
115
117
|
objects = [objects] unless objects.kind_of? Array
|
116
|
-
return if objects.empty?
|
117
|
-
#
|
118
|
-
|
119
|
-
#
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
save_param sth, object, name.to_s, param
|
124
|
-
}
|
125
|
-
end
|
126
|
-
end
|
127
|
-
# clear dirty flag
|
128
|
-
for object in objects
|
129
|
-
object.__peer.dirty = false
|
130
|
-
end
|
118
|
+
return if objects.empty?
|
119
|
+
# gather parameters changes
|
120
|
+
create, delete, update, insert = gather_changes objects
|
121
|
+
bulk_o_create create unless create.empty? # create virtual objects
|
122
|
+
bulk_p_delete delete unless delete.empty? # delete params
|
123
|
+
bulk_p_update update unless update.empty? # update params
|
124
|
+
bulk_p_insert insert unless insert.empty? # insert params
|
131
125
|
end
|
132
126
|
|
133
127
|
# Loads object by primary_key
|
@@ -173,7 +167,8 @@ module DBI
|
|
173
167
|
@dbh.rollback rescue Log.error 'Error during rollback in session failed state'
|
174
168
|
@dbh.disconnect rescue Log.error 'Error during disconnect in session failed state'
|
175
169
|
end
|
176
|
-
|
170
|
+
|
171
|
+
private
|
177
172
|
# Select objects from database by query and determines objects
|
178
173
|
# that should be loaded and put them to buffer.
|
179
174
|
def find_buffer query, params, buffer
|
@@ -197,45 +192,117 @@ module DBI
|
|
197
192
|
}
|
198
193
|
}
|
199
194
|
end
|
200
|
-
|
201
|
-
#
|
202
|
-
def
|
203
|
-
|
195
|
+
|
196
|
+
# Returns tree arrays [delete, update, insert] each of which consist of two values [object, attr_name]
|
197
|
+
def gather_changes objects
|
198
|
+
create, delete, update, insert = [], [], [], []
|
199
|
+
for obj in objects
|
200
|
+
create << obj if obj.__virtual?
|
201
|
+
for attr, change in obj.__peer.change_track
|
202
|
+
track, value = [obj, attr], obj.__peer[attr]
|
203
|
+
if change == :inserted # New values shoud be inserted
|
204
|
+
insert << track unless value.is_a? Persistent and ref_lost? obj, attr, value
|
205
|
+
else # Updated values should be considered by another way
|
206
|
+
case value
|
207
|
+
when Rubernate::Peer::Container : # Collections
|
208
|
+
delete << track # first delete all values
|
209
|
+
insert << track # second insert again all values
|
210
|
+
when Persistent :
|
211
|
+
if ref_lost? obj, attr, value
|
212
|
+
delete << track
|
213
|
+
else
|
214
|
+
update << track
|
215
|
+
end
|
216
|
+
when nil: delete << track
|
217
|
+
else update << track # just update record in database
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
221
|
+
obj.__virtual = false # reset virtual flag
|
222
|
+
obj.__peer.dirty = false # reset dirty flag
|
223
|
+
end
|
224
|
+
[create, delete, update, insert]
|
204
225
|
end
|
205
226
|
|
227
|
+
def bulk_o_create objects
|
228
|
+
@dbh.prepare CREATE_PEER do |sth|
|
229
|
+
objects.each {|obj| sth.execute obj.primary_key, obj.class.name}
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
def bulk_p_delete params
|
234
|
+
@dbh.prepare DELETE_PARAMS do |sth|
|
235
|
+
params.each {|obj, attr| sth.execute obj.primary_key, attr.to_s}
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
def bulk_p_insert params
|
240
|
+
@dbh.prepare INSERT_PARAMS do |sth|
|
241
|
+
params.each {|obj, attr|
|
242
|
+
save_param sth, obj, attr.to_s, obj.__peer[attr]
|
243
|
+
}
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
def bulk_p_update params
|
248
|
+
@dbh.prepare UPDATE_PARAMS do |sth|
|
249
|
+
params.each {|obj, attr|
|
250
|
+
update_param sth, obj.primary_key, attr.to_s, obj.__peer[attr]
|
251
|
+
}
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
206
255
|
def save_param sth, obj, name, param
|
207
256
|
case param
|
208
257
|
when Persistent: save_ref sth, obj, name, param
|
209
258
|
when Hash: save_hash sth, obj, name, param
|
210
259
|
when Array: save_array sth, obj, name, param
|
211
|
-
when Integer:
|
212
|
-
when Float:
|
213
|
-
when Time:
|
214
|
-
when Date:
|
215
|
-
else
|
260
|
+
when Integer: sth.execute obj.primary_key, name, *row_fmt_int(param)
|
261
|
+
when Float: sth.execute obj.primary_key, name, *row_fmt_float(param)
|
262
|
+
when Time: sth.execute obj.primary_key, name, *row_fmt_time(param)
|
263
|
+
when Date: sth.execute obj.primary_key, name, *row_fmt_date(param)
|
264
|
+
else sth.execute obj.primary_key, name, *row_fmt_str(param)
|
216
265
|
end
|
217
266
|
end
|
218
|
-
|
219
|
-
def
|
220
|
-
|
267
|
+
|
268
|
+
def update_param sth, pk, name, param
|
269
|
+
row_fmt = case param
|
270
|
+
when Persistent: row_fmt_ref param
|
271
|
+
when Integer: row_fmt_int param
|
272
|
+
when Float: row_fmt_float param
|
273
|
+
when Time: row_fmt_time param
|
274
|
+
when Date: row_fmt_date param
|
275
|
+
when Hash: raise 'internal error Hash is not supposed to be updated'
|
276
|
+
when Array: raise 'internal error Array is not supposed to be updated'
|
277
|
+
else row_fmt_str param # It's supposed to be string here
|
278
|
+
end
|
279
|
+
sth.execute *(row_fmt << pk << name)
|
221
280
|
end
|
222
281
|
|
223
|
-
def
|
224
|
-
|
282
|
+
def row_fmt_int int
|
283
|
+
[PARAM_FLAG_INT, int, nil, nil, nil, nil]
|
225
284
|
end
|
226
|
-
|
227
|
-
def save_str sth, pk, name, str
|
228
|
-
sth.execute pk, name, PARAM_FLAG_STRING, nil, nil, str, nil, nil
|
229
|
-
end
|
230
285
|
|
231
|
-
def
|
232
|
-
|
286
|
+
def row_fmt_float float
|
287
|
+
[PARAM_FLAG_FLOAT, nil, float, nil, nil, nil]
|
288
|
+
end
|
289
|
+
|
290
|
+
def row_fmt_str str
|
291
|
+
[PARAM_FLAG_STRING, nil, nil, str, nil, nil]
|
292
|
+
end
|
293
|
+
|
294
|
+
def row_fmt_time time
|
295
|
+
[PARAM_FLAG_TIME, nil, nil, nil, TimeType.new(time), nil]
|
296
|
+
end
|
297
|
+
|
298
|
+
def row_fmt_date time
|
299
|
+
[PARAM_FLAG_DATE, nil, nil, nil, TimeType.new(time), nil]
|
300
|
+
end
|
301
|
+
|
302
|
+
def row_fmt_ref obj
|
303
|
+
[PARAM_FLAG_REF, nil, nil, nil, nil, obj.primary_key]
|
233
304
|
end
|
234
305
|
|
235
|
-
def save_date sth, pk, name, date
|
236
|
-
sth.execute pk, name, PARAM_FLAG_DATE, nil, nil, nil, TimeType.new(date), nil
|
237
|
-
end
|
238
|
-
|
239
306
|
def save_ref sth, obj, name, ref
|
240
307
|
sth.execute obj.primary_key, name, PARAM_FLAG_REF, nil, nil, nil, nil,
|
241
308
|
ref.primary_key unless ref_lost? obj, name, ref
|
@@ -258,8 +325,7 @@ module DBI
|
|
258
325
|
when ::Date: sth.execute pk, name, HASH_DATE_REF, nil, nil, nil, TimeType.new(key), ref
|
259
326
|
when ::Time: sth.execute pk, name, HASH_TIME_REF, nil, nil, nil, TimeType.new(key), ref
|
260
327
|
when nil: sth.execute pk, name, PARAM_FLAG_HASH, nil, nil, nil, nil, ref
|
261
|
-
else
|
262
|
-
raise "invalid hash key value #{key}"
|
328
|
+
else raise "invalid hash key value #{key}"
|
263
329
|
end
|
264
330
|
end
|
265
331
|
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'thread'
|
2
|
+
|
3
|
+
module Rubernate
|
4
|
+
module DBI
|
5
|
+
class PKChunk
|
6
|
+
def initialize first, size
|
7
|
+
@next, @limit = first, first + size
|
8
|
+
end
|
9
|
+
def empty?
|
10
|
+
@next >= @limit
|
11
|
+
end
|
12
|
+
def get_pk
|
13
|
+
@next+= 1
|
14
|
+
@next - 1
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class PKChunkFactory
|
19
|
+
PK_CHUNCK_SIZE = 25 # Pk chunck size (step of sequence).
|
20
|
+
|
21
|
+
# Returns new chunk
|
22
|
+
def create dbh
|
23
|
+
PKChunk.new dbh.select_all(@get_from_seq)[0][0].to_i, PK_CHUNCK_SIZE
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# Represents primary key generator
|
28
|
+
class PKGenerator
|
29
|
+
class << self
|
30
|
+
attr_accessor :chunk_factory
|
31
|
+
|
32
|
+
# Gets PKGenerator instance
|
33
|
+
def instance
|
34
|
+
PKGenerator.new
|
35
|
+
end
|
36
|
+
|
37
|
+
@@chunk_pool_lock = Mutex.new
|
38
|
+
@@chunk_pool = []
|
39
|
+
# Acquires chunk
|
40
|
+
def acquire_chunk dbh
|
41
|
+
@@chunk_pool_lock.synchronize {
|
42
|
+
return @@chunk_pool.shift unless @@chunk_pool.empty?
|
43
|
+
}
|
44
|
+
self.chunk_factory.create dbh
|
45
|
+
end
|
46
|
+
# Release chunk
|
47
|
+
def release_chunk chunk
|
48
|
+
@@chunk_pool_lock.synchronize {
|
49
|
+
@@chunk_pool << chunk
|
50
|
+
}
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# Generates pk.
|
55
|
+
def generate dbh
|
56
|
+
@chunk = self.class.acquire_chunk dbh if @chunk.nil? or @chunk.empty?
|
57
|
+
@chunk.get_pk
|
58
|
+
end
|
59
|
+
|
60
|
+
# Should be invoked at the end of the session.
|
61
|
+
def release
|
62
|
+
self.class.release_chunk @chunk if @chunk and !@chunk.empty?
|
63
|
+
@chunk = nil
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -1,34 +1,43 @@
|
|
1
1
|
require 'dbi'
|
2
|
+
require 'rubernate/impl/dbi_genpk'
|
2
3
|
require 'rubernate/impl/dbi_generic'
|
3
4
|
|
4
5
|
module Rubernate
|
5
6
|
module DBI
|
6
|
-
class
|
7
|
+
class OraPKChunkFactory < PKChunkFactory
|
7
8
|
SELECT_NEXT_PK = <<-SQL
|
8
9
|
SELECT R_PK_SEQUENCE.NEXTVAL FROM DUAL
|
9
10
|
SQL
|
10
|
-
|
11
|
+
def initialize
|
12
|
+
@get_from_seq = SELECT_NEXT_PK
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class OracleRuntime < Runtime
|
17
|
+
def initialize dbh
|
18
|
+
super
|
19
|
+
@pk_gen = PKGenerator.new
|
20
|
+
end
|
21
|
+
|
11
22
|
# Creates record in r_objects for specified object
|
12
23
|
def create object
|
13
24
|
object.__peer = Rubernate::Peer.new unless object.__peer
|
14
|
-
object.primary_key
|
15
|
-
|
25
|
+
object.primary_key = @pk_gen.generate @dbh
|
26
|
+
object.__virtual = true
|
16
27
|
object.__peer.dirty = true
|
17
28
|
object.primary_key
|
18
|
-
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def close
|
32
|
+
super
|
33
|
+
@pk_gen.release
|
34
|
+
end
|
35
|
+
|
36
|
+
def failed
|
37
|
+
@pk_gen.release
|
38
|
+
end
|
19
39
|
|
20
40
|
private
|
21
|
-
# Oracle requires that IN list must be less or equal to 1000 items
|
22
|
-
def clear_params objects
|
23
|
-
return super if objects.size <= 1000
|
24
|
-
idx = 0
|
25
|
-
while chunck = objects.slice(idx, 1000)
|
26
|
-
break if chunck.empty?
|
27
|
-
super chunck
|
28
|
-
idx+= chunck.size
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
41
|
# Oracle requires that IN list must be less or equal to 1000 items
|
33
42
|
def load_buffer buffer
|
34
43
|
return super if buffer.size <= 1000
|
@@ -3,30 +3,40 @@ require 'rubernate/impl/dbi_generic'
|
|
3
3
|
|
4
4
|
module Rubernate
|
5
5
|
module DBI
|
6
|
-
class
|
6
|
+
class PgPKChunkFactory < PKChunkFactory
|
7
7
|
SELECT_NEXT_PK = <<-SQL
|
8
8
|
SELECT NEXTVAL('R_PK_SEQUENCE')
|
9
9
|
SQL
|
10
|
-
|
10
|
+
def initialize
|
11
|
+
@get_from_seq = SELECT_NEXT_PK
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class PgRuntime < Runtime
|
11
16
|
# Invokes super and then issues 'BEGIN'
|
12
17
|
def initialize dbh
|
13
18
|
super
|
14
19
|
dbh.do 'BEGIN'
|
20
|
+
@pk_gen = PKGenerator.new
|
15
21
|
end
|
16
22
|
|
17
23
|
# Creates record in r_objects for specified object
|
18
24
|
def create object
|
19
25
|
object.__peer = Rubernate::Peer.new unless object.__peer
|
20
|
-
object.primary_key = @dbh
|
21
|
-
|
26
|
+
object.primary_key = @pk_gen.generate @dbh
|
27
|
+
object.__virtual = true
|
22
28
|
object.__peer.dirty = true
|
23
29
|
object.primary_key
|
24
30
|
end
|
25
|
-
|
31
|
+
|
26
32
|
# Issues 'COMMIT' against database.
|
27
33
|
def close
|
28
34
|
dbh.do 'COMMIT'
|
29
|
-
|
35
|
+
@pk_gen.release
|
36
|
+
end
|
37
|
+
|
38
|
+
def failed
|
39
|
+
@pk_gen.release
|
30
40
|
end
|
31
41
|
end
|
32
42
|
end
|
@@ -30,7 +30,7 @@ module DBI
|
|
30
30
|
CREATE INDEX R_P_PK_NAME ON R_PARAMS (OBJECT_PK, NAME)
|
31
31
|
}.gsub(INDENT, '')
|
32
32
|
CREATE_R_PK_SEQUENCE = %q{
|
33
|
-
CREATE SEQUENCE R_PK_SEQUENCE START WITH 1001 INCREMENT BY
|
33
|
+
CREATE SEQUENCE R_PK_SEQUENCE START WITH 1001 INCREMENT BY 25
|
34
34
|
}.gsub(INDENT, '')
|
35
35
|
|
36
36
|
TEMPLATE = %q{
|
data/lib/rubernate/peer.rb
CHANGED
@@ -1,10 +1,25 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
1
3
|
module Rubernate
|
2
4
|
# Represent objects peer - properties holder.
|
3
5
|
class Peer < Hash
|
6
|
+
# Retrus change track of attribute modifications
|
7
|
+
# Hash {p_name => :inserted, :}
|
8
|
+
def change_track
|
9
|
+
@change_track ||= Hash.new
|
10
|
+
each do |name, value|
|
11
|
+
if value.is_a? Container and not @change_track.has_key? name
|
12
|
+
@change_track[name] = :modified if value.recall
|
13
|
+
end
|
14
|
+
end
|
15
|
+
@change_track
|
16
|
+
end
|
17
|
+
|
4
18
|
# Sets peers dirty flag to specified value.
|
5
19
|
# And resets dirty flag for all properites which stored in this peer.
|
6
20
|
def dirty= f_dirty
|
7
21
|
@dirty = f_dirty
|
22
|
+
@change_track = nil unless f_dirty
|
8
23
|
for value in values
|
9
24
|
value.memorize! if value.is_a? Container
|
10
25
|
end
|
@@ -13,7 +28,7 @@ class Peer < Hash
|
|
13
28
|
# If peers dirty flag set to true returns it immediately
|
14
29
|
# else iterate over all properties and returns true if dirty
|
15
30
|
# property apperas. +on_modify+ callback invoked if dirty is +true+.
|
16
|
-
def dirty?
|
31
|
+
def dirty?
|
17
32
|
return true if @dirty
|
18
33
|
f_dirty, name, value, old_value = false
|
19
34
|
each do |name, value|
|
@@ -21,6 +36,7 @@ class Peer < Hash
|
|
21
36
|
old_value = value.recall
|
22
37
|
if old_value
|
23
38
|
f_dirty = dirty = true
|
39
|
+
update_track name, :modified
|
24
40
|
break
|
25
41
|
end
|
26
42
|
end
|
@@ -47,6 +63,7 @@ class Peer < Hash
|
|
47
63
|
else
|
48
64
|
delete key
|
49
65
|
end
|
66
|
+
update_track key, change_type(old_value, value)
|
50
67
|
yield key, old_value, value if block_given?
|
51
68
|
end
|
52
69
|
|
@@ -66,5 +83,17 @@ class Peer < Hash
|
|
66
83
|
return self != @copy ? @copy : nil
|
67
84
|
end
|
68
85
|
end
|
86
|
+
|
87
|
+
private
|
88
|
+
def change_type old_value, new_value
|
89
|
+
return :inserted if old_value.nil?
|
90
|
+
return :inserted if old_value.is_a? Persistent and old_value.removed?
|
91
|
+
return :modified
|
92
|
+
end
|
93
|
+
|
94
|
+
def update_track key, type
|
95
|
+
@change_track ||= Hash.new
|
96
|
+
@change_track[key] = type unless @change_track.has_key? key
|
97
|
+
end
|
69
98
|
end
|
70
99
|
end
|
data/lib/rubernate/persistent.rb
CHANGED
@@ -48,12 +48,22 @@ module Persistent
|
|
48
48
|
@__peer = value
|
49
49
|
end
|
50
50
|
end
|
51
|
+
|
52
|
+
# This property is implementation internal and should not be used by users.
|
53
|
+
attr_writer :__virtual # :nodoc:
|
54
|
+
|
55
|
+
# This property indicates that object is virtual i.e. not stored in database.
|
56
|
+
def __virtual? # :nodoc:
|
57
|
+
@__virtual
|
58
|
+
end
|
51
59
|
|
52
60
|
# Defines metod == in for rubernated classes
|
53
61
|
def self.included klass
|
54
62
|
klass.module_eval %{
|
55
63
|
def == other
|
56
|
-
return
|
64
|
+
return false unless other.is_a? Persistent
|
65
|
+
return false if self.removed? or other.removed?
|
66
|
+
return self.primary_key == other.primary_key
|
57
67
|
super
|
58
68
|
end
|
59
69
|
}
|
data/lib/rubernate/queries.rb
CHANGED
@@ -334,8 +334,7 @@ module Queries
|
|
334
334
|
@pk, @klass = @factory.field(self, 'object_pk'), @factory.field(self, 'object_class')
|
335
335
|
end
|
336
336
|
|
337
|
-
|
338
|
-
attr_reader :pk, :klass, :to_sql
|
337
|
+
attr_reader :pk, :klass, :to_sql
|
339
338
|
|
340
339
|
# Creates subclasses constraint
|
341
340
|
def derived klass
|
@@ -12,8 +12,8 @@ module DBI::GenericTests
|
|
12
12
|
include DBI
|
13
13
|
include FixtureClasses
|
14
14
|
|
15
|
-
def init rt_class, db_url, db_user, db_password
|
16
|
-
@factory = Configuration.new rt_class, nil, db_url, db_user, db_password
|
15
|
+
def init rt_class, gen, db_url, db_user, db_password
|
16
|
+
@factory = Configuration.new rt_class, nil, gen, db_url, db_user, db_password
|
17
17
|
@factory.connect {|dbh|
|
18
18
|
dbh.do 'delete from r_params'
|
19
19
|
dbh.do 'delete from r_objects'
|
@@ -44,10 +44,12 @@ module DBI::GenericTests
|
|
44
44
|
o = C1.new
|
45
45
|
o.__peer = Peer.new
|
46
46
|
@runtime.create o
|
47
|
+
@runtime.save o
|
48
|
+
|
47
49
|
rows = @runtime.dbh.select_all 'select * from r_objects'
|
48
50
|
assert_equal 1, rows.size
|
49
51
|
assert_equal C1.name, rows[0][1]
|
50
|
-
assert o.__peer.dirty?
|
52
|
+
assert !o.__peer.dirty?
|
51
53
|
end
|
52
54
|
|
53
55
|
def test_save_objects
|
@@ -89,6 +91,7 @@ module DBI::GenericTests
|
|
89
91
|
@runtime.create o
|
90
92
|
@runtime.save o
|
91
93
|
rows = @runtime.dbh.select_all 'select * from r_params'
|
94
|
+
|
92
95
|
assert_equal 1, rows.size
|
93
96
|
assert_equal o.primary_key, rows[0][P_PK].to_i
|
94
97
|
assert_equal PARAM_FLAG_INT, rows[0][P_FLAGS].to_i
|
@@ -122,6 +125,7 @@ module DBI::GenericTests
|
|
122
125
|
for i in 1..10
|
123
126
|
hash[i] = C2.new
|
124
127
|
@runtime.create hash[i]
|
128
|
+
@runtime.save hash[i]
|
125
129
|
end
|
126
130
|
o, o.p1 = C1.new, hash
|
127
131
|
|
@@ -145,9 +149,11 @@ module DBI::GenericTests
|
|
145
149
|
hash = {777 => C2.new}
|
146
150
|
|
147
151
|
@runtime.create hash[777]
|
152
|
+
@runtime.save hash[777]
|
148
153
|
|
149
154
|
hash['key'] = C2.new
|
150
155
|
@runtime.create hash['key']
|
156
|
+
@runtime.save hash['key']
|
151
157
|
|
152
158
|
o, o.p1 = C1.new, hash
|
153
159
|
|
@@ -183,6 +189,7 @@ module DBI::GenericTests
|
|
183
189
|
for i in 0..9
|
184
190
|
o = C2.new
|
185
191
|
@runtime.create o
|
192
|
+
@runtime.save o
|
186
193
|
array[i] = o
|
187
194
|
end
|
188
195
|
o, o.p1 = C1.new, array
|
@@ -211,8 +218,7 @@ module DBI::GenericTests
|
|
211
218
|
@runtime.create o2
|
212
219
|
|
213
220
|
o1.p1 = o2
|
214
|
-
@runtime.save o1
|
215
|
-
@runtime.save o2
|
221
|
+
@runtime.save [o1, o2]
|
216
222
|
|
217
223
|
assert 1, @runtime.dbh.select_all(
|
218
224
|
'select * from r_objects where object_pk = ?', o1.primary_key).size
|
@@ -280,7 +286,7 @@ module DBI::GenericTests
|
|
280
286
|
end
|
281
287
|
|
282
288
|
def test_param_type_date
|
283
|
-
pk, date = nil, Date.today
|
289
|
+
pk, date = nil, Date.today
|
284
290
|
Rubernate.session {o = C1.new.attach; o.p1 = date; pk = o.primary_key}
|
285
291
|
Rubernate.session {
|
286
292
|
o = find_by_pk pk
|
@@ -419,8 +425,8 @@ module GenericRuntimeTests
|
|
419
425
|
include Queries
|
420
426
|
include FixtureClasses
|
421
427
|
|
422
|
-
def init rt_class, db_url, db_user, db_password
|
423
|
-
@factory = Configuration.new rt_class, nil, db_url, db_user, db_password
|
428
|
+
def init rt_class, gen, db_url, db_user, db_password
|
429
|
+
@factory = Configuration.new rt_class, nil, gen, db_url, db_user, db_password
|
424
430
|
@factory.connect {|dbh|
|
425
431
|
dbh.do 'delete from r_params'
|
426
432
|
dbh.do 'delete from r_objects'
|
@@ -617,17 +623,76 @@ module GenericRuntimeTests
|
|
617
623
|
def test_remove_referred
|
618
624
|
pk1, pk2 = nil
|
619
625
|
Rubernate.session do
|
620
|
-
o1, o2 =
|
626
|
+
o1, o2 = C2.new.attach, C1.new.attach
|
621
627
|
pk1, pk2 = o1.primary_key, o2.primary_key
|
622
|
-
o1.p1 = o2
|
628
|
+
o1.p1, o1.p2 = o2, 'initial'
|
623
629
|
end
|
624
630
|
Rubernate.session do
|
625
631
|
o1 = find_by_pk pk1
|
626
632
|
o2 = find_by_pk pk2
|
633
|
+
assert_equal 'initial', o1.p2
|
627
634
|
assert_equal o2, o1.p1
|
635
|
+
o1.p2 = 'updated'
|
628
636
|
o2.remove!
|
629
637
|
assert o1.p1.removed?
|
630
638
|
end
|
639
|
+
Rubernate.session do
|
640
|
+
o1 = find_by_pk pk1
|
641
|
+
assert_nil o1.p1
|
642
|
+
assert_equal 'updated', o1.p2
|
643
|
+
end
|
644
|
+
end
|
645
|
+
|
646
|
+
def test_insert_removed
|
647
|
+
pk1, pk2 = nil
|
648
|
+
Rubernate.session do
|
649
|
+
o1, o2 = C1.new.attach, C1.new.attach
|
650
|
+
pk1, pk2 = o1.primary_key, o2.primary_key
|
651
|
+
end
|
652
|
+
Rubernate.session do
|
653
|
+
o1 = find_by_pk pk1
|
654
|
+
o2 = find_by_pk pk2
|
655
|
+
o2.remove!
|
656
|
+
o1.p1 = o2
|
657
|
+
end
|
658
|
+
Rubernate.session do
|
659
|
+
o1 = find_by_pk pk1
|
660
|
+
assert_nil o1.p1
|
661
|
+
end
|
662
|
+
end
|
663
|
+
|
664
|
+
def test_override_removed
|
665
|
+
pk1, pk2 = nil
|
666
|
+
Rubernate.session do
|
667
|
+
o1, o2 = C1.new.attach, C1.new.attach
|
668
|
+
pk1, pk2 = o1.primary_key, o2.primary_key
|
669
|
+
o1.p1 = o2
|
670
|
+
end
|
671
|
+
Rubernate.session do
|
672
|
+
o1 = find_by_pk pk1
|
673
|
+
o2 = find_by_pk pk2
|
674
|
+
o2.remove!
|
675
|
+
o1.p1 = o1
|
676
|
+
end
|
677
|
+
Rubernate.session do
|
678
|
+
o1 = find_by_pk pk1
|
679
|
+
assert_equal o1, o1.p1
|
680
|
+
end
|
681
|
+
end
|
682
|
+
|
683
|
+
def test_update_by_removed
|
684
|
+
pk1, pk2 = nil
|
685
|
+
Rubernate.session do
|
686
|
+
o1, o2 = C1.new.attach, C1.new.attach
|
687
|
+
pk1, pk2 = o1.primary_key, o2.primary_key
|
688
|
+
o1.p1 = 'initial'
|
689
|
+
end
|
690
|
+
Rubernate.session do
|
691
|
+
o1 = find_by_pk pk1
|
692
|
+
o2 = find_by_pk pk2
|
693
|
+
o2.remove!
|
694
|
+
o1.p1 = o2
|
695
|
+
end
|
631
696
|
Rubernate.session do
|
632
697
|
o1 = find_by_pk pk1
|
633
698
|
assert_nil o1.p1
|
@@ -750,5 +815,41 @@ module GenericRuntimeTests
|
|
750
815
|
assert o1.p2[2].__peer.is_a?(Peer)
|
751
816
|
end
|
752
817
|
end
|
818
|
+
|
819
|
+
def test_find_virtual
|
820
|
+
Rubernate.session do
|
821
|
+
o = C1.new.attach
|
822
|
+
assert_equal o, find_by_pk(o.primary_key)
|
823
|
+
end
|
824
|
+
|
825
|
+
Rubernate.session do
|
826
|
+
o = C1.new.attach
|
827
|
+
assert_equal o, find_by_query('Select :o; Where o.pk == :pk', :pk => o.primary_key)[0]
|
828
|
+
end
|
829
|
+
end
|
830
|
+
|
831
|
+
def test_remove_virtual
|
832
|
+
pk = nil
|
833
|
+
Rubernate.session do
|
834
|
+
o = C1.new.attach
|
835
|
+
pk = o.primary_key
|
836
|
+
o.remove!
|
837
|
+
end
|
838
|
+
Rubernate.session do
|
839
|
+
assert_raise(ObjectNotFoundException) {find_by_pk pk}
|
840
|
+
end
|
841
|
+
end
|
842
|
+
|
843
|
+
def test_pk_gen
|
844
|
+
return unless PKGenerator.chunk_factory
|
845
|
+
gen = PKGenerator.instance
|
846
|
+
Rubernate.session do
|
847
|
+
pk = gen.generate dbh
|
848
|
+
51.times do
|
849
|
+
old, pk = pk, gen.generate(dbh)
|
850
|
+
assert_equal old + 1, pk
|
851
|
+
end
|
852
|
+
end
|
853
|
+
end
|
753
854
|
end
|
754
855
|
end
|
@@ -7,13 +7,13 @@ require 'rubernate/impl/dbi_generic_stub'
|
|
7
7
|
class Rubernate::DBI::MysqlTest < Test::Unit::TestCase
|
8
8
|
include Rubernate::DBI::GenericTests
|
9
9
|
def setup
|
10
|
-
init MYSQL_RUNTIME_CLASS, MYSQL_DB_URL, MYSQL_DB_USER, MYSQL_DB_PWD
|
10
|
+
init MYSQL_RUNTIME_CLASS, nil, MYSQL_DB_URL, MYSQL_DB_USER, MYSQL_DB_PWD
|
11
11
|
end
|
12
12
|
end
|
13
13
|
|
14
14
|
class Rubernate::DBI::MysqlRuntimeTest < Test::Unit::TestCase
|
15
15
|
include Rubernate::GenericRuntimeTests
|
16
16
|
def setup
|
17
|
-
init MYSQL_RUNTIME_CLASS, MYSQL_DB_URL, MYSQL_DB_USER, MYSQL_DB_PWD
|
17
|
+
init MYSQL_RUNTIME_CLASS, nil, MYSQL_DB_URL, MYSQL_DB_USER, MYSQL_DB_PWD
|
18
18
|
end
|
19
19
|
end
|
@@ -10,7 +10,7 @@ module DBI
|
|
10
10
|
class OracleTest < Test::Unit::TestCase
|
11
11
|
include Rubernate::DBI::GenericTests
|
12
12
|
def setup
|
13
|
-
init ORA_RUNTIME_CLASS, ORA_DB_URL, ORA_DB_USER, ORA_DB_PWD
|
13
|
+
init ORA_RUNTIME_CLASS, OraPKChunkFactory.new, ORA_DB_URL, ORA_DB_USER, ORA_DB_PWD
|
14
14
|
end
|
15
15
|
=begin
|
16
16
|
# This methods test bug appers when we try
|
@@ -37,7 +37,7 @@ module DBI
|
|
37
37
|
class OracleRuntimeTest < Test::Unit::TestCase
|
38
38
|
include Rubernate::GenericRuntimeTests
|
39
39
|
def setup
|
40
|
-
init ORA_RUNTIME_CLASS, ORA_DB_URL, ORA_DB_USER, ORA_DB_PWD
|
40
|
+
init ORA_RUNTIME_CLASS, OraPKChunkFactory.new, ORA_DB_URL, ORA_DB_USER, ORA_DB_PWD
|
41
41
|
end
|
42
42
|
end
|
43
43
|
end
|
@@ -9,14 +9,14 @@ module DBI
|
|
9
9
|
class PgTest < Test::Unit::TestCase
|
10
10
|
include Rubernate::DBI::GenericTests
|
11
11
|
def setup
|
12
|
-
init PG_RUNTIME_CLASS, PG_DB_URL, PG_DB_USER, PG_DB_PWD
|
12
|
+
init PG_RUNTIME_CLASS, PgPKChunkFactory.new, PG_DB_URL, PG_DB_USER, PG_DB_PWD
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
16
16
|
class PgRuntimeTest < Test::Unit::TestCase
|
17
17
|
include Rubernate::GenericRuntimeTests
|
18
18
|
def setup
|
19
|
-
init PG_RUNTIME_CLASS, PG_DB_URL, PG_DB_USER, PG_DB_PWD
|
19
|
+
init PG_RUNTIME_CLASS, PgPKChunkFactory.new, PG_DB_URL, PG_DB_USER, PG_DB_PWD
|
20
20
|
end
|
21
21
|
end
|
22
22
|
end
|
@@ -264,6 +264,23 @@ module Rubernate
|
|
264
264
|
assert_not_nil a.recall
|
265
265
|
assert_equal 4, a[1]
|
266
266
|
end
|
267
|
+
|
268
|
+
def test_change_track
|
269
|
+
peer = Peer.new
|
270
|
+
peer[:p1] = 'new'
|
271
|
+
assert_equal({:p1 => :inserted}, peer.change_track)
|
272
|
+
peer.dirty = false
|
273
|
+
peer[:p2] = ["new_element"]
|
274
|
+
assert_equal({:p2 => :inserted}, peer.change_track)
|
275
|
+
peer.dirty = false
|
276
|
+
peer[:p1] = 'updated'
|
277
|
+
peer[:p2] << 'array_updated'
|
278
|
+
assert_equal({:p1 => :modified, :p2 => :modified}, peer.change_track)
|
279
|
+
peer.dirty = false
|
280
|
+
peer[:p1] = nil
|
281
|
+
peer[:p2] = nil
|
282
|
+
assert_equal({:p1 => :modified, :p2 => :modified}, peer.change_track)
|
283
|
+
end
|
267
284
|
end
|
268
285
|
|
269
286
|
class Rubernate::BaseRuntimeTest < Test::Unit::TestCase
|
metadata
CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.8.11
|
|
3
3
|
specification_version: 1
|
4
4
|
name: Rubernate
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.1.
|
7
|
-
date: 2006-
|
6
|
+
version: 0.1.7
|
7
|
+
date: 2006-05-23 00:00:00 +04:00
|
8
8
|
summary: Object-oriented storage for Ruby objects based on relational database model
|
9
9
|
require_paths:
|
10
10
|
- lib
|
@@ -40,6 +40,7 @@ files:
|
|
40
40
|
- lib/rubernate/queries.rb
|
41
41
|
- lib/rubernate/runtime.rb
|
42
42
|
- lib/rubernate/impl/dbi_generic.rb
|
43
|
+
- lib/rubernate/impl/dbi_genpk.rb
|
43
44
|
- lib/rubernate/impl/dbi_mysql.rb
|
44
45
|
- lib/rubernate/impl/dbi_oracle.rb
|
45
46
|
- lib/rubernate/impl/dbi_pg.rb
|