Rubernate 0.1.5 → 0.1.6
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/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
|