odba 1.0.0

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