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 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