Rubernate 0.1.5 → 0.1.6
Sign up to get free protection for your applications and to get access to all the features.
- data/README +1 -1
- data/lib/rubernate/callbacks.rb +17 -20
- data/lib/rubernate/impl/dbi_generic.rb +77 -50
- data/lib/rubernate/impl/dbi_mysql.rb +2 -2
- data/lib/rubernate/impl/dbi_oracle.rb +29 -3
- data/lib/rubernate/impl/dbi_pg.rb +2 -2
- data/lib/rubernate/impl/memory.rb +14 -12
- data/lib/rubernate/init/init_mysql.rb +2 -2
- data/lib/rubernate/init/init_oracle.rb +2 -2
- data/lib/rubernate/lazyload.rb +63 -0
- data/lib/rubernate/mixins.rb +6 -4
- data/lib/rubernate/persistent.rb +21 -21
- data/lib/rubernate/queries.rb +33 -11
- data/lib/rubernate/runtime.rb +49 -27
- data/lib/rubernate.rb +51 -33
- data/tests/config.rb +8 -5
- data/tests/rubernate/impl/dbi_generic_stub.rb +102 -35
- data/tests/rubernate/impl/dbi_oracle_test.rb +20 -0
- data/tests/rubernate/impl/memory_test.rb +16 -17
- data/tests/rubernate/rubernate_test.rb +352 -302
- metadata +3 -2
data/README
CHANGED
@@ -4,7 +4,7 @@ Rubernate is an object-oriented storage for Ruby objects based on relational dat
|
|
4
4
|
|
5
5
|
Rubernate provides an ability to create persistent object hierarchies with minimal restrictions
|
6
6
|
on their structure. The main difference of Rubernate from traditional ORM is that it uses common
|
7
|
-
database tables to store all classes of persistent objects. All object
|
7
|
+
database tables to store all classes of persistent objects. All object-related data are stored in
|
8
8
|
fixed set of tables.
|
9
9
|
|
10
10
|
This approach has following advantages:
|
data/lib/rubernate/callbacks.rb
CHANGED
@@ -4,64 +4,61 @@ module Rubernate
|
|
4
4
|
# Methods in these modules are intended to be overridden by user
|
5
5
|
# in other to extend basic functionality.
|
6
6
|
module Callbacks
|
7
|
-
# This module contains callback methods and
|
8
|
-
# included in all persisten classes.
|
7
|
+
# This module contains callback methods and included in all persisten classes.
|
9
8
|
module Persistent
|
10
|
-
#
|
9
|
+
# It's invoked if object has just been created in database.
|
11
10
|
def on_create
|
12
11
|
end
|
13
12
|
|
14
|
-
#
|
13
|
+
# It's invoked when object is about to be removed from database.
|
15
14
|
def on_remove
|
16
15
|
end
|
17
16
|
|
18
|
-
#
|
17
|
+
# It's invoked when object is about to be stored in database.
|
19
18
|
def on_save
|
20
19
|
end
|
21
20
|
|
22
|
-
#
|
21
|
+
# It's invoked if object has just been loaded from database.
|
23
22
|
def on_load
|
24
23
|
end
|
25
24
|
|
26
|
-
#
|
25
|
+
# It's invoked if property of object has just been changed via = or [] = operator.
|
27
26
|
def on_change prop_name, old_value, new_value
|
28
27
|
end
|
29
28
|
|
30
|
-
#
|
29
|
+
# It's invoked if Hash or Array property has been modified.
|
31
30
|
def on_modify prop_name, old_value, new_value
|
32
31
|
end
|
33
32
|
|
34
|
-
#
|
33
|
+
# It's invoked if object referred by this one has been deleted
|
35
34
|
def on_lose_ref ref_prop, ref_obj
|
36
35
|
end
|
37
36
|
end
|
38
37
|
|
39
|
-
# This module contains callback method and
|
40
|
-
# included in class Rubernate::Runtime.
|
38
|
+
# This module contains callback method and included in class Rubernate::Runtime.
|
41
39
|
module Runtime
|
42
|
-
#
|
40
|
+
# It's invoked on session creation.
|
43
41
|
def on_begin
|
44
42
|
end
|
45
43
|
|
46
|
-
#
|
44
|
+
# It's invoked if session/transaction is about to be rolled back.
|
47
45
|
def on_rollback
|
48
46
|
end
|
49
47
|
|
50
|
-
#
|
48
|
+
# It's invoked if session/transaction is about to be commited.
|
51
49
|
def before_commit
|
52
50
|
end
|
53
51
|
|
54
|
-
#
|
52
|
+
# It's invoked if transaction has just been commited.
|
55
53
|
def after_commit
|
56
54
|
end
|
57
55
|
|
58
|
-
#
|
59
|
-
# It
|
56
|
+
# It's invoked when Rubernate needs to flush modified objects.
|
57
|
+
# It happens when find_by_query is called or if session is going to be commited.
|
60
58
|
def before_flush modified
|
61
59
|
end
|
62
|
-
|
63
|
-
#
|
64
|
-
# This method is invoked only if any objects has been actually flushed
|
60
|
+
|
61
|
+
# It's invoked if after flushing modified objects.
|
65
62
|
def after_flush
|
66
63
|
end
|
67
64
|
end
|
@@ -59,7 +59,6 @@ module DBI
|
|
59
59
|
"Impl: #{@klass}, db_url: #{@db_url}, db_user: #{@db_user}, db_password: #{@db_password}"
|
60
60
|
end
|
61
61
|
end
|
62
|
-
|
63
62
|
|
64
63
|
TimeType = ::DBI::Timestamp
|
65
64
|
|
@@ -116,56 +115,42 @@ module DBI
|
|
116
115
|
objects = [objects] unless objects.kind_of? Array
|
117
116
|
return if objects.empty?
|
118
117
|
# clear r_params for objects
|
119
|
-
|
118
|
+
clear_params objects
|
120
119
|
# store r_params for objects
|
121
120
|
@dbh.prepare SAVE_PARAMS do |sth|
|
122
121
|
for object in objects
|
123
|
-
object.
|
122
|
+
object.__peer.each {|name, param|
|
124
123
|
save_param sth, object, name.to_s, param
|
125
124
|
}
|
126
125
|
end
|
127
126
|
end
|
128
127
|
# clear dirty flag
|
129
128
|
for object in objects
|
130
|
-
object.
|
129
|
+
object.__peer.dirty = false
|
131
130
|
end
|
132
131
|
end
|
133
132
|
|
134
133
|
# Loads object by primary_key
|
135
134
|
def load_by_pk pk
|
136
|
-
|
135
|
+
obj = nil
|
137
136
|
dbh.execute SELECT_ONE_OBJECT, pk do |sth|
|
138
|
-
|
139
|
-
|
140
|
-
|
137
|
+
lazily = lazy_factory
|
138
|
+
sth.fetch do |row|
|
139
|
+
obj = instantiate pk, row[P_CLASS], Rubernate::Peer.new unless obj
|
140
|
+
fetch_param obj, row, lazily
|
141
141
|
end
|
142
142
|
end
|
143
|
-
return nil unless
|
144
|
-
obj
|
145
|
-
post_load obj
|
143
|
+
return nil unless obj
|
144
|
+
post_load obj if obj
|
146
145
|
obj
|
147
|
-
end
|
146
|
+
end
|
148
147
|
|
149
148
|
# Loads objects by query with specified params.
|
150
149
|
def load_by_query query, params=[]
|
151
|
-
result = []
|
152
150
|
buffer = {} # buffer for not loaded objects (whose peer is nil)
|
153
|
-
|
154
|
-
dbh.execute(query, *params) { |sth|
|
155
|
-
sth.fetch { |row|
|
156
|
-
obj = instantiate row[0].to_i, row[1]
|
157
|
-
buffer[obj.primary_key], obj.peer = obj, Rubernate::Peer.new unless obj.peer
|
158
|
-
result << obj
|
159
|
-
}
|
160
|
-
}
|
151
|
+
result = find_buffer query, params, buffer
|
161
152
|
return result if buffer.empty?
|
162
|
-
|
163
|
-
dbh.execute(SELECT_PARAMS + object_ids_p(buffer.values)) {|sth|
|
164
|
-
sth.fetch {|row|
|
165
|
-
fetch_param buffer[row[P_PK].to_i].peer, row
|
166
|
-
}
|
167
|
-
}
|
168
|
-
# clears dirty flags
|
153
|
+
load_buffer buffer
|
169
154
|
buffer.values.each {|object| post_load object}
|
170
155
|
result
|
171
156
|
end
|
@@ -189,6 +174,35 @@ module DBI
|
|
189
174
|
@dbh.disconnect rescue Log.error 'Error during disconnect in session failed state'
|
190
175
|
end
|
191
176
|
private
|
177
|
+
# Select objects from database by query and determines objects
|
178
|
+
# that should be loaded and put them to buffer.
|
179
|
+
def find_buffer query, params, buffer
|
180
|
+
result = []
|
181
|
+
dbh.execute(query, *params) do|sth|
|
182
|
+
sth.fetch do |row|
|
183
|
+
obj = instantiate row[0].to_i, row[1]
|
184
|
+
buffer[obj.primary_key], obj.__peer = obj, Peer.new unless obj.__peer.is_a? Peer
|
185
|
+
result << obj
|
186
|
+
end
|
187
|
+
end
|
188
|
+
result
|
189
|
+
end
|
190
|
+
|
191
|
+
# Loads objects in buffer.
|
192
|
+
def load_buffer buffer
|
193
|
+
lazily = lazy_factory
|
194
|
+
dbh.execute(SELECT_PARAMS + object_ids_p(buffer.values)) {|sth|
|
195
|
+
sth.fetch {|row|
|
196
|
+
fetch_param buffer[row[P_PK].to_i], row, lazily
|
197
|
+
}
|
198
|
+
}
|
199
|
+
end
|
200
|
+
|
201
|
+
# Delete entries corresponding to objects from r_params
|
202
|
+
def clear_params objects
|
203
|
+
@dbh.do DELETE_PARAMS_FOR + object_ids_p(objects)
|
204
|
+
end
|
205
|
+
|
192
206
|
def save_param sth, obj, name, param
|
193
207
|
case param
|
194
208
|
when Persistent: save_ref sth, obj, name, param
|
@@ -259,35 +273,48 @@ module DBI
|
|
259
273
|
end
|
260
274
|
end
|
261
275
|
|
262
|
-
|
276
|
+
# Fetch param from db row
|
277
|
+
# * peer - is peer of object that is being loaded
|
278
|
+
# * row - database row
|
279
|
+
# * lazily - LazyLoader factory.
|
280
|
+
def fetch_param obj, row, lazily
|
263
281
|
return unless row[P_NAME] # there are no params for this object
|
264
|
-
name, flags, ref = row[P_NAME].to_sym, row[P_FLAGS].to_i
|
265
|
-
if
|
266
|
-
peer[name] = [] unless peer[name]
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
peer[name]
|
282
|
+
name, flags, peer, ref = row[P_NAME].to_sym, row[P_FLAGS].to_i, obj.__peer
|
283
|
+
if flags & PARAM_FLAG_ARRAY != 0 # fetch array row
|
284
|
+
peer[name] = [] unless peer[name]
|
285
|
+
ref = fetch_ref_value row, obj, name, lazily
|
286
|
+
peer[name][fetch_value(row, flags)] = ref if ref
|
287
|
+
elsif flags & PARAM_FLAG_HASH != 0 # fetch hash row
|
288
|
+
peer[name] = {} unless peer[name]
|
289
|
+
ref = fetch_ref_value row, obj, name, lazily
|
290
|
+
peer[name][fetch_value(row, flags)] = ref if ref
|
291
|
+
elsif flags & PARAM_FLAG_REF != 0 # reference row
|
292
|
+
ref = fetch_ref_value row, obj, name, lazily
|
293
|
+
peer[name] = ref if ref
|
271
294
|
else
|
272
|
-
peer[name] = fetch_value row, flags
|
295
|
+
peer[name] = fetch_value row, flags # simple attr row
|
273
296
|
end
|
274
|
-
end
|
275
|
-
|
297
|
+
end
|
298
|
+
|
276
299
|
def fetch_value row, flags
|
277
|
-
return row[P_INT].to_i
|
278
|
-
return row[P_FLT].to_f
|
279
|
-
return row[P_STR].to_s
|
280
|
-
return
|
281
|
-
return row[P_TIME].
|
282
|
-
return row[P_TIME].to_date if flags & PARAM_FLAG_DATE != 0
|
300
|
+
return row[P_INT].to_i if flags & PARAM_FLAG_INT != 0
|
301
|
+
return row[P_FLT].to_f if flags & PARAM_FLAG_FLOAT != 0
|
302
|
+
return row[P_STR].to_s if flags & PARAM_FLAG_STRING != 0
|
303
|
+
return row[P_TIME].to_time if flags & PARAM_FLAG_TIME != 0
|
304
|
+
return row[P_TIME].to_date if flags & PARAM_FLAG_DATE != 0
|
283
305
|
return nil
|
284
306
|
end
|
307
|
+
|
308
|
+
def fetch_ref_value row, obj, name, lazily
|
309
|
+
ref = row[P_REF]
|
310
|
+
return nil unless ref
|
311
|
+
ref_obj = instantiate ref, row[P_REF_CLASS]
|
312
|
+
unless ref_obj.__peer.is_a? Rubernate::Peer
|
313
|
+
ref_obj.__peer = lazily.create obj.primary_key, name
|
314
|
+
end
|
315
|
+
ref_obj
|
316
|
+
end
|
285
317
|
|
286
|
-
def fetch_ref_value row
|
287
|
-
ref_pk = nil
|
288
|
-
instantiate ref_pk, row[P_REF_CLASS] if ref_pk = row[P_REF]
|
289
|
-
end
|
290
|
-
|
291
318
|
def object_ids objects
|
292
319
|
ids = objects.collect {|o| o.primary_key}
|
293
320
|
ids.uniq!
|
@@ -18,10 +18,10 @@ module DBI
|
|
18
18
|
|
19
19
|
# Creates record in r_objects for specified object
|
20
20
|
def create object
|
21
|
-
object.
|
21
|
+
object.__peer = Rubernate::Peer.new unless object.__peer
|
22
22
|
@dbh.do CREATE_PEER, object.class.name
|
23
23
|
object.primary_key = @dbh.select_one(SELECT_NEXT_PK)[0].to_i
|
24
|
-
object.
|
24
|
+
object.__peer.dirty = true
|
25
25
|
object.primary_key
|
26
26
|
end
|
27
27
|
end
|
@@ -10,12 +10,38 @@ module DBI
|
|
10
10
|
|
11
11
|
# Creates record in r_objects for specified object
|
12
12
|
def create object
|
13
|
-
object.
|
13
|
+
object.__peer = Rubernate::Peer.new unless object.__peer
|
14
14
|
object.primary_key = @dbh.select_one(SELECT_NEXT_PK)[0].to_i
|
15
15
|
@dbh.do CREATE_PEER, object.primary_key, object.class.name
|
16
|
-
object.
|
16
|
+
object.__peer.dirty = true
|
17
17
|
object.primary_key
|
18
18
|
end
|
19
|
-
|
19
|
+
|
20
|
+
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
|
+
# Oracle requires that IN list must be less or equal to 1000 items
|
33
|
+
def load_buffer buffer
|
34
|
+
return super if buffer.size <= 1000
|
35
|
+
until buffer.empty?
|
36
|
+
chunck = {}
|
37
|
+
for i in 1..1000
|
38
|
+
break if buffer.empty?
|
39
|
+
pk, obj = buffer.shift
|
40
|
+
chunck[pk] = obj
|
41
|
+
end
|
42
|
+
super chunck
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
20
46
|
end
|
21
47
|
end
|
@@ -16,10 +16,10 @@ module DBI
|
|
16
16
|
|
17
17
|
# Creates record in r_objects for specified object
|
18
18
|
def create object
|
19
|
-
object.
|
19
|
+
object.__peer = Rubernate::Peer.new unless object.__peer
|
20
20
|
object.primary_key = @dbh.select_one(SELECT_NEXT_PK)[0].to_i
|
21
21
|
@dbh.do CREATE_PEER, object.primary_key, object.class.name
|
22
|
-
object.
|
22
|
+
object.__peer.dirty = true
|
23
23
|
object.primary_key
|
24
24
|
end
|
25
25
|
|
@@ -2,10 +2,10 @@ require 'rubernate/queries'
|
|
2
2
|
|
3
3
|
module Rubernate
|
4
4
|
# This module contains implementation of Runtime that operates with memory instead database.
|
5
|
-
# It is intended only
|
6
|
-
module Memory
|
7
|
-
module Queries
|
8
|
-
class Query < Rubernate::Queries::Expr
|
5
|
+
# It is intended only for testing.
|
6
|
+
module Memory # :nodoc:
|
7
|
+
module Queries #:nodoc:
|
8
|
+
class Query < Rubernate::Queries::Expr #:nodoc:
|
9
9
|
def params v
|
10
10
|
case v
|
11
11
|
when Array: v
|
@@ -15,7 +15,7 @@ module Memory
|
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
18
|
-
class Factory < Rubernate::Queries::Factory
|
18
|
+
class Factory < Rubernate::Queries::Factory # :nodoc:
|
19
19
|
def initialize
|
20
20
|
super
|
21
21
|
@query = Query
|
@@ -23,7 +23,7 @@ module Memory
|
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
26
|
-
class Reference
|
26
|
+
class Reference #:nodoc:
|
27
27
|
attr_reader :primary_key, :object_class
|
28
28
|
def initialize pk, klass
|
29
29
|
@primary_key, @object_class = pk, klass
|
@@ -35,7 +35,7 @@ module Memory
|
|
35
35
|
end
|
36
36
|
end
|
37
37
|
|
38
|
-
class Configuration
|
38
|
+
class Configuration # :nodoc:
|
39
39
|
attr_accessor :database
|
40
40
|
def initialize
|
41
41
|
@pk, @database = 1001, {}
|
@@ -48,9 +48,11 @@ module Memory
|
|
48
48
|
end
|
49
49
|
end
|
50
50
|
|
51
|
-
class Runtime < Rubernate::Runtime
|
51
|
+
class Runtime < Rubernate::Runtime # :nodoc:
|
52
52
|
attr_accessor :pk, :database
|
53
53
|
|
54
|
+
@@lazy_loader = LazyLoader.new
|
55
|
+
|
54
56
|
# Database is maps {pk => [class, {attr => value}]}
|
55
57
|
def initialize database, pk_seq
|
56
58
|
super()
|
@@ -86,13 +88,13 @@ module Memory
|
|
86
88
|
def load_param p_data
|
87
89
|
case p_data
|
88
90
|
when Reference:
|
89
|
-
instantiate p_data.primary_key, p_data.object_class
|
91
|
+
instantiate p_data.primary_key, p_data.object_class, @@lazy_loader
|
90
92
|
when Array:
|
91
93
|
p_data.collect{|ref| instantiate(ref.primary_key, ref.object_class)}
|
92
94
|
when Hash:
|
93
95
|
result = {}
|
94
96
|
p_data.each_pair {|key, ref|
|
95
|
-
result[key] = instantiate
|
97
|
+
result[key] = instantiate ref.primary_key, ref.object_class, LazyLoader.new
|
96
98
|
}
|
97
99
|
result
|
98
100
|
when Fixnum
|
@@ -106,13 +108,13 @@ module Memory
|
|
106
108
|
objects = [objects] unless objects.kind_of? Array
|
107
109
|
for object in objects
|
108
110
|
@database[object.primary_key] = [object.class, save_peer(object)]
|
109
|
-
object.
|
111
|
+
object.__peer.dirty = false
|
110
112
|
end
|
111
113
|
end
|
112
114
|
|
113
115
|
def save_peer obj
|
114
116
|
result = {}
|
115
|
-
obj.
|
117
|
+
obj.__peer.each {|key, value| result[key] = save_param obj, key, value}
|
116
118
|
result
|
117
119
|
end
|
118
120
|
|
@@ -24,10 +24,10 @@ module DBI
|
|
24
24
|
CONSTRAINT R_REF_FK FOREIGN KEY (REF_VALUE) REFERENCES R_OBJECTS(OBJECT_PK) ON DELETE CASCADE) ENGINE=InnoDB;
|
25
25
|
}.gsub(INDENT, '')
|
26
26
|
CREATE_INDEX_O_PK_CLASS = %q{
|
27
|
-
CREATE INDEX R_O_PK_CLASS ON R_OBJECTS (OBJECT_PK
|
27
|
+
CREATE INDEX R_O_PK_CLASS ON R_OBJECTS (OBJECT_PK, OBJECT_CLASS);
|
28
28
|
}.gsub(INDENT, '')
|
29
29
|
CREATE_INDEX_P_PK_NAME = %q{
|
30
|
-
CREATE INDEX R_P_PK_NAME ON R_PARAMS (OBJECT_PK
|
30
|
+
CREATE INDEX R_P_PK_NAME ON R_PARAMS (OBJECT_PK, NAME);
|
31
31
|
}.gsub(INDENT, '')
|
32
32
|
|
33
33
|
TEMPLATE = %q{
|
@@ -24,10 +24,10 @@ module DBI
|
|
24
24
|
CONSTRAINT R_REF_FK FOREIGN KEY (REF_VALUE) REFERENCES R_OBJECTS(OBJECT_PK) ON DELETE CASCADE)
|
25
25
|
}.gsub(INDENT, '')
|
26
26
|
CREATE_INDEX_O_PK_CLASS = %q{
|
27
|
-
CREATE INDEX R_O_PK_CLASS ON R_OBJECTS (OBJECT_PK
|
27
|
+
CREATE INDEX R_O_PK_CLASS ON R_OBJECTS (OBJECT_PK, OBJECT_CLASS)
|
28
28
|
}.gsub(INDENT, '')
|
29
29
|
CREATE_INDEX_P_PK_NAME = %q{
|
30
|
-
CREATE INDEX R_P_PK_NAME ON R_PARAMS (OBJECT_PK
|
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
33
|
CREATE SEQUENCE R_PK_SEQUENCE START WITH 1001 INCREMENT BY 1
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module Rubernate
|
2
|
+
# Base class and default implementation for lazy loaders.
|
3
|
+
# Loads each objects separately.
|
4
|
+
class LazyLoader
|
5
|
+
def load_lazy obj
|
6
|
+
Rubernate::Log.debug {"load-lazy, pk: #{obj.primary_key}"}
|
7
|
+
Rubernate.runtime.find_by_pk obj.primary_key, true
|
8
|
+
end
|
9
|
+
# Creates LazyLoader
|
10
|
+
# * obj_pk - object that holds reference to object that is supposed to be load lazily.
|
11
|
+
# * p_name - name of reference(or collection) parameter that referes to object that is to be loaded lazily.
|
12
|
+
def create obj_pk, p_name
|
13
|
+
@@single_lazy_loader
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# Factory for LazyLoader default implementation
|
18
|
+
class LazyLoaderFactory
|
19
|
+
# All instances of LazyLoader are similzr so only one instance is used
|
20
|
+
def create obj_pk, p_name
|
21
|
+
@@single_lazy_loader
|
22
|
+
end
|
23
|
+
@@single_lazy_loader = LazyLoader.new
|
24
|
+
end
|
25
|
+
|
26
|
+
# Loads all objects that are holded by the same collection as the object that is loaded
|
27
|
+
class ParamLazyLoader < LazyLoader
|
28
|
+
PRE_LOAD = 'select distinct ref_value from r_params where object_pk = ? and name = ?'
|
29
|
+
def initialize referer_pk, ref_param
|
30
|
+
@referer_pk, @ref_param = referer_pk, ref_param
|
31
|
+
end
|
32
|
+
def load_lazy obj
|
33
|
+
Rubernate::Log.debug {"load-lazy, referer: #{@referer_pk}, reference: #{@ref_param}"}
|
34
|
+
Rubernate.find_by_query PRE_LOAD, [@referer_pk, @ref_param.to_s]
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
class ParamLazyLoaderFactory
|
39
|
+
def create obj_pk, p_name
|
40
|
+
@cache = {} unless @cache
|
41
|
+
@cache[[obj_pk, p_name]] ||= ParamLazyLoader.new obj_pk, p_name
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# Loads all objects that are refered by the same object as the object that is loaded
|
46
|
+
class HolderLazyLoader < LazyLoader
|
47
|
+
PRE_LOAD = 'select distinct ref_value from r_params where object_pk = ?'
|
48
|
+
def initialize referer_pk
|
49
|
+
@referer_pk = referer_pk
|
50
|
+
end
|
51
|
+
def load_lazy obj
|
52
|
+
Rubernate::Log.debug {"load-lazy, referer: #{@referer_pk}"}
|
53
|
+
Rubernate.find_by_query PRE_LOAD, [@referer_pk]
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
class HolderLazyLoaderFactory
|
58
|
+
def create obj_pk, p_name
|
59
|
+
@cache = {} unless @cache
|
60
|
+
@cache[obj_pk] ||= HolderLazyLoader.new obj_pk
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
data/lib/rubernate/mixins.rb
CHANGED
@@ -2,10 +2,12 @@ require 'set'
|
|
2
2
|
|
3
3
|
class Module
|
4
4
|
# Finds class defined in current module.
|
5
|
-
#
|
5
|
+
#
|
6
|
+
# :call-seq: find_class(name, scaned = Set.new)
|
7
|
+
#
|
6
8
|
# Module.find_class 'ClassName' # finds class 'ClassName' in global space
|
7
9
|
# SomeModule.find_class 'ClassName' # finds class 'ClassName' in module SomeModule
|
8
|
-
#
|
10
|
+
#
|
9
11
|
def find_class name, scaned = Set.new
|
10
12
|
scaned << self
|
11
13
|
for unit in module_units - scaned
|
@@ -64,12 +66,12 @@ class Class
|
|
64
66
|
module_eval %{
|
65
67
|
def #{name}
|
66
68
|
ensure_loaded
|
67
|
-
|
69
|
+
@__peer[:#{name}]
|
68
70
|
end
|
69
71
|
|
70
72
|
def #{name}= value
|
71
73
|
ensure_loaded
|
72
|
-
|
74
|
+
@__peer.set :#{name}, value, &on_change_callback
|
73
75
|
end
|
74
76
|
}
|
75
77
|
end
|
data/lib/rubernate/persistent.rb
CHANGED
@@ -25,29 +25,28 @@ module Persistent
|
|
25
25
|
def removed?
|
26
26
|
@removed
|
27
27
|
end
|
28
|
+
|
29
|
+
# Checks if object has been modified.
|
30
|
+
def dirty?
|
31
|
+
return false unless @__peer.is_a? Peer
|
32
|
+
@__peer.dirty? &on_modify_callback
|
33
|
+
end
|
28
34
|
|
29
35
|
# Retruns objects peer if ther is one or nil in other case
|
30
|
-
|
31
|
-
|
36
|
+
# This method is implementation detail. Users are not supposed to use it.
|
37
|
+
def __peer # :nodoc:
|
38
|
+
@__peer
|
32
39
|
end
|
33
40
|
|
34
41
|
# Sets peer if there is no one and invokes on_load callback else skip.
|
35
42
|
# If value is nil peer will be lazy initialized in ensure_loaded.
|
36
|
-
# This method should be invoked only once with not nil value.
|
37
|
-
def
|
38
|
-
|
39
|
-
|
40
|
-
disable_ensure
|
43
|
+
# This method should be invoked only once with not nil value. # TODO: rdoc on lazy loaders
|
44
|
+
def __peer= value # :nodoc:
|
45
|
+
if @__peer
|
46
|
+
@__peer = value unless value.is_a? LazyLoader
|
41
47
|
else
|
42
|
-
|
43
|
-
Rubernate.runtime.find_by_pk @primary_key, true
|
44
|
-
end
|
48
|
+
@__peer = value
|
45
49
|
end
|
46
|
-
@peer = value
|
47
|
-
end
|
48
|
-
|
49
|
-
def dirty?
|
50
|
-
peer.dirty? &on_modify_callback
|
51
50
|
end
|
52
51
|
|
53
52
|
# Defines metod == in for rubernated classes
|
@@ -61,13 +60,14 @@ module Persistent
|
|
61
60
|
end
|
62
61
|
|
63
62
|
private
|
64
|
-
def ensure_loaded
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
63
|
+
def ensure_loaded
|
64
|
+
return if @__peer.is_a? Peer # Object has already been initialized
|
65
|
+
if @__peer.is_a? LazyLoader
|
66
|
+
@__peer.load_lazy self # LazyLoader
|
67
|
+
else
|
68
|
+
@__peer = Peer.new # Default peer.
|
70
69
|
end
|
70
|
+
raise "lazy loading fails, peer is #{@__peer}" unless @__peer.is_a? Peer
|
71
71
|
end
|
72
72
|
|
73
73
|
def on_modify_callback
|