odba 1.1.8 → 1.2.0
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.
- checksums.yaml +4 -4
- data/.github/workflows/devenv.yml +30 -0
- data/.github/workflows/ruby.yml +8 -7
- data/.gitignore +11 -0
- data/{History.txt → History.md} +43 -19
- data/README.md +79 -2
- data/Rakefile +3 -15
- data/devenv.lock +171 -0
- data/devenv.nix +53 -0
- data/devenv.yaml +8 -0
- data/lib/odba/cache.rb +401 -344
- data/lib/odba/cache_entry.rb +42 -31
- data/lib/odba/connection_pool.rb +99 -72
- data/lib/odba/drbwrapper.rb +26 -18
- data/lib/odba/id_server.rb +20 -20
- data/lib/odba/index.rb +249 -215
- data/lib/odba/index_definition.rb +17 -17
- data/lib/odba/marshal.rb +15 -14
- data/lib/odba/odba.rb +40 -32
- data/lib/odba/odba_error.rb +4 -2
- data/lib/odba/persistable.rb +460 -414
- data/lib/odba/storage.rb +328 -277
- data/lib/odba/stub.rb +166 -153
- data/lib/odba/version.rb +1 -1
- data/lib/odba.rb +9 -9
- data/odba.gemspec +8 -3
- data/test/example.rb +47 -0
- data/test/helper.rb +6 -0
- data/test/suite.rb +7 -0
- data/test/test_array.rb +38 -33
- data/test/test_cache.rb +657 -622
- data/test/test_cache_entry.rb +51 -71
- data/test/test_connection_pool.rb +70 -25
- data/test/test_drbwrapper.rb +24 -19
- data/test/test_hash.rb +98 -89
- data/test/test_id_server.rb +30 -32
- data/test/test_index.rb +191 -158
- data/test/test_marshal.rb +15 -25
- data/test/test_persistable.rb +378 -369
- data/test/test_storage.rb +510 -471
- data/test/test_stub.rb +147 -135
- metadata +63 -20
- data/Guide.txt +0 -36
- data/Manifest.txt +0 -38
- data/install.rb +0 -1098
- data/lib/odba/18_19_loading_compatibility.rb +0 -77
data/lib/odba/persistable.rb
CHANGED
|
@@ -1,27 +1,36 @@
|
|
|
1
1
|
#!/usr/bin/env ruby
|
|
2
|
-
|
|
2
|
+
|
|
3
3
|
# ODBA::Persistable -- odba -- 23.12.2011 -- mhatakeyama@ywesee.com
|
|
4
4
|
# ODBA::Persistable -- odba -- 29.04.2004 -- hwyss@ywesee.com rwaltert@ywesee.com mwalder@ywesee.com
|
|
5
5
|
|
|
6
6
|
class Object # :nodoc: all
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
7
|
+
def odba_id
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def odba_instance
|
|
11
|
+
self
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def odba_isolated_stub
|
|
15
|
+
self
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def metaclass
|
|
19
|
+
class << self; self; end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def meta_eval &blk
|
|
23
|
+
metaclass.instance_eval(&blk)
|
|
24
|
+
end
|
|
17
25
|
end
|
|
18
26
|
|
|
19
|
-
require
|
|
20
|
-
require
|
|
27
|
+
require "odba/stub"
|
|
28
|
+
require "observer"
|
|
21
29
|
|
|
22
30
|
module ODBA
|
|
23
|
-
|
|
24
|
-
|
|
31
|
+
class Stub; end
|
|
32
|
+
|
|
33
|
+
module Persistable
|
|
25
34
|
meta = Struct.new(:exact, :limit)
|
|
26
35
|
Exact = meta.new
|
|
27
36
|
Exact.exact = true
|
|
@@ -29,28 +38,28 @@ module ODBA
|
|
|
29
38
|
Find.limit = 1
|
|
30
39
|
# Classes which include Persistable have a class-method 'odba_index'
|
|
31
40
|
attr_accessor :odba_persistent
|
|
32
|
-
def
|
|
41
|
+
def self.append_features(mod)
|
|
33
42
|
super
|
|
34
43
|
mod.module_eval {
|
|
35
44
|
class << self
|
|
36
45
|
def odba_index(*keys)
|
|
37
|
-
require
|
|
46
|
+
require "odba/index_definition"
|
|
38
47
|
origin_klass = self
|
|
39
48
|
resolve_origin = nil
|
|
40
49
|
resolve_target = :none
|
|
41
50
|
resolve = {}
|
|
42
51
|
opts = {}
|
|
43
|
-
if
|
|
44
|
-
if
|
|
52
|
+
if keys.size > 1
|
|
53
|
+
if keys.last.is_a?(Hash)
|
|
45
54
|
opts = keys.pop
|
|
46
55
|
end
|
|
47
|
-
if
|
|
56
|
+
if keys.last.is_a?(Class)
|
|
48
57
|
origin_klass = keys.pop
|
|
49
58
|
resolve = keys.pop
|
|
50
59
|
resolve_origin = keys.pop
|
|
51
|
-
elsif
|
|
60
|
+
elsif keys.last.is_a?(Symbol)
|
|
52
61
|
keys.each { |key|
|
|
53
|
-
resolve.store(key, {
|
|
62
|
+
resolve.store(key, {"resolve" => key})
|
|
54
63
|
}
|
|
55
64
|
else
|
|
56
65
|
resolve = keys.pop
|
|
@@ -59,17 +68,13 @@ module ODBA
|
|
|
59
68
|
resolve = keys.first
|
|
60
69
|
end
|
|
61
70
|
keys.each { |key|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
else
|
|
65
|
-
key = key.to_s
|
|
66
|
-
end
|
|
67
|
-
unless(instance_methods.include?(key))
|
|
71
|
+
key.to_sym
|
|
72
|
+
unless instance_methods.include?(key)
|
|
68
73
|
attr_accessor key
|
|
69
74
|
end
|
|
70
75
|
}
|
|
71
|
-
index_prefix =
|
|
72
|
-
index_suffix = Persistable.sanitize(keys.join(
|
|
76
|
+
index_prefix = name.downcase.gsub("::", "_")
|
|
77
|
+
index_suffix = Persistable.sanitize(keys.join("_and_"))
|
|
73
78
|
index_name = sprintf("%s_%s", index_prefix, index_suffix)
|
|
74
79
|
search_name = sprintf("search_by_%s", index_suffix)
|
|
75
80
|
exact_name = sprintf("search_by_exact_%s", index_suffix)
|
|
@@ -82,58 +87,58 @@ module ODBA
|
|
|
82
87
|
index_definition.resolve_search_term = resolve
|
|
83
88
|
index_definition.resolve_origin = resolve_origin.to_s
|
|
84
89
|
index_definition.resolve_target = resolve_target
|
|
85
|
-
opts.each { |key, val| index_definition.send "#{key}=", val }
|
|
90
|
+
opts.each { |key, val| index_definition.send :"#{key}=", val }
|
|
86
91
|
ODBA.cache.ensure_index_deferred(index_definition)
|
|
87
92
|
meta_eval {
|
|
88
93
|
define_method(search_name) { |*vals|
|
|
89
|
-
if
|
|
94
|
+
if vals.size > 1
|
|
90
95
|
args = {}
|
|
91
96
|
vals.each_with_index { |val, idx|
|
|
92
97
|
cond = case val
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
+
when Numeric, Date
|
|
99
|
+
"="
|
|
100
|
+
else
|
|
101
|
+
"like"
|
|
102
|
+
end
|
|
98
103
|
args.store(keys.at(idx),
|
|
99
|
-
|
|
104
|
+
{"value" => val, "condition" => cond})
|
|
100
105
|
}
|
|
101
106
|
ODBA.cache.retrieve_from_index(index_name, args)
|
|
102
107
|
else
|
|
103
108
|
ODBA.cache.retrieve_from_index(index_name, vals.first)
|
|
104
109
|
end
|
|
105
110
|
}
|
|
106
|
-
define_method(exact_name) {
|
|
107
|
-
if
|
|
111
|
+
define_method(exact_name) { |*vals|
|
|
112
|
+
if vals.size > 1
|
|
108
113
|
args = {}
|
|
109
114
|
vals.each_with_index { |val, idx|
|
|
110
115
|
args.store(keys.at(idx), val)
|
|
111
116
|
}
|
|
112
117
|
ODBA.cache.retrieve_from_index(index_name, args,
|
|
113
|
-
|
|
118
|
+
ODBA::Persistable::Exact)
|
|
114
119
|
else
|
|
115
120
|
ODBA.cache.retrieve_from_index(index_name, vals.first,
|
|
116
|
-
|
|
121
|
+
ODBA::Persistable::Exact)
|
|
117
122
|
end
|
|
118
123
|
}
|
|
119
|
-
define_method(find_name) {
|
|
120
|
-
if
|
|
124
|
+
define_method(find_name) { |*vals|
|
|
125
|
+
if vals.size > 1
|
|
121
126
|
args = {}
|
|
122
127
|
vals.each_with_index { |val, idx|
|
|
123
128
|
cond = case val
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
+
when Numeric, Date
|
|
130
|
+
"="
|
|
131
|
+
else
|
|
132
|
+
"like"
|
|
133
|
+
end
|
|
129
134
|
args.store(keys.at(idx),
|
|
130
|
-
|
|
135
|
+
{"value" => val, "condition" => cond})
|
|
131
136
|
}
|
|
132
137
|
ODBA.cache.retrieve_from_index(index_name, args,
|
|
133
|
-
|
|
138
|
+
ODBA::Persistable::Find)
|
|
134
139
|
else
|
|
135
140
|
ODBA.cache.retrieve_from_index(index_name, vals.first,
|
|
136
|
-
|
|
141
|
+
ODBA::Persistable::Find)
|
|
137
142
|
end.first
|
|
138
143
|
}
|
|
139
144
|
define_method(keys_name) { |*vals|
|
|
@@ -144,15 +149,17 @@ module ODBA
|
|
|
144
149
|
}
|
|
145
150
|
index_definition
|
|
146
151
|
end
|
|
152
|
+
|
|
147
153
|
def odba_extent
|
|
148
154
|
all = ODBA.cache.extent(self)
|
|
149
|
-
if
|
|
155
|
+
if block_given?
|
|
150
156
|
all.each { |instance| yield instance }
|
|
151
157
|
nil
|
|
152
158
|
else
|
|
153
159
|
all
|
|
154
160
|
end
|
|
155
161
|
end
|
|
162
|
+
|
|
156
163
|
def odba_count
|
|
157
164
|
ODBA.cache.count(self)
|
|
158
165
|
end
|
|
@@ -160,32 +167,27 @@ module ODBA
|
|
|
160
167
|
}
|
|
161
168
|
end
|
|
162
169
|
@@sanitize_ptrn = /[^a-z0-9_]/i
|
|
163
|
-
def
|
|
164
|
-
name.gsub(@@sanitize_ptrn,
|
|
165
|
-
end
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
ODBA_SERIALIZABLE = []
|
|
185
|
-
def ==(other) # :nodoc:
|
|
186
|
-
super(other.odba_instance)
|
|
187
|
-
end
|
|
188
|
-
@@odba_id_name = RUBY_VERSION >= '1.9' ? :@odba_id : '@odba_id'
|
|
170
|
+
def self.sanitize(name)
|
|
171
|
+
name.gsub(@@sanitize_ptrn, "_")
|
|
172
|
+
end
|
|
173
|
+
attr_accessor :odba_name, :odba_prefetch
|
|
174
|
+
# Classes which include Persistable may override ODBA_EXCLUDE_VARS to
|
|
175
|
+
# prevent data from being stored in the database (e.g. passwords, file
|
|
176
|
+
# descriptors). Simply redefine: ODBA_EXCLUDE_VARS = ['@foo']
|
|
177
|
+
ODBA_EXCLUDE_VARS = []
|
|
178
|
+
ODBA_INDEXABLE = true # :nodoc:
|
|
179
|
+
# see odba_prefetch?
|
|
180
|
+
ODBA_PREFETCH = false
|
|
181
|
+
ODBA_PREDEFINE_EXCLUDE_VARS = [:@odba_observers] # :nodoc:
|
|
182
|
+
ODBA_PREDEFINE_SERIALIZABLE = [:@odba_target_ids] # :nodoc:, legacy
|
|
183
|
+
# If you want to prevent Persistables from being disconnected and stored
|
|
184
|
+
# separately (Array and Hash are Persistable by default), redefine:
|
|
185
|
+
# ODBA_SERIALIZABLE = ['@bar']
|
|
186
|
+
ODBA_SERIALIZABLE = []
|
|
187
|
+
def ==(other) # :nodoc:
|
|
188
|
+
super(other.odba_instance)
|
|
189
|
+
end
|
|
190
|
+
@@odba_id_name = :@odba_id
|
|
189
191
|
def dup # :nodoc:
|
|
190
192
|
twin = super
|
|
191
193
|
## since twin may not be a Persistable, we need to do some magic here to
|
|
@@ -193,343 +195,376 @@ module ODBA
|
|
|
193
195
|
twin.instance_variable_set(@@odba_id_name, nil)
|
|
194
196
|
twin
|
|
195
197
|
end
|
|
198
|
+
|
|
196
199
|
def eql?(other) # :nodoc:
|
|
197
200
|
(other.is_a?(Stub) && other.odba_id == @odba_id) \
|
|
198
201
|
|| super(other.odba_instance)
|
|
199
202
|
end
|
|
203
|
+
|
|
200
204
|
# Add an observer for Cache#store(self), Cache#delete(self) and
|
|
201
205
|
# Cache#clean removing the object from the Cache
|
|
202
206
|
def odba_add_observer(obj)
|
|
203
207
|
odba_observers.push(obj)
|
|
204
208
|
obj
|
|
205
209
|
end
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
210
|
+
|
|
211
|
+
def odba_collection # :nodoc:
|
|
212
|
+
[]
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
# Removes all connections to another persistable. This method is called
|
|
216
|
+
# by the Cache server when _remove_object_ is deleted from the database
|
|
217
|
+
def odba_cut_connection(remove_object)
|
|
218
|
+
odba_potentials.each { |name|
|
|
219
|
+
var = instance_variable_get(name)
|
|
220
|
+
if var.eql?(remove_object)
|
|
221
|
+
instance_variable_set(name, nil)
|
|
222
|
+
end
|
|
223
|
+
}
|
|
224
|
+
end
|
|
225
|
+
|
|
219
226
|
# Permanently deletes this Persistable from the database and remove
|
|
220
227
|
# all connections to it
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
228
|
+
def odba_delete
|
|
229
|
+
ODBA.cache.delete(self)
|
|
230
|
+
end
|
|
231
|
+
|
|
224
232
|
# Delete _observer_ as an observer on this object.
|
|
225
233
|
# It will no longer receive notifications.
|
|
226
234
|
def odba_delete_observer(observer)
|
|
227
|
-
@odba_observers.delete(observer) if
|
|
235
|
+
@odba_observers.delete(observer) if @odba_observers
|
|
228
236
|
end
|
|
237
|
+
|
|
229
238
|
# Delete all observers associated with this object.
|
|
230
239
|
def odba_delete_observers
|
|
231
240
|
@odba_observers = nil
|
|
232
241
|
end
|
|
233
|
-
|
|
234
|
-
|
|
242
|
+
|
|
243
|
+
def odba_dup # :nodoc:
|
|
244
|
+
twin = dup
|
|
235
245
|
twin.extend(Persistable)
|
|
236
246
|
twin.odba_id = @odba_id
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
+
odba_potentials.each { |name|
|
|
248
|
+
var = twin.instance_variable_get(name)
|
|
249
|
+
if var.is_a?(ODBA::Stub)
|
|
250
|
+
stub = var.odba_dup
|
|
251
|
+
stub.odba_container = twin
|
|
252
|
+
twin.instance_variable_set(name, stub)
|
|
253
|
+
end
|
|
254
|
+
}
|
|
255
|
+
twin
|
|
256
|
+
end
|
|
257
|
+
|
|
247
258
|
def odba_exclude_vars # :nodoc:
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
else
|
|
251
|
-
ODBA_PREDEFINE_EXCLUDE_VARS
|
|
252
|
-
end
|
|
253
|
-
if(defined?(self::class::ODBA_EXCLUDE_VARS))
|
|
254
|
-
exc += self::class::ODBA_EXCLUDE_VARS
|
|
255
|
-
end
|
|
256
|
-
if RUBY_VERSION >= '1.9'
|
|
257
|
-
exc.map{|v| v.to_sym}
|
|
259
|
+
exc = if defined?(self.class::ODBA_PREDEFINE_EXCLUDE_VARS)
|
|
260
|
+
self.class::ODBA_PREDEFINE_EXCLUDE_VARS
|
|
258
261
|
else
|
|
259
|
-
|
|
262
|
+
ODBA_PREDEFINE_EXCLUDE_VARS
|
|
263
|
+
end
|
|
264
|
+
if defined?(self.class::ODBA_EXCLUDE_VARS)
|
|
265
|
+
exc += self.class::ODBA_EXCLUDE_VARS
|
|
260
266
|
end
|
|
267
|
+
exc.map { |v| v.to_sym }
|
|
261
268
|
end
|
|
262
|
-
|
|
269
|
+
|
|
270
|
+
# Returns the odba unique id of this Persistable.
|
|
263
271
|
# If no id had been assigned, this is now done.
|
|
264
272
|
# No attempt is made to store the Persistable in the db.
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
273
|
+
def odba_id
|
|
274
|
+
@odba_id ||= ODBA.cache.next_id
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
def odba_isolated_dump # :nodoc:
|
|
278
|
+
ODBA.marshaller.dump(odba_isolated_twin)
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
# Convenience method equivalent to ODBA.cache.store(self)
|
|
282
|
+
def odba_isolated_store
|
|
283
|
+
@odba_persistent = true
|
|
284
|
+
ODBA.cache.store(self)
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
# Returns a new instance of Stub, which can be used as a stand-in replacement
|
|
288
|
+
# for this Persistable.
|
|
289
|
+
def odba_isolated_stub
|
|
290
|
+
Stub.new(odba_id, nil, self)
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
# Returns a duplicate of this Persistable, for which all connected
|
|
294
|
+
# Persistables have been replaced by a Stub
|
|
295
|
+
def odba_isolated_twin
|
|
296
|
+
# ensure a valid odba_id
|
|
297
|
+
odba_id
|
|
298
|
+
twin = odba_dup
|
|
299
|
+
twin.odba_replace_persistables
|
|
300
|
+
twin.odba_replace_excluded!
|
|
301
|
+
twin
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
# A Persistable instance can be _prefetchable_. This means that the object
|
|
305
|
+
# can be loaded at startup by calling ODBA.cache.prefetch, and that it will
|
|
306
|
+
# never expire from the Cache. The prefetch status can be controlled per
|
|
307
|
+
# instance by setting the instance variable @odba_prefetch, and per class by
|
|
308
|
+
# overriding the module constant ODBA_PREFETCH
|
|
309
|
+
def odba_prefetch?
|
|
297
310
|
@odba_prefetch ||= nil
|
|
298
|
-
|
|
299
|
-
|| (defined?(self
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
311
|
+
@odba_prefetch \
|
|
312
|
+
|| (defined?(self.class::ODBA_PREFETCH) && self.class::ODBA_PREFETCH)
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
def odba_indexable? # :nodoc:
|
|
316
|
+
@odba_indexable \
|
|
317
|
+
|| (defined?(self.class::ODBA_INDEXABLE) && self.class::ODBA_INDEXABLE)
|
|
318
|
+
end
|
|
319
|
+
|
|
305
320
|
# Invoke the update method in each currently associated observer
|
|
306
321
|
# in turn, passing it the given arguments
|
|
307
322
|
def odba_notify_observers(*args)
|
|
308
323
|
odba_observers.each { |obs| obs.odba_update(*args) }
|
|
309
324
|
end
|
|
325
|
+
|
|
310
326
|
def odba_observers
|
|
311
327
|
@odba_observers ||= []
|
|
312
328
|
end
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
329
|
+
|
|
330
|
+
def odba_potentials # :nodoc:
|
|
331
|
+
instance_variables - odba_serializables - odba_exclude_vars
|
|
332
|
+
end
|
|
333
|
+
|
|
316
334
|
def odba_replace!(obj) # :nodoc:
|
|
317
335
|
@odba_observers ||= []
|
|
318
336
|
instance_variables.each { |name|
|
|
319
337
|
instance_variable_set(name, obj.instance_variable_get(name))
|
|
320
338
|
}
|
|
321
339
|
end
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
340
|
+
|
|
341
|
+
def odba_replace_persistables # :nodoc:
|
|
342
|
+
odba_potentials.each { |name|
|
|
343
|
+
var = instance_variable_get(name)
|
|
344
|
+
if var.is_a?(ODBA::Stub)
|
|
326
345
|
var.odba_clear_receiver # ensure we don't leak into the db
|
|
327
346
|
var.odba_container = self # ensure we don't leak into memory
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
347
|
+
elsif var.is_a?(ODBA::Persistable)
|
|
348
|
+
odba_id = var.odba_id
|
|
349
|
+
stub = ODBA::Stub.new(odba_id, self, var)
|
|
350
|
+
instance_variable_set(name, stub)
|
|
351
|
+
end
|
|
352
|
+
}
|
|
353
|
+
odba_serializables.each { |name|
|
|
354
|
+
var = instance_variable_get(name)
|
|
355
|
+
if var.is_a?(ODBA::Stub)
|
|
356
|
+
instance_variable_set(name, var.odba_instance)
|
|
357
|
+
end
|
|
358
|
+
}
|
|
359
|
+
end
|
|
360
|
+
|
|
361
|
+
def odba_replace_stubs(odba_id, substitution) # :nodoc:
|
|
342
362
|
odba_potentials.each { |name|
|
|
343
363
|
var = instance_variable_get(name)
|
|
344
|
-
if
|
|
364
|
+
if var.is_a?(Stub) && odba_id == var.odba_id
|
|
345
365
|
instance_variable_set(name, substitution)
|
|
346
366
|
end
|
|
347
367
|
}
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
end
|
|
357
|
-
if(defined?(self::class::ODBA_SERIALIZABLE))
|
|
358
|
-
srs += self::class::ODBA_SERIALIZABLE
|
|
359
|
-
end
|
|
360
|
-
if RUBY_VERSION >= '1.9'
|
|
361
|
-
srs.map{|s| s.to_sym}
|
|
368
|
+
end
|
|
369
|
+
|
|
370
|
+
def odba_restore(collection = []) # :nodoc:
|
|
371
|
+
end
|
|
372
|
+
|
|
373
|
+
def odba_serializables # :nodoc:
|
|
374
|
+
srs = if defined?(self.class::ODBA_PREDEFINE_SERIALIZABLE)
|
|
375
|
+
self.class::ODBA_PREDEFINE_SERIALIZABLE
|
|
362
376
|
else
|
|
363
|
-
|
|
377
|
+
ODBA_PREDEFINE_SERIALIZABLE
|
|
364
378
|
end
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
379
|
+
if defined?(self.class::ODBA_SERIALIZABLE)
|
|
380
|
+
srs += self.class::ODBA_SERIALIZABLE
|
|
381
|
+
end
|
|
382
|
+
srs.map { |s| s.to_sym }
|
|
383
|
+
end
|
|
384
|
+
|
|
385
|
+
def odba_snapshot(snapshot_level) # :nodoc:
|
|
386
|
+
if snapshot_level > @odba_snapshot_level.to_i
|
|
387
|
+
@odba_snapshot_level = snapshot_level
|
|
388
|
+
odba_isolated_store
|
|
389
|
+
end
|
|
390
|
+
end
|
|
391
|
+
|
|
392
|
+
# Stores this Persistable and recursively all connected unsaved persistables,
|
|
393
|
+
# until no more direcly connected unsaved persistables can be found.
|
|
394
|
+
# The optional parameter _name_ can be used later to retrieve this
|
|
395
|
+
# Persistable using Cache#fetch_named
|
|
396
|
+
def odba_store(name = nil)
|
|
397
|
+
unless name.nil?
|
|
398
|
+
old_name = @odba_name
|
|
399
|
+
@odba_name = name
|
|
400
|
+
end
|
|
401
|
+
odba_store_unsaved
|
|
402
|
+
self
|
|
403
|
+
rescue
|
|
404
|
+
@odba_name = old_name
|
|
405
|
+
raise
|
|
406
|
+
end
|
|
407
|
+
|
|
408
|
+
def odba_store_unsaved # :nodoc:
|
|
409
|
+
@odba_persistent = false
|
|
410
|
+
current_level = [self]
|
|
411
|
+
until current_level.empty?
|
|
412
|
+
next_level = []
|
|
413
|
+
current_level.each { |item|
|
|
414
|
+
if item.odba_unsaved?
|
|
415
|
+
next_level += item.odba_unsaved_neighbors
|
|
416
|
+
item.odba_isolated_store
|
|
417
|
+
end
|
|
418
|
+
}
|
|
419
|
+
current_level = next_level
|
|
420
|
+
end
|
|
421
|
+
end
|
|
422
|
+
|
|
423
|
+
def odba_stubize(obj, opts = {}) # :nodoc:
|
|
424
|
+
return false if frozen?
|
|
425
|
+
id = obj.odba_id
|
|
426
|
+
odba_potentials.each { |name|
|
|
427
|
+
var = instance_variable_get(name)
|
|
428
|
+
# must not be synchronized because of the following if
|
|
429
|
+
# statement (if an object has already been replaced by
|
|
430
|
+
# a stub, it will have the correct id and it
|
|
431
|
+
# will be ignored)
|
|
412
432
|
case var
|
|
413
433
|
when Stub
|
|
414
434
|
# no need to make a new stub
|
|
415
435
|
when Persistable
|
|
416
|
-
|
|
436
|
+
if var.odba_id == id
|
|
417
437
|
stub = ODBA::Stub.new(id, self, obj)
|
|
418
438
|
instance_variable_set(name, stub)
|
|
419
439
|
end
|
|
420
|
-
|
|
421
|
-
|
|
440
|
+
end
|
|
441
|
+
}
|
|
422
442
|
odba_notify_observers(:stubize, id, obj.object_id)
|
|
423
443
|
## allow CacheEntry to retire
|
|
424
444
|
true
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
445
|
+
end
|
|
446
|
+
|
|
447
|
+
# Recursively stores all connected Persistables.
|
|
448
|
+
def odba_take_snapshot
|
|
449
|
+
@odba_snapshot_level ||= 0
|
|
450
|
+
snapshot_level = @odba_snapshot_level.next
|
|
451
|
+
current_level = [self]
|
|
452
|
+
tree_level = 0
|
|
453
|
+
until current_level.empty?
|
|
454
|
+
tree_level += 1
|
|
455
|
+
obj_count = 0
|
|
456
|
+
next_level = []
|
|
457
|
+
current_level.each { |item|
|
|
458
|
+
if item.odba_unsaved?(snapshot_level)
|
|
459
|
+
obj_count += 1
|
|
460
|
+
next_level += item.odba_unsaved_neighbors(snapshot_level)
|
|
461
|
+
item.odba_snapshot(snapshot_level)
|
|
462
|
+
end
|
|
463
|
+
}
|
|
464
|
+
current_level = next_level # .uniq
|
|
465
|
+
end
|
|
466
|
+
end
|
|
467
|
+
|
|
468
|
+
def odba_target_ids # :nodoc:
|
|
469
|
+
odba_potentials.collect { |name|
|
|
470
|
+
var = instance_variable_get(name)
|
|
471
|
+
if var.is_a?(ODBA::Persistable)
|
|
472
|
+
var.odba_id
|
|
473
|
+
end
|
|
474
|
+
}.compact.uniq
|
|
475
|
+
end
|
|
476
|
+
|
|
477
|
+
def odba_unsaved_neighbors(snapshot_level = nil) # :nodoc:
|
|
478
|
+
unsaved = []
|
|
479
|
+
odba_potentials.each { |name|
|
|
480
|
+
item = instance_variable_get(name)
|
|
481
|
+
if item.is_a?(ODBA::Persistable) \
|
|
482
|
+
&& item.odba_unsaved?(snapshot_level)
|
|
483
|
+
unsaved.push(item)
|
|
484
|
+
end
|
|
485
|
+
}
|
|
486
|
+
unsaved
|
|
487
|
+
end
|
|
488
|
+
|
|
489
|
+
def odba_unsaved?(snapshot_level = nil) # :nodoc:
|
|
490
|
+
if snapshot_level.nil?
|
|
491
|
+
!@odba_persistent
|
|
492
|
+
# true
|
|
493
|
+
else
|
|
470
494
|
@odba_snapshot_level ||= 0
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
495
|
+
@odba_snapshot_level.to_i < snapshot_level
|
|
496
|
+
end
|
|
497
|
+
end
|
|
498
|
+
|
|
499
|
+
protected
|
|
500
|
+
|
|
475
501
|
attr_writer :odba_id
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
502
|
+
def odba_replace_excluded!
|
|
503
|
+
odba_exclude_vars.each { |name|
|
|
504
|
+
instance_variable_set(name, nil)
|
|
505
|
+
}
|
|
506
|
+
end
|
|
507
|
+
end
|
|
482
508
|
end
|
|
509
|
+
|
|
483
510
|
class Array # :nodoc: all
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
511
|
+
include ODBA::Persistable
|
|
512
|
+
def odba_collection
|
|
513
|
+
coll = []
|
|
514
|
+
each_with_index { |item, index|
|
|
515
|
+
coll.push([index, item])
|
|
516
|
+
}
|
|
517
|
+
coll
|
|
518
|
+
end
|
|
519
|
+
|
|
520
|
+
def odba_cut_connection(remove_object)
|
|
521
|
+
super
|
|
522
|
+
delete_if { |val| val.eql?(remove_object) }
|
|
523
|
+
end
|
|
524
|
+
|
|
525
|
+
def odba_prefetch?
|
|
526
|
+
super || any? { |item|
|
|
527
|
+
item.respond_to?(:odba_prefetch?) \
|
|
528
|
+
&& item.odba_prefetch?
|
|
529
|
+
}
|
|
530
|
+
end
|
|
531
|
+
|
|
502
532
|
def odba_replace!(obj) # :nodoc:
|
|
503
533
|
super
|
|
504
534
|
replace(obj)
|
|
505
535
|
end
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
536
|
+
|
|
537
|
+
def odba_replace_persistables
|
|
538
|
+
clear
|
|
539
|
+
super
|
|
540
|
+
end
|
|
541
|
+
|
|
542
|
+
def odba_restore(collection = [])
|
|
543
|
+
collection.each { |key, val|
|
|
544
|
+
self[key] = val
|
|
545
|
+
}
|
|
546
|
+
end
|
|
547
|
+
|
|
548
|
+
def odba_unsaved_neighbors(snapshot_level = nil)
|
|
549
|
+
unsaved = super
|
|
550
|
+
each { |item|
|
|
551
|
+
# odba_extend_enumerable(item)
|
|
552
|
+
if item.is_a?(ODBA::Persistable) \
|
|
553
|
+
&& item.odba_unsaved?(snapshot_level)
|
|
554
|
+
unsaved.push(item)
|
|
555
|
+
end
|
|
556
|
+
}
|
|
557
|
+
unsaved
|
|
558
|
+
end
|
|
559
|
+
|
|
560
|
+
def odba_unsaved?(snapshot_level = nil)
|
|
561
|
+
super || (snapshot_level.nil? && any? { |val|
|
|
562
|
+
val.is_a?(ODBA::Persistable) && val.odba_unsaved?
|
|
563
|
+
})
|
|
564
|
+
end
|
|
565
|
+
|
|
566
|
+
def odba_stubize(obj, opts = {}) # :nodoc:
|
|
567
|
+
return false if frozen?
|
|
533
568
|
super
|
|
534
569
|
if opts[:force]
|
|
535
570
|
id = obj.odba_id
|
|
@@ -546,73 +581,83 @@ class Array # :nodoc: all
|
|
|
546
581
|
false
|
|
547
582
|
end
|
|
548
583
|
end
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
584
|
+
|
|
585
|
+
def odba_target_ids
|
|
586
|
+
ids = super
|
|
587
|
+
each { |value|
|
|
588
|
+
if value.is_a?(ODBA::Persistable)
|
|
589
|
+
ids.push(value.odba_id)
|
|
590
|
+
end
|
|
591
|
+
}
|
|
592
|
+
ids.uniq
|
|
593
|
+
end
|
|
558
594
|
end
|
|
595
|
+
|
|
559
596
|
class Hash # :nodoc: all
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
597
|
+
include ODBA::Persistable
|
|
598
|
+
def odba_cut_connection(remove_object)
|
|
599
|
+
super
|
|
600
|
+
delete_if { |key, val|
|
|
601
|
+
key.eql?(remove_object) || val.eql?(remove_object)
|
|
602
|
+
}
|
|
603
|
+
end
|
|
604
|
+
|
|
605
|
+
def odba_collection
|
|
606
|
+
to_a
|
|
607
|
+
end
|
|
608
|
+
|
|
609
|
+
def odba_prefetch?
|
|
610
|
+
super || any? { |item|
|
|
611
|
+
item.respond_to?(:odba_prefetch?) \
|
|
612
|
+
&& item.odba_prefetch?
|
|
613
|
+
}
|
|
614
|
+
end
|
|
615
|
+
|
|
576
616
|
def odba_replace!(obj) # :nodoc:
|
|
577
617
|
super
|
|
578
618
|
replace(obj)
|
|
579
619
|
end
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
620
|
+
|
|
621
|
+
def odba_replace_persistables
|
|
622
|
+
clear
|
|
623
|
+
super
|
|
624
|
+
end
|
|
625
|
+
|
|
626
|
+
def odba_restore(collection = [])
|
|
627
|
+
collection.each { |key, val|
|
|
628
|
+
self[key] = val
|
|
629
|
+
}
|
|
630
|
+
end
|
|
631
|
+
|
|
632
|
+
def odba_unsaved?(snapshot_level = nil)
|
|
633
|
+
super || (snapshot_level.nil? && any? { |key, val|
|
|
634
|
+
val.is_a?(ODBA::Persistable) && val.odba_unsaved? \
|
|
635
|
+
|| key.is_a?(ODBA::Persistable) && key.odba_unsaved?
|
|
636
|
+
})
|
|
637
|
+
end
|
|
638
|
+
|
|
639
|
+
def odba_unsaved_neighbors(snapshot_level = nil)
|
|
640
|
+
unsaved = super
|
|
641
|
+
each { |pair|
|
|
642
|
+
pair.each { |item|
|
|
643
|
+
# odba_extend_enumerable(item)
|
|
644
|
+
if item.is_a?(ODBA::Persistable) \
|
|
645
|
+
&& item.odba_unsaved?(snapshot_level)
|
|
646
|
+
unsaved.push(item)
|
|
647
|
+
end
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
unsaved
|
|
651
|
+
end
|
|
652
|
+
|
|
653
|
+
def odba_stubize(obj, opts = {}) # :nodoc:
|
|
654
|
+
return false if frozen?
|
|
610
655
|
super
|
|
611
656
|
if opts[:force]
|
|
612
657
|
dup = {}
|
|
613
658
|
each do |pair|
|
|
614
659
|
pair.odba_stubize(obj)
|
|
615
|
-
dup.store
|
|
660
|
+
dup.store(*pair)
|
|
616
661
|
end
|
|
617
662
|
replace dup
|
|
618
663
|
true
|
|
@@ -620,16 +665,17 @@ class Hash # :nodoc: all
|
|
|
620
665
|
false
|
|
621
666
|
end
|
|
622
667
|
end
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
668
|
+
|
|
669
|
+
def odba_target_ids
|
|
670
|
+
ids = super
|
|
671
|
+
each { |key, value|
|
|
672
|
+
if value.is_a?(ODBA::Persistable)
|
|
673
|
+
ids.push(value.odba_id)
|
|
674
|
+
end
|
|
675
|
+
if key.is_a?(ODBA::Persistable)
|
|
676
|
+
ids.push(key.odba_id)
|
|
677
|
+
end
|
|
678
|
+
}
|
|
679
|
+
ids.uniq
|
|
680
|
+
end
|
|
635
681
|
end
|