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 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's-related data are stored in
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:
@@ -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
- # Invoked if object has just been created in database.
9
+ # It's invoked if object has just been created in database.
11
10
  def on_create
12
11
  end
13
12
 
14
- # Invoked when object is about to be removed from database.
13
+ # It's invoked when object is about to be removed from database.
15
14
  def on_remove
16
15
  end
17
16
 
18
- # Invoked when object is about to be stored in database.
17
+ # It's invoked when object is about to be stored in database.
19
18
  def on_save
20
19
  end
21
20
 
22
- # Invoked if object has just been loaded from database.
21
+ # It's invoked if object has just been loaded from database.
23
22
  def on_load
24
23
  end
25
24
 
26
- # Invoked if property of object has just been changed via = or [] = operator.
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
- # Invoked if Hash or Array property has been modified.
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
- # Invoked if object referred by this one has been deleted
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
- # Invoked on session creation.
40
+ # It's invoked on session creation.
43
41
  def on_begin
44
42
  end
45
43
 
46
- # Invoked if session is about to be rolled back.
44
+ # It's invoked if session/transaction is about to be rolled back.
47
45
  def on_rollback
48
46
  end
49
47
 
50
- # Invoked if session is about to be commited.
48
+ # It's invoked if session/transaction is about to be commited.
51
49
  def before_commit
52
50
  end
53
51
 
54
- # Invoked if session has just been commited
52
+ # It's invoked if transaction has just been commited.
55
53
  def after_commit
56
54
  end
57
55
 
58
- # Invoked when Rubernate needs to flush modified objects
59
- # It can happens when find_by_query is called or if session is going to be commited
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
- # Invoked if after flushing modified objects
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
- @dbh.do DELETE_PARAMS_FOR + object_ids_p(objects)
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.peer.each {|name, param|
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.peer.dirty = false
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
- klass, peer = nil
135
+ obj = nil
137
136
  dbh.execute SELECT_ONE_OBJECT, pk do |sth|
138
- sth.fetch do |row|
139
- klass, peer = row[P_CLASS], Rubernate::Peer.new unless klass
140
- fetch_param peer, row
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 klass
144
- obj = instantiate pk, klass, peer
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
- # selects objects and stores not loaded to buffer
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
- # load peers for objects in buffer.
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
- def fetch_param peer, row
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 flags & PARAM_FLAG_ARRAY != 0
266
- peer[name] = [] unless peer[name]
267
- peer[name][fetch_value(row, flags)] = ref if ref = fetch_ref_value(row)
268
- elsif flags & PARAM_FLAG_HASH != 0
269
- peer[name] = {} unless peer[name]
270
- peer[name][fetch_value(row, flags)] = ref if ref = fetch_ref_value(row)
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 if flags & PARAM_FLAG_INT != 0
278
- return row[P_FLT].to_f if flags & PARAM_FLAG_FLOAT != 0
279
- return row[P_STR].to_s if flags & PARAM_FLAG_STRING != 0
280
- return instantiate(row[P_REF].to_i, row[P_REF_CLASS]) if flags & PARAM_FLAG_REF != 0
281
- return row[P_TIME].to_time if flags & PARAM_FLAG_TIME != 0
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.peer = Rubernate::Peer.new
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.peer.dirty = true
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.peer = Rubernate::Peer.new
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.peer.dirty = true
16
+ object.__peer.dirty = true
17
17
  object.primary_key
18
18
  end
19
- end
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.peer = Rubernate::Peer.new
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.peer.dirty = true
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 of testing.
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(ref.primary_key, ref.object_class)
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.peer.dirty = false
111
+ object.__peer.dirty = false
110
112
  end
111
113
  end
112
114
 
113
115
  def save_peer obj
114
116
  result = {}
115
- obj.peer.each {|key, value| result[key] = save_param obj, key, value}
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 ASC, OBJECT_CLASS);
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 ASC, NAME);
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 ASC, OBJECT_CLASS)
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 ASC, NAME)
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
@@ -2,10 +2,12 @@ require 'set'
2
2
 
3
3
  class Module
4
4
  # Finds class defined in current module.
5
- # :call-seq:
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
- peer[:#{name}]
69
+ @__peer[:#{name}]
68
70
  end
69
71
 
70
72
  def #{name}= value
71
73
  ensure_loaded
72
- peer.set :#{name}, value, &on_change_callback
74
+ @__peer.set :#{name}, value, &on_change_callback
73
75
  end
74
76
  }
75
77
  end
@@ -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
- def peer
31
- @peer
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 peer= value
38
- return if @peer
39
- if value
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
- def self.ensure_loaded
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
- self.peer = Peer.new
66
- end
67
-
68
- def disable_ensure
69
- def self.ensure_loaded
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