Rubernate 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/db/mysql.sql +1 -0
- data/db/oracle.sql +1 -0
- data/lib/rubernate/callbacks.rb +2 -2
- data/lib/rubernate/entity.rb +3 -3
- data/lib/rubernate/impl/dbigeneric.rb +81 -75
- data/lib/rubernate/impl/memory.rb +3 -2
- data/lib/rubernate/mixins.rb +1 -1
- data/lib/rubernate/peer.rb +2 -2
- data/lib/rubernate/queries.rb +21 -20
- data/lib/rubernate/runtime.rb +35 -28
- data/lib/rubernate.rb +8 -1
- data/tests/config.rb +2 -2
- data/tests/rubernate/fixtures.rb +4 -1
- data/tests/rubernate/impl/dbigeneric_stub.rb +54 -6
- data/tests/rubernate/rubernate_test.rb +12 -7
- metadata +2 -2
data/db/mysql.sql
CHANGED
data/db/oracle.sql
CHANGED
data/lib/rubernate/callbacks.rb
CHANGED
@@ -5,7 +5,7 @@ module Rubernate
|
|
5
5
|
# in other to extend basic functionality.
|
6
6
|
module Callbacks
|
7
7
|
# This module contains callback methods and
|
8
|
-
# included in
|
8
|
+
# included in all persisten classes.
|
9
9
|
module Entity
|
10
10
|
# Invoked if object has just been created in database.
|
11
11
|
def on_create
|
@@ -23,7 +23,7 @@ module Callbacks
|
|
23
23
|
def on_load
|
24
24
|
end
|
25
25
|
|
26
|
-
# Invoked if property of object has just been
|
26
|
+
# Invoked if property of object has just been changed via = or [] = operator.
|
27
27
|
def on_change prop_name, old_value, new_value
|
28
28
|
end
|
29
29
|
|
data/lib/rubernate/entity.rb
CHANGED
@@ -6,7 +6,7 @@ module Entity
|
|
6
6
|
include Callbacks::Entity
|
7
7
|
|
8
8
|
# This property holds objects primary key.
|
9
|
-
# The primary key is not
|
9
|
+
# The primary key is not nil for objects attached to session.
|
10
10
|
attr :primary_key, true
|
11
11
|
|
12
12
|
# Attaches object to session returns self
|
@@ -26,13 +26,13 @@ module Entity
|
|
26
26
|
@removed
|
27
27
|
end
|
28
28
|
|
29
|
-
# Retruns objects
|
29
|
+
# Retruns objects peer if ther is one or nil in other case
|
30
30
|
def peer
|
31
31
|
@peer
|
32
32
|
end
|
33
33
|
|
34
34
|
# Sets peer if there is no one and invokes on_load callback else skip.
|
35
|
-
# If value is
|
35
|
+
# If value is nil peer will be lazy initialized in ensure_loaded.
|
36
36
|
# This method should be invoked only once with not nil value.
|
37
37
|
def peer= value
|
38
38
|
return if @peer
|
@@ -5,23 +5,27 @@ module Rubernate
|
|
5
5
|
module DBI
|
6
6
|
# Param flags constants
|
7
7
|
PARAM_FLAG_INT = (1 << 0).to_i
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
8
|
+
PARAM_FLAG_FLOAT = (1 << 1).to_i
|
9
|
+
PARAM_FLAG_STRING = (1 << 2).to_i
|
10
|
+
PARAM_FLAG_TIME = (1 << 3).to_i
|
11
|
+
PARAM_FLAG_DATE = (1 << 4).to_i
|
12
|
+
PARAM_FLAG_REF = (1 << 5).to_i
|
13
|
+
PARAM_FLAG_ARRAY = (1 << 6).to_i
|
14
|
+
PARAM_FLAG_HASH = (1 << 7).to_i
|
14
15
|
|
15
16
|
# Array and Hashs combinations
|
16
17
|
ARRAY_INT_REF = (PARAM_FLAG_ARRAY | PARAM_FLAG_INT).to_i
|
17
18
|
HASH_INT_REF = (PARAM_FLAG_HASH | PARAM_FLAG_INT).to_i
|
19
|
+
HASH_FLOAT_REF = (PARAM_FLAG_HASH | PARAM_FLAG_FLOAT).to_i
|
18
20
|
HASH_STRING_REF = (PARAM_FLAG_HASH | PARAM_FLAG_STRING).to_i
|
19
21
|
HASH_TIME_REF = (PARAM_FLAG_HASH | PARAM_FLAG_TIME).to_i
|
20
22
|
HASH_DATE_REF = (PARAM_FLAG_HASH | PARAM_FLAG_DATE).to_i
|
21
23
|
|
24
|
+
TimeType = ::DBI::Timestamp
|
25
|
+
|
22
26
|
# Represents factory for +Runtime+ implementors.
|
23
27
|
class RuntimeFactory
|
24
|
-
# Accepts
|
28
|
+
# Accepts Runtime impl. class, database url, user name, and user password
|
25
29
|
def initialize klass, db_url, db_user, db_password
|
26
30
|
@klass, @db_url, @db_user, @db_password = klass, db_url, db_user, db_password
|
27
31
|
# @params = {'AutoCommit' => false} #TODO: refine
|
@@ -45,11 +49,11 @@ module DBI
|
|
45
49
|
end
|
46
50
|
|
47
51
|
# Represent base class for all Runtime implementation.
|
48
|
-
# Contains generic sql implementations of all required methods except
|
49
|
-
#
|
52
|
+
# Contains generic sql implementations of all required methods except create.
|
53
|
+
#Method create should be implemented in subclasses.
|
50
54
|
class Runtime < Rubernate::Runtime
|
51
55
|
include DBI
|
52
|
-
|
56
|
+
|
53
57
|
# Parameters are deleted automatically with corresponding r_object records
|
54
58
|
#DELETE_PARAMS = <<-SQL
|
55
59
|
# DELETE FROM R_PARAMS WHERE OBJECT_PK = ?
|
@@ -61,35 +65,34 @@ module DBI
|
|
61
65
|
DELETE FROM R_OBJECTS WHERE OBJECT_PK = ?
|
62
66
|
SQL
|
63
67
|
SAVE_PARAMS = <<-SQL
|
64
|
-
INSERT INTO R_PARAMS VALUES (?, ?, ?, ?, ?, ?, ?)
|
68
|
+
INSERT INTO R_PARAMS VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
65
69
|
SQL
|
66
70
|
SELECT_PARAMS = <<-SQL
|
67
|
-
SELECT P.*,
|
71
|
+
SELECT P.*, R.OBJECT_CLASS, O.OBJECT_CLASS
|
68
72
|
FROM R_PARAMS P JOIN R_OBJECTS O ON (O.OBJECT_PK = P.OBJECT_PK)
|
69
73
|
LEFT OUTER JOIN R_OBJECTS R ON (P.REF_VALUE = R.OBJECT_PK)
|
70
74
|
WHERE O.OBJECT_PK IN
|
71
75
|
SQL
|
72
76
|
SELECT_ONE_OBJECT = <<-SQL
|
73
|
-
SELECT O.OBJECT_PK, P.NAME, P.FLAGS, P.INT_VALUE, P.STR_VALUE,
|
74
|
-
P.DAT_VALUE, P.REF_VALUE,
|
77
|
+
SELECT O.OBJECT_PK, P.NAME, P.FLAGS, P.INT_VALUE, P.FLT_VALUE, P.STR_VALUE,
|
78
|
+
P.DAT_VALUE, P.REF_VALUE, R.OBJECT_CLASS, O.OBJECT_CLASS
|
75
79
|
FROM R_OBJECTS O
|
76
80
|
LEFT OUTER JOIN R_PARAMS P ON (O.OBJECT_PK = P.OBJECT_PK)
|
77
81
|
LEFT OUTER JOIN R_OBJECTS R ON (P.REF_VALUE = R.OBJECT_PK)
|
78
82
|
WHERE O.OBJECT_PK = ?
|
79
83
|
SQL
|
80
|
-
|
81
|
-
# R_OBJECTS table's column's indexes
|
82
|
-
CI_O_PK = 0
|
83
|
-
CI_O_CLASS = 1
|
84
|
-
|
84
|
+
|
85
85
|
# R_PARAMS table's column's indexes
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
86
|
+
P_PK = 0
|
87
|
+
P_NAME = 1
|
88
|
+
P_FLAGS = 2
|
89
|
+
P_INT = 3
|
90
|
+
P_FLT = 4
|
91
|
+
P_STR = 5
|
92
|
+
P_TIME = 6
|
93
|
+
P_REF = 7 # Refered object PK
|
94
|
+
P_REF_CLASS = 8 # Refered object Class
|
95
|
+
P_CLASS = 9 # Self object Class
|
93
96
|
|
94
97
|
attr :dbh, false
|
95
98
|
|
@@ -121,14 +124,14 @@ module DBI
|
|
121
124
|
# Loads object by primary_key
|
122
125
|
def load_by_pk pk
|
123
126
|
klass, peer = nil
|
124
|
-
dbh.execute
|
125
|
-
sth.fetch
|
126
|
-
klass, peer = row[
|
127
|
+
dbh.execute SELECT_ONE_OBJECT, pk do |sth|
|
128
|
+
sth.fetch do |row|
|
129
|
+
klass, peer = row[P_CLASS], Rubernate::Peer.new unless klass
|
127
130
|
fetch_param peer, row
|
128
|
-
|
129
|
-
|
131
|
+
end
|
132
|
+
end
|
130
133
|
return nil unless klass
|
131
|
-
obj = instantiate pk,
|
134
|
+
obj = instantiate pk, klass, peer
|
132
135
|
post_load obj
|
133
136
|
obj
|
134
137
|
end
|
@@ -140,7 +143,7 @@ module DBI
|
|
140
143
|
# selects objects and stores not loaded to buffer
|
141
144
|
dbh.execute(query, *params) { |sth|
|
142
145
|
sth.fetch { |row|
|
143
|
-
obj = instantiate row[0].to_i,
|
146
|
+
obj = instantiate row[0].to_i, row[1]
|
144
147
|
buffer[obj.primary_key], obj.peer = obj, Rubernate::Peer.new unless obj.peer
|
145
148
|
result << obj
|
146
149
|
}
|
@@ -148,8 +151,8 @@ module DBI
|
|
148
151
|
return result if buffer.empty?
|
149
152
|
# load peers for objects in buffer.
|
150
153
|
dbh.execute(SELECT_PARAMS + object_ids_p(buffer.values)) {|sth|
|
151
|
-
sth.fetch {|row|
|
152
|
-
fetch_param buffer[row[
|
154
|
+
sth.fetch {|row|
|
155
|
+
fetch_param buffer[row[P_PK].to_i].peer, row
|
153
156
|
}
|
154
157
|
}
|
155
158
|
# clears dirty flags
|
@@ -182,70 +185,72 @@ module DBI
|
|
182
185
|
when Hash: save_hash sth, pk, name, param
|
183
186
|
when Array: save_array sth, pk, name, param
|
184
187
|
when Integer: save_int sth, pk, name, param
|
188
|
+
when Float: save_float sth, pk, name, param
|
185
189
|
when Time: save_time sth, pk, name, param
|
186
190
|
when Date: save_date sth, pk, name, param
|
187
|
-
else save_str sth, pk, name, param
|
191
|
+
else save_str sth, pk, name, param.to_s
|
188
192
|
end
|
189
193
|
end
|
190
|
-
|
191
|
-
def
|
192
|
-
sth.execute pk, name,
|
194
|
+
|
195
|
+
def save_int sth, pk, name, int
|
196
|
+
sth.execute pk, name, PARAM_FLAG_INT, int, nil, nil, nil, nil
|
193
197
|
end
|
198
|
+
|
199
|
+
def save_float sth, pk, name, float
|
200
|
+
sth.execute pk, name, PARAM_FLAG_FLOAT, nil, float, nil, nil, nil
|
201
|
+
end
|
202
|
+
|
203
|
+
def save_str sth, pk, name, str
|
204
|
+
sth.execute pk, name, PARAM_FLAG_STRING, nil, nil, str, nil, nil
|
205
|
+
end
|
194
206
|
|
195
207
|
def save_time sth, pk, name, time
|
196
|
-
sth.execute pk, name, PARAM_FLAG_TIME, nil, nil,
|
208
|
+
sth.execute pk, name, PARAM_FLAG_TIME, nil, nil, nil, TimeType.new(time), nil
|
197
209
|
end
|
198
210
|
|
199
211
|
def save_date sth, pk, name, date
|
200
|
-
sth.execute pk, name, PARAM_FLAG_DATE, nil, nil,
|
212
|
+
sth.execute pk, name, PARAM_FLAG_DATE, nil, nil, nil, TimeType.new(date), nil
|
201
213
|
end
|
214
|
+
|
215
|
+
def save_ref sth, pk, name, ref
|
216
|
+
sth.execute pk, name, PARAM_FLAG_REF, nil, nil, nil, nil, ref.primary_key unless ref.removed?
|
217
|
+
end
|
202
218
|
|
203
219
|
def save_hash sth, pk, name, hash
|
204
|
-
hash.delete_if {|key, ref| ref.removed?}
|
220
|
+
hash.delete_if {|key, ref| ref.removed?} # Discard removed objects
|
205
221
|
if hash.empty?
|
206
|
-
sth.execute pk, name, PARAM_FLAG_HASH, 0, nil, nil, nil
|
222
|
+
sth.execute pk, name, PARAM_FLAG_HASH, 0, nil, nil, nil, nil
|
207
223
|
else
|
208
224
|
hash.each {|key, ref| save_key_value sth, pk, name, key, ref.primary_key}
|
209
225
|
end
|
210
226
|
end
|
211
227
|
|
212
228
|
def save_key_value sth, pk, name, key, ref
|
213
|
-
|
214
|
-
sth.execute pk, name,
|
215
|
-
|
216
|
-
sth.execute pk, name,
|
217
|
-
|
218
|
-
sth.execute pk, name, HASH_TIME_REF, nil, nil,
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
sth.execute pk, name, PARAM_FLAG_HASH, nil, nil, nil, ref
|
223
|
-
else
|
224
|
-
raise "invalid hash key value #{key}"
|
229
|
+
case key
|
230
|
+
when Integer: sth.execute pk, name, HASH_INT_REF, key, nil, nil, nil, ref
|
231
|
+
when Float: sth.execute pk, name, HASH_FLOAT_REF, nil, key, nil, nil, ref
|
232
|
+
when String: sth.execute pk, name, HASH_STRING_REF, nil, nil, key, nil, ref
|
233
|
+
when ::Date: sth.execute pk, name, HASH_DATE_REF, nil, nil, nil, TimeType.new(key), ref
|
234
|
+
when ::Time: sth.execute pk, name, HASH_TIME_REF, nil, nil, nil, TimeType.new(key), ref
|
235
|
+
when nil: sth.execute pk, name, PARAM_FLAG_HASH, nil, nil, nil, nil, ref
|
236
|
+
else
|
237
|
+
raise "invalid hash key value #{key}"
|
225
238
|
end
|
226
239
|
end
|
227
240
|
|
228
241
|
def save_array sth, pk, name, array
|
229
|
-
array.delete_if {|ref| !ref or ref.removed?} #
|
242
|
+
array.delete_if {|ref| !ref or ref.removed?} # Discard removed and nil items
|
230
243
|
if array.empty? # Empty arrays should also be stored
|
231
|
-
sth.execute pk, name, ARRAY_INT_REF, 0, nil, nil, nil
|
244
|
+
sth.execute pk, name, ARRAY_INT_REF, 0, nil, nil, nil, nil
|
232
245
|
else
|
233
|
-
array.each_index {
|
234
|
-
ARRAY_INT_REF, idx, nil, nil, array[idx].primary_key}
|
246
|
+
array.each_index {|idx| sth.execute pk, name,
|
247
|
+
ARRAY_INT_REF, idx, nil, nil, nil, array[idx].primary_key}
|
235
248
|
end
|
236
249
|
end
|
237
250
|
|
238
|
-
def save_int sth, pk, name, param
|
239
|
-
sth.execute pk, name, PARAM_FLAG_INT, param, nil, nil, nil
|
240
|
-
end
|
241
|
-
|
242
|
-
def save_str sth, pk, name, param
|
243
|
-
sth.execute pk, name, PARAM_FLAG_STRING, nil, param.to_s, nil, nil
|
244
|
-
end
|
245
|
-
|
246
251
|
def fetch_param peer, row
|
247
|
-
return unless row[
|
248
|
-
name, flags, ref = row[
|
252
|
+
return unless row[P_NAME] # there are no params for this object
|
253
|
+
name, flags, ref = row[P_NAME].to_sym, row[P_FLAGS].to_i
|
249
254
|
if flags & PARAM_FLAG_ARRAY != 0
|
250
255
|
peer[name] = [] unless peer[name]
|
251
256
|
peer[name][fetch_value(row, flags)] = ref if ref = fetch_ref_value(row)
|
@@ -258,17 +263,18 @@ module DBI
|
|
258
263
|
end
|
259
264
|
|
260
265
|
def fetch_value row, flags
|
261
|
-
return row[
|
262
|
-
return row[
|
263
|
-
return
|
264
|
-
return row[
|
265
|
-
return row[
|
266
|
+
return row[P_INT].to_i if flags & PARAM_FLAG_INT != 0
|
267
|
+
return row[P_FLT].to_f if flags & PARAM_FLAG_FLOAT != 0
|
268
|
+
return row[P_STR].to_s if flags & PARAM_FLAG_STRING != 0
|
269
|
+
return instantiate(row[P_REF].to_i, row[P_REF_CLASS]) if flags & PARAM_FLAG_REF != 0
|
270
|
+
return row[P_TIME].to_time if flags & PARAM_FLAG_TIME != 0
|
271
|
+
return row[P_TIME].to_date if flags & PARAM_FLAG_DATE != 0
|
266
272
|
return nil
|
267
273
|
end
|
268
274
|
|
269
275
|
def fetch_ref_value row
|
270
276
|
ref_pk = nil
|
271
|
-
instantiate ref_pk,
|
277
|
+
instantiate ref_pk, row[P_REF_CLASS] if ref_pk = row[P_REF]
|
272
278
|
end
|
273
279
|
|
274
280
|
def object_ids objects
|
@@ -32,6 +32,7 @@ module Memory
|
|
32
32
|
end
|
33
33
|
|
34
34
|
class RuntimeFactory
|
35
|
+
attr_accessor :database
|
35
36
|
def initialize
|
36
37
|
@pk, @database = 1001, {}
|
37
38
|
end
|
@@ -44,9 +45,9 @@ module Memory
|
|
44
45
|
end
|
45
46
|
|
46
47
|
class Runtime < Rubernate::Runtime
|
47
|
-
|
48
|
+
attr_accessor :pk, :database
|
48
49
|
|
49
|
-
# Database is maps {pk => [class, {
|
50
|
+
# Database is maps {pk => [class, {attr => value}]}
|
50
51
|
def initialize database, pk_seq
|
51
52
|
super()
|
52
53
|
@pk_seq, @database, @factory = pk_seq, database, Queries::Factory.new
|
data/lib/rubernate/mixins.rb
CHANGED
@@ -52,7 +52,7 @@ class Class
|
|
52
52
|
# Returns subclasses names (self is not included)
|
53
53
|
attr :subclasses
|
54
54
|
|
55
|
-
# Register
|
55
|
+
# Register klass as descendant in all superclasses
|
56
56
|
def update_superclass klass
|
57
57
|
@subclasses ||= []
|
58
58
|
@subclasses << klass.name if klass != self
|
data/lib/rubernate/peer.rb
CHANGED
@@ -50,8 +50,8 @@ class Peer < Hash
|
|
50
50
|
yield key, old_value, value if block_given?
|
51
51
|
end
|
52
52
|
|
53
|
-
# This module
|
54
|
-
# objects. It adds
|
53
|
+
# This module for including to collections of Persistent
|
54
|
+
# objects. It adds field that contains copy of collection
|
55
55
|
# and controls dirty status of collection.
|
56
56
|
module Container
|
57
57
|
attr_writer :copy
|
data/lib/rubernate/queries.rb
CHANGED
@@ -9,37 +9,37 @@ module Queries
|
|
9
9
|
|
10
10
|
# Contains operations definitions
|
11
11
|
module Operations
|
12
|
-
# Declares
|
12
|
+
# Declares And sql clause.
|
13
13
|
def And expr1, expr2
|
14
14
|
@factory.bin_op expr1, expr2, 'and'
|
15
15
|
end
|
16
16
|
|
17
|
-
# Declares
|
17
|
+
# Declares Or sql clause.
|
18
18
|
def Or expr1, expr2
|
19
19
|
@factory.bin_op expr1, expr2, 'or', true
|
20
20
|
end
|
21
21
|
|
22
|
-
# Declares
|
22
|
+
# Declares = sql clause.
|
23
23
|
def Eq expr1, expr2
|
24
24
|
@factory.bin_op expr1, expr2, '='
|
25
25
|
end
|
26
26
|
|
27
|
-
# Declares
|
27
|
+
# Declares Not sql clause.
|
28
28
|
def Not expr
|
29
29
|
@factory.un_op expr, 'not', true
|
30
30
|
end
|
31
31
|
|
32
|
-
# Declares
|
32
|
+
# Declares is null sql clause
|
33
33
|
def IsNil expr
|
34
34
|
@factory.bin_op expr, @factory.expr(nil), 'is'
|
35
35
|
end
|
36
36
|
|
37
|
-
# Declares
|
37
|
+
# Declares is not null sql clause
|
38
38
|
def IsNotNil expr
|
39
39
|
@factory.bin_op expr, @factory.expr(nil), 'is not'
|
40
40
|
end
|
41
41
|
|
42
|
-
# Declares
|
42
|
+
# Declares in sql clause
|
43
43
|
def In expr, list
|
44
44
|
@factory.bin_op expr, @factory.list(list), 'in'
|
45
45
|
end
|
@@ -55,7 +55,7 @@ module Queries
|
|
55
55
|
end
|
56
56
|
|
57
57
|
# Defines query elements factory. By default it creates elements defined
|
58
|
-
# in module
|
58
|
+
# in module Generic, but this befavior can be changed by seting of appropriate class.
|
59
59
|
# Following example changes implementation of BinOpConst
|
60
60
|
#
|
61
61
|
# :call-seq:
|
@@ -82,7 +82,7 @@ module Queries
|
|
82
82
|
end
|
83
83
|
private
|
84
84
|
# Defines factory method that will instantiate element instance
|
85
|
-
# and pass
|
85
|
+
# and pass self as element factory.
|
86
86
|
def self.def_factory name
|
87
87
|
module_eval %{
|
88
88
|
attr_writer :#{name}
|
@@ -212,7 +212,7 @@ module Queries
|
|
212
212
|
end
|
213
213
|
end
|
214
214
|
|
215
|
-
# Represent
|
215
|
+
# Represent r_params for hashes and arrays constrained by key
|
216
216
|
class KeyRefExpr < BinOpConstr
|
217
217
|
def initialize factory, r_param, key_field, key_value
|
218
218
|
@factory, @r_param, @key_field, @key_value = factory, r_param, key_field, key_value
|
@@ -258,19 +258,20 @@ module Queries
|
|
258
258
|
# Fields accessors.
|
259
259
|
def pk () f_expr 'object_pk'; end
|
260
260
|
def int () f_expr 'int_value'; end
|
261
|
+
def float() f_expr 'flt_value'; end
|
261
262
|
def str () f_expr 'str_value'; end
|
262
263
|
def time () f_expr 'dat_value'; end
|
263
|
-
def date () f_expr 'dat_value'; end
|
264
|
+
def date () f_expr 'dat_value'; end
|
264
265
|
def ref () f_expr 'ref_value'; end
|
265
266
|
def flags() f_expr 'flags'; end
|
266
267
|
|
267
|
-
# The following methods checks
|
268
|
+
# The following methods checks r_param.flags value. (r_param.flags)
|
268
269
|
def is_int () Eq flags, PARAM_FLAG_INT; end
|
269
270
|
def is_str () Eq flags, PARAM_FLAG_STRING; end
|
270
271
|
def is_time() Eq flags, PARAM_FLAG_TIME; end
|
271
272
|
def is_ref () Eq flags, PARAM_FLAG_REF; end
|
272
273
|
|
273
|
-
# Shortcut for
|
274
|
+
# Shortcut for Eq method
|
274
275
|
def == expr
|
275
276
|
expr.is_a?(RObject) ?
|
276
277
|
@factory.bin_op(ref, expr.pk, '=') :
|
@@ -288,7 +289,7 @@ module Queries
|
|
288
289
|
end
|
289
290
|
end
|
290
291
|
private
|
291
|
-
# Creates
|
292
|
+
# Creates FieldExpr witch field of this (r_param) table
|
292
293
|
def f_expr field
|
293
294
|
@factory.field self, field
|
294
295
|
end
|
@@ -368,7 +369,7 @@ module Queries
|
|
368
369
|
@exprs.inject([]){|result, expr| result.concat expr.markers}
|
369
370
|
end
|
370
371
|
|
371
|
-
# Arranges map:
|
372
|
+
# Arranges map: values {marker=>value} to ordered array of values
|
372
373
|
# accroding to markers in query.
|
373
374
|
def params values
|
374
375
|
markers.inject([]){|r, m| r << values[m]}
|
@@ -403,18 +404,18 @@ module Queries
|
|
403
404
|
@evaluated = true
|
404
405
|
end
|
405
406
|
|
406
|
-
# Generates
|
407
|
+
# Generates where clause for each expression joined by and clause
|
407
408
|
def where_sql
|
408
409
|
r_params.collect{|rp| rp.to_sql + '.name = \'' + rp.name + '\''}.concat(
|
409
410
|
@exprs.collect{|ex| ex.to_sql}).join(" and\n\t\t")
|
410
411
|
end
|
411
412
|
|
412
|
-
# Generates
|
413
|
+
# Generates order by sql clause
|
413
414
|
def order_by_sql
|
414
415
|
@order.collect{|ord| ord.to_sql}.join(', ')
|
415
416
|
end
|
416
417
|
|
417
|
-
# Generates left outer join for each
|
418
|
+
# Generates left outer join for each r_params used in query
|
418
419
|
def tables_sql
|
419
420
|
r_objects.collect{|r_object| 'r_objects ' + r_object.to_sql}.join(', ') +
|
420
421
|
r_params.collect{|r_param|
|
@@ -423,7 +424,7 @@ module Queries
|
|
423
424
|
}.join()
|
424
425
|
end
|
425
426
|
|
426
|
-
# Retruns
|
427
|
+
# Retruns r_params used in query. List of RParam.
|
427
428
|
def r_params
|
428
429
|
res = @exprs.inject([]) {|res, exp| res.concat exp.r_params}
|
429
430
|
@order.inject(res) {|res, ord| res.concat ord.r_params}
|
@@ -431,7 +432,7 @@ module Queries
|
|
431
432
|
res
|
432
433
|
end
|
433
434
|
|
434
|
-
# Retruns
|
435
|
+
# Retruns r_objects used in query. List of RObject.
|
435
436
|
def r_objects
|
436
437
|
res = (@r_objects.values + r_params.inject([]){|res, rp| res << rp.r_object})
|
437
438
|
res.uniq!
|
data/lib/rubernate/runtime.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
module Rubernate
|
2
|
-
# Base class for all
|
3
|
-
# Most of these methods should be overriden by subclasses.
|
2
|
+
# Base class for all Runtime implementations.
|
4
3
|
class Runtime
|
5
4
|
include Callbacks::Runtime
|
6
5
|
|
@@ -116,20 +115,32 @@ module Rubernate
|
|
116
115
|
end
|
117
116
|
|
118
117
|
private
|
119
|
-
# Creates new object instance or returns
|
118
|
+
# Creates new object instance or returns insance from pool
|
119
|
+
# Parameter klass can be Class or String (class name)
|
120
120
|
def instantiate pk, klass, peer = nil
|
121
121
|
object = @pool[pk]
|
122
|
-
if object and object.class != klass
|
123
|
-
raise "invalid class #{klass} of object pk: #{pk}: #{object.class}"
|
124
|
-
end
|
125
122
|
unless object
|
126
|
-
|
123
|
+
clazz = klass.is_a?(Class) ? klass : get_class(klass)
|
124
|
+
object = clazz ? clazz.allocate : FakeEntity.new(klass)
|
125
|
+
object.primary_key = pk
|
127
126
|
@pool[pk] = object
|
128
127
|
end
|
129
|
-
object.peer = peer
|
128
|
+
object.peer = peer # TODO: TEST!!! and write description
|
130
129
|
object
|
131
130
|
end
|
132
|
-
|
131
|
+
|
132
|
+
#Classes cache.
|
133
|
+
@@classes = {}
|
134
|
+
|
135
|
+
# Returns class with specified name or nil if there is no one.
|
136
|
+
def get_class name
|
137
|
+
klass = @@classes[name]
|
138
|
+
return klass if klass
|
139
|
+
klass = Module.find_class name
|
140
|
+
return nil unless klass
|
141
|
+
@@classes[name] = klass
|
142
|
+
end
|
143
|
+
|
133
144
|
# Returns modified objects.
|
134
145
|
def modified
|
135
146
|
@pool.values.find_all {|object| object.peer and object.dirty?}
|
@@ -192,24 +203,20 @@ module Rubernate
|
|
192
203
|
when Class: p.name
|
193
204
|
else p
|
194
205
|
end
|
195
|
-
end
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
klass = Module.find_class name
|
211
|
-
@@classes[name] = klass if klass
|
212
|
-
klass
|
213
|
-
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
# Represetns fake entity. - objects whose class wasn't found during loading.
|
210
|
+
class FakeEntity
|
211
|
+
persistent
|
212
|
+
def initialize class_name
|
213
|
+
@class_name = class_name
|
214
|
+
end
|
215
|
+
def method_missing name, *args
|
216
|
+
raise ObjectLoadError.new("class: #{@class_name} not found for object: #{primary_key}")
|
217
|
+
end
|
218
|
+
def to_s
|
219
|
+
"Fake Entity: #{primaty_key}, for class: #{@class_name}"
|
220
|
+
end
|
214
221
|
end
|
215
222
|
end
|
data/lib/rubernate.rb
CHANGED
@@ -35,6 +35,9 @@ require 'rubernate/impl/dbimysql'
|
|
35
35
|
require 'rubernate/impl/dbioracle'
|
36
36
|
|
37
37
|
|
38
|
+
|
39
|
+
# Contains most useful methods. This module is included
|
40
|
+
# in all persistent classes.
|
38
41
|
module Rubernate
|
39
42
|
# Common Rubernate Log instance
|
40
43
|
Log = Log4r::Logger.new self.name
|
@@ -42,7 +45,7 @@ module Rubernate
|
|
42
45
|
# Rubernate core singelton methods.
|
43
46
|
class << self
|
44
47
|
# Allows you setup Rubernate by yours implementaion of RuntimeFactory.
|
45
|
-
# See also method
|
48
|
+
# See also method config
|
46
49
|
def runtime_factory= factory
|
47
50
|
Log.info "Rubernate configured by #{factory}"
|
48
51
|
@runtime_factory = factory
|
@@ -124,4 +127,8 @@ module Rubernate
|
|
124
127
|
@msg
|
125
128
|
end
|
126
129
|
end
|
130
|
+
|
131
|
+
# Signals about errors occured during object loading.
|
132
|
+
class ObjectLoadError < RuntimeError
|
133
|
+
end
|
127
134
|
end
|
data/tests/config.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
# Set to true to run tests or Oracle
|
2
|
-
$run_oracle_tests =
|
2
|
+
$run_oracle_tests = false
|
3
3
|
|
4
4
|
ORA_RUNTIME_CLASS = Rubernate::DBI::OracleRuntime
|
5
5
|
ORA_DB_URL = 'dbi:OCI8:dbg91'
|
@@ -7,7 +7,7 @@ ORA_DB_USER = 'netcracker65'
|
|
7
7
|
ORA_DB_PWD = 'netcracker65'
|
8
8
|
|
9
9
|
# Set to true to run tests on MySQL
|
10
|
-
$run_mysql_tests =
|
10
|
+
$run_mysql_tests = false
|
11
11
|
|
12
12
|
MYSQL_RUNTIME_CLASS = Rubernate::DBI::MySqlRuntime
|
13
13
|
MYSQL_DB_URL = 'dbi:Mysql:rubernate_db:localhost'
|
data/tests/rubernate/fixtures.rb
CHANGED
@@ -130,6 +130,7 @@ module DBI::GenericTests
|
|
130
130
|
assert_equal o.primary_key, row['OBJECT_PK'].to_i
|
131
131
|
assert_equal PARAM_FLAG_INT | PARAM_FLAG_HASH, row['FLAGS'].to_i
|
132
132
|
assert hash.has_key?(row['INT_VALUE'].to_i)
|
133
|
+
assert_nil row['FLT_VALUE']
|
133
134
|
assert_nil row['STR_VALUE']
|
134
135
|
assert_nil row['DAT_VALUE']
|
135
136
|
assert_equal hash[row['INT_VALUE'].to_i].primary_key, row['REF_VALUE'].to_i
|
@@ -156,17 +157,19 @@ module DBI::GenericTests
|
|
156
157
|
if row['FLAGS'].to_i == HASH_INT_REF
|
157
158
|
assert_equal o.primary_key, row['OBJECT_PK'].to_i
|
158
159
|
assert hash.has_key?(row['INT_VALUE'].to_i)
|
160
|
+
assert_nil row['FLT_VALUE']
|
159
161
|
assert_nil row['STR_VALUE']
|
160
162
|
assert_nil row['DAT_VALUE']
|
161
163
|
assert_equal hash[row['INT_VALUE'].to_i].primary_key, row['REF_VALUE'].to_i
|
162
164
|
elsif row['FLAGS'].to_i == HASH_STRING_REF
|
163
165
|
assert_equal o.primary_key, row['OBJECT_PK'].to_i
|
164
166
|
assert_nil row['INT_VALUE']
|
167
|
+
assert_nil row['FLT_VALUE']
|
165
168
|
assert hash.has_key?(row['STR_VALUE'])
|
166
169
|
assert_nil row['DAT_VALUE']
|
167
170
|
assert_equal hash[row['STR_VALUE']].primary_key, row['REF_VALUE'].to_i
|
168
171
|
else
|
169
|
-
flunk 'Invalid flags ' + row['FLAGS'].to_i
|
172
|
+
flunk 'Invalid flags: ' + row['FLAGS'].to_i
|
170
173
|
end
|
171
174
|
end
|
172
175
|
end
|
@@ -286,8 +289,8 @@ module DBI::GenericTests
|
|
286
289
|
Rubernate.session {o = C1.new.attach; o.p1 = {time => o}; pk = o.primary_key}
|
287
290
|
Rubernate.session {
|
288
291
|
o = find_by_pk pk
|
289
|
-
assert_equal 1, o.p1.size
|
290
|
-
_time, _obj = o.p1.shift
|
292
|
+
assert_equal 1, o.p1.size # check hash size
|
293
|
+
_time, _obj = o.p1.shift # get hash value
|
291
294
|
assert_equal time.to_a[0..5], _time.to_a[0..5] # Ignore time-zone
|
292
295
|
assert_equal o, _obj
|
293
296
|
}
|
@@ -298,8 +301,8 @@ module DBI::GenericTests
|
|
298
301
|
Rubernate.session {o = C1.new.attach; o.p1 = {date => o}; pk = o.primary_key}
|
299
302
|
Rubernate.session {
|
300
303
|
o = find_by_pk pk
|
301
|
-
assert_equal 1, o.p1.size
|
302
|
-
_date, _obj = o.p1.shift
|
304
|
+
assert_equal 1, o.p1.size # check hash size
|
305
|
+
_date, _obj = o.p1.shift # get hash value
|
303
306
|
assert_equal date, _date
|
304
307
|
assert_equal o, _obj
|
305
308
|
}
|
@@ -345,7 +348,52 @@ module DBI::GenericTests
|
|
345
348
|
o = find_by_pk pk
|
346
349
|
assert_equal ({}), o.p1
|
347
350
|
end
|
348
|
-
end
|
351
|
+
end
|
352
|
+
|
353
|
+
def test_param_all_types
|
354
|
+
pk, time, date = nil, Time.now, Date.today
|
355
|
+
Rubernate.session do
|
356
|
+
o = Full.new.attach
|
357
|
+
pk = o.primary_key
|
358
|
+
o.p_nil = nil
|
359
|
+
o.p_int = 123
|
360
|
+
o.p_float = 77.5
|
361
|
+
o.p_str = 'Test String'
|
362
|
+
o.p_time = time
|
363
|
+
o.p_date = date
|
364
|
+
o.p_array = [o, o, o]
|
365
|
+
o.p_hash = {nil => o,
|
366
|
+
777 => o,
|
367
|
+
1.23 => o,
|
368
|
+
'Str.Key' => o,
|
369
|
+
time => o,
|
370
|
+
date => o
|
371
|
+
}
|
372
|
+
end
|
373
|
+
Rubernate.session do
|
374
|
+
o = find_by_pk pk
|
375
|
+
assert_nil o.p_nil
|
376
|
+
assert_equal 123, o.p_int
|
377
|
+
assert_equal 77.5, o.p_float
|
378
|
+
assert_equal 'Test String', o.p_str
|
379
|
+
assert_equal time.to_a[0..5], o.p_time.to_a[0..5] # Ignore time zone
|
380
|
+
assert_equal date, o.p_date # Ignore time zone
|
381
|
+
assert_equal [o, o, o], o.p_array
|
382
|
+
assert_equal 6, o.p_hash.size
|
383
|
+
|
384
|
+
o.p_hash.each {|key, val|
|
385
|
+
assert_equal time.to_a[0..5], key.to_a[0..5] if key.is_a? Time
|
386
|
+
assert_equal date, key if key.is_a? Date
|
387
|
+
}
|
388
|
+
o.p_hash.delete_if {|key, val| key.is_a? Time or key.is_a? Date}
|
389
|
+
|
390
|
+
assert_equal ({nil => o,
|
391
|
+
777 => o,
|
392
|
+
1.23 => o,
|
393
|
+
'Str.Key' => o
|
394
|
+
}), o.p_hash
|
395
|
+
end
|
396
|
+
end
|
349
397
|
end
|
350
398
|
|
351
399
|
module GenericRuntimeTests
|
@@ -251,15 +251,15 @@ class Rubernate::BaseRuntimeTest < Test::Unit::TestCase
|
|
251
251
|
end
|
252
252
|
|
253
253
|
def test_instantiate
|
254
|
-
object = @runtime.instantiate 1, C1
|
254
|
+
object = @runtime.instantiate 1, C1.name
|
255
255
|
assert object
|
256
256
|
assert_equal 1, object.primary_key
|
257
257
|
assert_equal C1, object.class
|
258
258
|
end
|
259
259
|
|
260
260
|
def test_instantiate_existing
|
261
|
-
o0 = @runtime.instantiate 1, C1
|
262
|
-
o1 = @runtime.instantiate 1, C1
|
261
|
+
o0 = @runtime.instantiate 1, C1.name
|
262
|
+
o1 = @runtime.instantiate 1, C1.name
|
263
263
|
|
264
264
|
assert o0.equal?(o1)
|
265
265
|
assert_equal o0, o1
|
@@ -284,10 +284,15 @@ class Rubernate::BaseRuntimeTest < Test::Unit::TestCase
|
|
284
284
|
assert_equal o_.object_id, o__.object_id
|
285
285
|
end
|
286
286
|
end
|
287
|
-
|
288
|
-
def
|
289
|
-
@
|
290
|
-
|
287
|
+
|
288
|
+
def test_instantiate_fake_class
|
289
|
+
@factory.database = {27 => ['NoSuchClass', {}]}
|
290
|
+
Rubernate.session do
|
291
|
+
o = find_by_pk 27
|
292
|
+
assert_equal FakeEntity, o.class
|
293
|
+
assert_equal 27, o.primary_key
|
294
|
+
assert_raise(ObjectLoadError) {o.test}
|
295
|
+
end
|
291
296
|
end
|
292
297
|
|
293
298
|
def test_modified
|
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-03-
|
6
|
+
version: 0.1.1
|
7
|
+
date: 2006-03-07 00:00:00 +03:00
|
8
8
|
summary: Object-oriented storage for Ruby objects based on relational database model
|
9
9
|
require_paths:
|
10
10
|
- lib
|