Rubernate 0.1.0 → 0.1.1
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/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
|