odba 1.0.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.
@@ -0,0 +1,187 @@
1
+ #!/usr/bin/env ruby
2
+ #-- Stub -- odba -- 29.04.2004 -- hwyss@ywesee.com rwaltert@ywesee.com mwalder@ywesee.com
3
+
4
+ require 'yaml'
5
+ require 'odba/odba_error'
6
+
7
+ module ODBA
8
+ class Stub # :nodoc: all
9
+ attr_accessor :odba_id, :odba_container
10
+ def initialize(odba_id, odba_container, receiver)
11
+ @odba_id = odba_id
12
+ @odba_container = odba_container
13
+ @odba_class = receiver.class unless receiver.nil?
14
+ @receiver_loaded = true
15
+ end
16
+ def class
17
+ @odba_class ||= odba_instance.class
18
+ end
19
+ def eql?(other)
20
+ @odba_id == other.odba_id || odba_instance.eql?(other)
21
+ end
22
+ def inspect
23
+ "#<ODBA::Stub:#{object_id}##@odba_id @odba_class=#@odba_class @odba_container=#{@odba_container.object_id}##{@odba_container.odba_id}>"
24
+ end
25
+ def is_a?(klass)
26
+ klass == Stub || klass == Persistable || klass == @odba_class \
27
+ || odba_instance.is_a?(klass)
28
+ end
29
+ def odba_clear_receiver
30
+ @receiver = nil
31
+ @receiver_loaded = nil
32
+ end
33
+ def odba_dup
34
+ odba_isolated_stub
35
+ end
36
+ def odba_isolated_stub
37
+ Stub.new(@odba_id, nil, nil)
38
+ end
39
+ def odba_prefetch?
40
+ false
41
+ end
42
+ def odba_receiver(name=nil)
43
+ if(@receiver && !@receiver_loaded)
44
+ warn "stub for #{@receiver.class}:#{@odba_id} was saved with receiver"
45
+ @receiver = nil
46
+ end
47
+ @receiver || begin
48
+ #begin
49
+ @receiver = ODBA.cache.fetch(@odba_id, @odba_container)
50
+ @receiver_loaded = true
51
+ if(@odba_container)
52
+ @odba_container.odba_replace_stubs(@odba_id, @receiver)
53
+ else
54
+ warn "Potential Memory-Leak: stub for #{@receiver.class}##{@odba_id} was saved without container"
55
+ end
56
+ @receiver
57
+ rescue OdbaError => e
58
+ warn "ODBA::Stub was unable to replace #{@odba_class}##{@odba_id} from #{@odba_container.class}:##{@odba_container.odba_id}"
59
+ end
60
+ end
61
+ alias :odba_instance :odba_receiver
62
+ # A stub always references a Persistable that has
63
+ # already been saved.
64
+ def odba_unsaved?(snapshot_level=nil)
65
+ false
66
+ end
67
+ def to_yaml_properties
68
+ ['@odba_id', '@odba_container']
69
+ end
70
+ def to_yaml_type
71
+ "!ruby/object:ODBA::Stub"
72
+ end
73
+ def yaml_initialize(tag, val)
74
+ if RUBY_VERSION >= '1.9'
75
+ val.each { |key, value| instance_variable_set(:"@#{key}", value) }
76
+ else
77
+ val.each { |key, value| instance_variable_set("@#{key}", value) }
78
+ end
79
+ end
80
+ no_override = [
81
+ "class", "is_a?", "__id__", "__send__", "inspect",
82
+ "eql?", "nil?", "respond_to?", "object_id",
83
+ "instance_variables", "instance_variable_get",
84
+ "instance_variable_set", "==",
85
+ ## methods defined in persistable.rb:Object
86
+ "odba_id", "odba_instance", "odba_isolated_stub", "odba_prefetch?",
87
+ ## yaml-methods
88
+ "to_yaml", "taguri", "to_yaml_style", "to_yaml_type",
89
+ "to_yaml_properties", "yaml_initialize",
90
+ ]
91
+ NO_OVERRIDE = no_override.collect { |name| name.to_sym }
92
+ if RUBY_VERSION >= '1.9'
93
+ no_override = NO_OVERRIDE
94
+ end
95
+ override_methods = Object.public_methods - no_override
96
+ override_methods.each { |method|
97
+ src = (method[-1] == ?=) ? <<-EOW : <<-EOS
98
+ def #{method}(args)
99
+ odba_instance.#{method}(args)
100
+ end
101
+ EOW
102
+ def #{method}(*args)
103
+ odba_instance.#{method}(*args)
104
+ end
105
+ EOS
106
+ eval src
107
+ }
108
+ def method_missing(meth_symbol, *args, &block)
109
+ if(NO_OVERRIDE.include?(meth_symbol))
110
+ super
111
+ else
112
+ odba_instance.send(meth_symbol, *args, &block)
113
+ end
114
+ end
115
+ def respond_to?(msg_id, *args)
116
+ case msg_id
117
+ when :_dump, :marshal_dump
118
+ false
119
+ when *NO_OVERRIDE
120
+ super
121
+ else
122
+ odba_instance.respond_to?(msg_id, *args)
123
+ end
124
+ end
125
+ ## FIXME
126
+ # implement full hash/array access - separate collection stub?
127
+ def [](*args, &block)
128
+ if(@odba_class == Hash \
129
+ && !ODBA.cache.include?(@odba_id))
130
+ ODBA.cache.fetch_collection_element(@odba_id, args.first)
131
+ end || method_missing(:[], *args, &block)
132
+ end
133
+ def ==(other)
134
+ @odba_id == other.odba_id || odba_instance == other
135
+ end
136
+ end
137
+ end
138
+
139
+ class Array # :nodoc: all
140
+ alias :_odba_amp :&
141
+ def &(stub)
142
+ self._odba_amp(stub.odba_instance)
143
+ end
144
+ alias :_odba_plus :+
145
+ def +(stub)
146
+ self._odba_plus(stub.odba_instance)
147
+ end
148
+ alias :_odba_minus :-
149
+ def -(stub)
150
+ self._odba_minus(stub.odba_instance)
151
+ end
152
+ alias :_odba_weight :<=>
153
+ def <=>(stub)
154
+ self._odba_weight(stub.odba_instance)
155
+ end
156
+ alias :_odba_equal? :==
157
+ def ==(stub)
158
+ self._odba_equal?(stub.odba_instance)
159
+ end
160
+ alias :_odba_union :|
161
+ def |(stub)
162
+ self._odba_union(stub.odba_instance)
163
+ end
164
+ ['concat', 'replace', 'include?'].each { |method|
165
+ #['concat', 'replace'].each { |method|
166
+ eval <<-EOS
167
+ alias :_odba_#{method} :#{method}
168
+ def #{method}(stub)
169
+ self._odba_#{method}(stub.odba_instance)
170
+ end
171
+ EOS
172
+ }
173
+ end
174
+ class Hash # :nodoc: all
175
+ alias :_odba_equal? :==
176
+ def ==(stub)
177
+ self._odba_equal?(stub.odba_instance)
178
+ end
179
+ ['merge', 'merge!', 'replace'].each { |method|
180
+ eval <<-EOS
181
+ alias :_odba_#{method} :#{method}
182
+ def #{method}(stub)
183
+ self._odba_#{method}(stub.odba_instance)
184
+ end
185
+ EOS
186
+ }
187
+ end
@@ -0,0 +1,6 @@
1
+ CREATE TABLE collection (
2
+ odba_id integer NOT NULL,
3
+ key text,
4
+ value text,
5
+ PRIMARY KEY(odba_id, key)
6
+ );
@@ -0,0 +1,6 @@
1
+ -- all Tables of the odba project
2
+ \i object.sql
3
+ \i object_connection.sql
4
+ \i collection.sql
5
+ \i update_object.sql
6
+ \i ensure_object_connection.sql
@@ -0,0 +1,8 @@
1
+ CREATE TABLE object (
2
+ odba_id integer NOT NULL,
3
+ content text,
4
+ name text,
5
+ prefetchable boolean,
6
+ PRIMARY KEY(odba_id),
7
+ UNIQUE(name)
8
+ );
@@ -0,0 +1,7 @@
1
+ CREATE TABLE object_connection (
2
+ origin_id integer,
3
+ target_id integer,
4
+ PRIMARY KEY(origin_id, target_id)
5
+ );
6
+ CREATE INDEX target_id_index ON object_connection (target_id);
7
+ CREATE INDEX origin_id_index ON object_connection (origin_id);
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+ # suite.rb -- oddb -- 20.11.2002 -- hwyss@ywesee.com
3
+
4
+ $: << File.expand_path(File.dirname(__FILE__))
5
+
6
+ Dir.foreach(File.dirname(__FILE__)) { |file|
7
+ require file if /^test_.*\.rb$/o.match(file)
8
+ }
@@ -0,0 +1,108 @@
1
+ #!/usr/bin/env ruby
2
+ # TestArray -- odba -- 30.01.2007 -- hwyss@ywesee.com
3
+
4
+ $: << File.dirname(__FILE__)
5
+ $: << File.expand_path('../lib', File.dirname(__FILE__))
6
+
7
+ require 'test/unit'
8
+ require 'flexmock'
9
+ require 'odba/persistable'
10
+ require 'odba/stub'
11
+ require 'odba/odba'
12
+
13
+ module ODBA
14
+ class TestArray < Test::Unit::TestCase
15
+ include FlexMock::TestCase
16
+ class ODBAContainer
17
+ include ODBA::Persistable
18
+ attr_accessor :non_replaceable, :replaceable, :array, :odba_id
19
+ end
20
+ class Container
21
+ attr_accessor :content
22
+ end
23
+ def setup
24
+ @array = Array.new
25
+ ODBA.storage = flexmock("storage")
26
+ ODBA.marshaller = flexmock("marshaller")
27
+ ODBA.cache = flexmock("cache")
28
+ end
29
+ def test_odba_cut_connection
30
+ remove_obj = Object
31
+ remove_obj.extend(ODBA::Persistable)
32
+ remove_obj.instance_variable_set('@odba_id', 2)
33
+ receiver = ODBA::Stub.new(2,self, remove_obj)
34
+ array = Array.new
35
+ array.push(receiver)
36
+ assert_equal(0, array.odba_cut_connection(remove_obj).size)
37
+ end
38
+ def test_odba_unsaved_neighbors_array
39
+ rep1 = ODBAContainer.new
40
+ rep2 = ODBAContainer.new
41
+ @array.push(rep1)
42
+ @array.push(rep2)
43
+ result = @array.odba_unsaved_neighbors(1)
44
+ assert_equal([rep1, rep2], result)
45
+ end
46
+ def test_array_replacement
47
+ replacement = flexmock('replacement')
48
+ replacement2 = flexmock('replacement2')
49
+ stub = flexmock('stub')
50
+ stub2 = flexmock('stub2')
51
+ foo = flexmock("foo")
52
+ @array.push(stub)
53
+ @array.push(stub2)
54
+ @array.odba_restore([[0,replacement], [1,replacement2]])
55
+ assert_equal(replacement, @array[0])
56
+ assert_equal(replacement2, @array[1])
57
+ end
58
+ def test_odba_replace_persistables_array
59
+ replaceable = flexmock("replaceable")
60
+ replaceable2 = flexmock("replaceable2")
61
+ @array.push(replaceable)
62
+ @array.push(replaceable2)
63
+ @array.odba_replace_persistables
64
+ #size is 0 because we store empty array in the db
65
+ # content of the array is in the collection table
66
+ assert_equal(0, @array.size)
67
+ end
68
+ def test_odba_unsaved_array_true
69
+ val = flexmock("val")
70
+ @array.instance_variable_set("@odba_persistent", true)
71
+ @array.push(val)
72
+ val.should_receive(:is_a?).and_return { |klass| true }
73
+ val.should_receive(:odba_unsaved?).and_return { true }
74
+ assert_equal(true, @array.odba_unsaved?)
75
+ end
76
+ def test_odba_collection
77
+ @array.push('foo', 'bar')
78
+ assert_equal([[0,'foo'], [1, 'bar']], @array.odba_collection)
79
+ end
80
+ def test_odba_replace
81
+ modified = ['foo']
82
+ reloaded = modified.dup
83
+ modified.push('bar')
84
+ modified.odba_replace!(reloaded)
85
+ assert_equal(reloaded, modified)
86
+ end
87
+ def test_odba_target_ids
88
+ o = ODBAContainer.new
89
+ o.odba_id = 1
90
+ p = ODBAContainer.new
91
+ p.odba_id = 2
92
+ q = ODBAContainer.new
93
+ q.odba_id = 3
94
+ @array.push(p, q)
95
+ @array.instance_variable_set('@foo', o)
96
+ assert_equal([1,2,3], @array.odba_target_ids)
97
+ end
98
+ def test_stubize
99
+ item = ODBAContainer.new
100
+ @array.push(item)
101
+ @array.odba_stubize(item)
102
+ first = @array.first
103
+ assert_equal false, first.is_a?(ODBA::Stub)
104
+ assert @array.include?(item)
105
+ assert_equal false, first.is_a?(ODBA::Stub)
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,725 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $: << File.dirname(__FILE__)
4
+ $: << File.expand_path('../lib/', File.dirname(__FILE__))
5
+
6
+ require 'test/unit'
7
+ require 'flexmock'
8
+ require 'odba/cache'
9
+ require 'odba/cache_entry'
10
+ require 'odba/persistable'
11
+ require 'odba/index'
12
+ require 'odba/index_definition'
13
+ require 'odba/odba_error'
14
+ require 'odba/odba'
15
+
16
+ module ODBA
17
+ class Cache
18
+ CLEANING_INTERVAL = 0
19
+ MAIL_RECIPIENTS = []
20
+ MAIL_FROM = "test@testfirst.local"
21
+ attr_accessor :cleaner, :fetched, :cache_entry_factory, :prefetched
22
+ attr_writer :indices
23
+ public :load_object
24
+ end
25
+ class TestCache < Test::Unit::TestCase
26
+ include FlexMock::TestCase
27
+ class ODBAContainer
28
+ include ODBA::Persistable
29
+ attr_accessor :odba_connection, :odba_id
30
+ end
31
+ def setup
32
+ @storage = ODBA.storage = flexmock("storage")
33
+ @marshal = ODBA.marshaller = flexmock("marshaller")
34
+ ODBA.cache = @cache = ODBA::Cache.instance
35
+ @cache.fetched = {}
36
+ @cache.prefetched = {}
37
+ @cache.indices = {}
38
+ end
39
+ def teardown
40
+ super
41
+ ODBA.storage = nil
42
+ ODBA.marshaller = nil
43
+ @cache.fetched.clear
44
+ @cache.prefetched.clear
45
+ @cache.indices.clear
46
+ end
47
+ def test_fetch_named_ok
48
+ obj = flexmock
49
+ obj.instance_variable_set("@odba_name", 'the_name')
50
+ obj.instance_variable_set("@odba_id", 2)
51
+ @marshal.should_receive(:load).with('dump2').and_return(obj)
52
+ @storage.should_receive(:restore_named).and_return('dump2')
53
+ @storage.should_receive(:restore_collection).and_return([])
54
+ load_1 = @cache.fetch_named('the_name', nil)
55
+ assert_instance_of(FlexMock, load_1)
56
+ assert_equal('the_name', load_1.odba_name)
57
+ assert_equal(2, load_1.odba_id)
58
+ load_2 = @cache.fetch_named('the_name', nil)
59
+ assert_equal(load_1, load_2)
60
+ load_3 = @cache.fetch(2, nil)
61
+ assert_equal(load_1, load_3)
62
+ end
63
+ def test_bulk_fetch_load_all
64
+ old_marshal = ODBA.marshaller
65
+ ODBA.marshaller = marshal = flexmock('Marshal')
66
+ array = [2, 3]
67
+ obj1 = Object.new
68
+ obj1.instance_variable_set("@odba_id", 2)
69
+ obj1.instance_variable_set("@odba_prefetch", false)
70
+ obj1.instance_variable_set("@odba_name", nil)
71
+ obj2 = Object.new
72
+ obj2.instance_variable_set("@odba_id", 3)
73
+ obj2.instance_variable_set("@odba_prefetch", true)
74
+ obj2.instance_variable_set("@odba_name", nil)
75
+ dump_1 = Marshal.dump(obj1)
76
+ dump_2 = Marshal.dump(obj2)
77
+ @storage.should_receive(:bulk_restore)\
78
+ .and_return([[2, dump_1],[3, dump_2]])
79
+ dumps = [dump_1, dump_2]
80
+ objs = [obj1, obj2]
81
+ marshal.should_receive(:load).and_return { |dump|
82
+ assert_equal(dumps.shift, dump)
83
+ objs.shift
84
+ }
85
+ @storage.should_receive(:restore_collection).and_return([])
86
+ loaded = @cache.bulk_fetch(array, nil)
87
+ assert_equal([obj1, obj2], loaded)
88
+ assert_equal([2], @cache.fetched.keys)
89
+ assert_equal([3], @cache.prefetched.keys)
90
+ assert_instance_of(CacheEntry, @cache.fetched[2])
91
+ assert_instance_of(CacheEntry, @cache.prefetched[3])
92
+ ensure
93
+ ODBA.marshaller = old_marshal
94
+ end
95
+ def test_bulk_fetch
96
+ old_marshal = ODBA.marshaller
97
+ ODBA.marshaller = marshal = flexmock('Marshal')
98
+ array = [2, 3, 7]
99
+ baz = flexmock('loaded')
100
+ baz.should_receive(:odba_add_reference).and_return { |odba_caller| }
101
+ baz.should_receive(:odba_object).and_return { 'foo' }
102
+ @cache.fetched = {
103
+ 7 => baz
104
+ }
105
+ obj1 = Object.new
106
+ obj1.instance_variable_set("@odba_id", 2)
107
+ obj1.instance_variable_set("@odba_prefetch", false)
108
+ obj1.instance_variable_set("@odba_name", nil)
109
+ obj2 = Object.new
110
+ obj2.instance_variable_set("@odba_id", 3)
111
+ obj2.instance_variable_set("@odba_prefetch", false)
112
+ obj2.instance_variable_set("@odba_name", nil)
113
+ dump_1 = Marshal.dump(obj1)
114
+ dump_2 = Marshal.dump(obj2)
115
+ dumps = [dump_1, dump_2]
116
+ objs = [obj1, obj2]
117
+ marshal.should_receive(:load).and_return { |dump|
118
+ assert_equal(dumps.shift, dump)
119
+ objs.shift
120
+ }
121
+ @storage.should_receive(:bulk_restore).and_return([[2, dump_1],[3, dump_2]])
122
+ @storage.should_receive(:restore_collection).and_return([])
123
+ @cache.bulk_fetch(array, nil)
124
+ assert_equal(true, @cache.fetched.has_key?(2))
125
+ assert_equal(3, @cache.fetched.size)
126
+ assert_equal(true, @cache.fetched.has_key?(3))
127
+ ODBA.marshaller = old_marshal
128
+ end
129
+ def test_bulk_restore_in_fetchedadd_caller
130
+ obj = Object.new
131
+ cache_entry = flexmock('cache_entry')
132
+ cache_entry.should_receive(:odba_object).and_return {
133
+ obj
134
+ }
135
+ cache_entry.should_receive(:_odba_object).and_return {
136
+ obj
137
+ }
138
+ cache_entry.should_receive(:odba_add_reference).and_return {|caller|
139
+ #this assert_equal is interesting
140
+ assert_equal('Reference', caller)
141
+ }
142
+ @cache.fetched.store(1, cache_entry)
143
+ rows = [[1, '']]
144
+ retrieved = @cache.bulk_restore(rows, 'Reference')
145
+ end
146
+ def test_clean
147
+ obj1 = flexmock
148
+ obj2 = flexmock
149
+ @cache.fetched.store(2, obj1)
150
+ @cache.fetched.store(3, obj2)
151
+ assert_equal(2, @cache.fetched.size)
152
+ obj1.should_receive(:odba_old?).and_return { true }
153
+ obj1.should_receive(:odba_retire).and_return { }
154
+ obj2.should_receive(:odba_old?).and_return { false }
155
+ @cache.clean
156
+ assert_equal(2, @cache.fetched.size)
157
+ end
158
+ def test_clean__prefetched
159
+ obj1 = flexmock
160
+ obj2 = flexmock
161
+ @cache.prefetched.store(2, obj1)
162
+ @cache.prefetched.store(3, obj2)
163
+ assert_equal(2, @cache.prefetched.size)
164
+ obj1.should_receive(:ready_to_destroy?).and_return { false }
165
+ obj1.should_receive(:odba_old?).and_return { true }
166
+ obj1.should_receive(:odba_retire).and_return { }
167
+ obj2.should_receive(:ready_to_destroy?).and_return { false }
168
+ obj2.should_receive(:odba_old?).and_return { false }
169
+ @cache.clean_prefetched
170
+ assert_equal(2, @cache.prefetched.size)
171
+ end
172
+ def test_clear
173
+ value = flexmock("value")
174
+ obj = flexmock('object')
175
+ prefetched = flexmock('prefetched')
176
+ @cache.fetched.store(12, value)
177
+ @cache.prefetched.store(13, prefetched)
178
+ assert_equal(2, @cache.size)
179
+ @cache.clear
180
+ assert_equal(0, @cache.size)
181
+ end
182
+ def test_fetch_named_block
183
+ restore = flexmock("restore")
184
+ caller = flexmock("caller")
185
+ caller2 = flexmock("caller2")
186
+ @storage.should_receive(:restore_named).and_return { |name| }
187
+ restore.should_receive(:odba_name=).and_return { |name| }
188
+ restore.should_receive(:odba_store).and_return { |obj| }
189
+ result = @cache.fetch_named("foo", caller2) {
190
+ restore
191
+ }
192
+ assert_equal(restore, result)
193
+ end
194
+ def prepare_fetch(id, receiver)
195
+ @storage.should_receive(:restore).and_return { |odba_id|
196
+ assert_equal(id, odba_id)
197
+ odba_id
198
+ }
199
+ @marshal.should_receive(:load).and_return { receiver }
200
+ if(receiver.is_a?(FlexMock))
201
+ receiver.should_receive(:odba_restore).and_return {}
202
+ receiver.should_receive(:odba_name).and_return {}
203
+ end
204
+ end
205
+ def test_fetch_has_name
206
+ caller1 = flexmock('Caller1')
207
+ caller2 = flexmock('Caller2')
208
+ caller3 = flexmock('Caller3')
209
+ receiver = flexmock('Receiver')
210
+ @storage.should_receive(:restore).and_return { |odba_id|
211
+ assert_equal(23, odba_id)
212
+ odba_id
213
+ }
214
+ @marshal.should_receive(:load).and_return {|dump|
215
+ receiver
216
+ }
217
+ @storage.should_receive(:restore_collection).and_return {|*args|
218
+ []
219
+ }
220
+ receiver.instance_variable_set("@odba_id", 23)
221
+ receiver.instance_variable_set("@odba_name", 'name')
222
+ first_fetch = @cache.fetch(23, caller1)
223
+ assert_equal(receiver, first_fetch)
224
+ assert_equal(2, @cache.fetched.size)
225
+ assert(@cache.fetched.include?('name'))
226
+ second_fetch = @cache.fetch(23, caller2)
227
+ assert_equal(receiver, second_fetch)
228
+ named_fetch = @cache.fetch_named('name', caller3)
229
+ assert_equal(receiver, named_fetch)
230
+ end
231
+ def test_fetch_error
232
+ receiver = flexmock
233
+ @storage.should_receive(:restore).and_return { |odba_id|
234
+ nil
235
+ }
236
+ assert_raises(OdbaError) {
237
+ @cache.load_object(23, receiver)
238
+ }
239
+ end
240
+ def test_fetch__adds_reference
241
+ obj = flexmock
242
+ obj.instance_variable_set("@odba_id", 23)
243
+ obj.instance_variable_set("@odba_prefetch", false)
244
+ @storage.should_receive(:restore).and_return { |id|
245
+ assert_equal(23, id)
246
+ 'dump'
247
+ }
248
+ @storage.should_receive(:restore_collection).and_return { |id|
249
+ []
250
+ }
251
+ @marshal.should_receive(:load).and_return { |dump|
252
+ assert_equal('dump', dump)
253
+ obj
254
+ }
255
+ callr = flexmock
256
+ res = @cache.fetch(23, callr)
257
+ cache_entry = @cache.fetched[23]
258
+ assert_instance_of(CacheEntry, cache_entry)
259
+ assert_equal([callr.object_id], cache_entry.accessed_by.keys)
260
+ assert_equal(obj, res)
261
+ ## test for duplicates
262
+ @cache.fetch(23, callr)
263
+ assert_equal([callr.object_id], cache_entry.accessed_by.keys)
264
+ end
265
+ def test_store
266
+ cont = flexmock('CacheEntry')
267
+ @cache.fetched.store(3, cont)
268
+ save_obj = flexmock("save_obj")
269
+ save_obj.should_receive(:odba_target_ids).and_return([3])
270
+ save_obj.should_receive(:odba_observers).and_return { [] }
271
+ prepare_store([save_obj])
272
+ save_obj.should_receive(:odba_add_observer)
273
+ cont.should_receive(:odba_add_reference).with(save_obj)\
274
+ .and_return { assert(true) }
275
+ @cache.store(save_obj)
276
+ end
277
+ def test_store_collection_elements
278
+ old_mar = ODBA.marshaller
279
+ ODBA.marshaller = Marshal
280
+
281
+ old_collection = [['key1', 'val1'], ['key2', 'val2']]
282
+ new_collection = [['key2', 'val2'], ['key3', 'val3']]
283
+
284
+ cache_entry = flexmock('cache_entry')
285
+ cache_entry.should_receive(:collection).and_return { old_collection }
286
+ cache_entry.should_receive(:collection=).and_return { |col|
287
+ assert_equal(new_collection, col)
288
+ }
289
+
290
+ @storage.should_receive(:restore_collection).and_return {
291
+ old_collection.collect { |key, val|
292
+ [Marshal.dump(key.odba_isolated_stub),
293
+ Marshal.dump(val.odba_isolated_stub)]
294
+ }
295
+ }
296
+ @storage.should_receive(:collection_remove).and_return { |odba_id, key|
297
+ assert_equal(54, odba_id)
298
+ assert_equal(Marshal.dump('key1'.odba_isolated_stub), key)
299
+ }
300
+ @storage.should_receive(:collection_store).and_return { |odba_id, key, value|
301
+ assert_equal(54, odba_id)
302
+ assert_equal(Marshal.dump('key3'.odba_isolated_stub), key)
303
+ assert_equal(Marshal.dump('val3'.odba_isolated_stub), value)
304
+ }
305
+
306
+ @cache.fetched = {
307
+ 54 => cache_entry
308
+ }
309
+
310
+ obj = flexmock('Obj')
311
+ obj.should_receive(:odba_id).and_return { 54 }
312
+ obj.should_receive(:odba_collection).and_return { new_collection }
313
+ @cache.store_collection_elements(obj)
314
+ ensure
315
+ ODBA.marshaller = old_mar
316
+ end
317
+ def test_store_object_connections
318
+ @storage.should_receive(:ensure_object_connections).and_return { |id,target_ids|
319
+ assert_equal(4, id)
320
+ assert_equal([1,2], target_ids)
321
+ }
322
+ @cache.store_object_connections(4, [1,2])
323
+ end
324
+ def test_load__and_restore_object
325
+ caller1 = flexmock
326
+ loaded = flexmock
327
+ @storage.should_receive(:restore).and_return { |odba_id|
328
+ assert_equal(23, odba_id)
329
+ 'dump'
330
+ }
331
+ @marshal.should_receive(:load).and_return { |dump|
332
+ assert_equal('dump', dump)
333
+ loaded
334
+ }
335
+ @storage.should_receive(:restore_collection).and_return { [] }
336
+
337
+ loaded.instance_variable_set('@odba_id', 23)
338
+ @cache.load_object(23, caller1)
339
+ end
340
+ def test_prefetch
341
+ foo = flexmock("foo")
342
+ @storage.should_receive(:restore_prefetchable).and_return {
343
+ [[2, foo]]
344
+ }
345
+ prepare_bulk_restore([foo])
346
+ assert_nothing_raised {
347
+ @cache.prefetch
348
+ }
349
+ end
350
+ def test_fill_index
351
+ foo = flexmock("foo")
352
+ foo.should_receive(:fill).and_return { |target|
353
+ assert_equal("baz", target)
354
+ }
355
+ @cache.indices = {
356
+ "foo" => foo
357
+ }
358
+ @cache.fill_index("foo", "baz")
359
+ end
360
+ def test_create_index
361
+ df = IndexDefinition.new
362
+ df.index_name = 'index'
363
+ indices = flexmock('indices')
364
+ indices.should_receive(:store).and_return { |key, val|
365
+ assert_instance_of(Index, val)
366
+ }
367
+ indices.should_receive(:odba_store_unsaved).times(1)
368
+ @cache.instance_variable_set('@indices', indices)
369
+ @storage.should_receive(:transaction).and_return { |block| block.call }
370
+ @storage.should_receive(:create_index).times(1)
371
+ index = @cache.create_index(df, flexmock)
372
+ assert_instance_of(Index, index)
373
+ @cache.instance_variable_set('@indices', {})
374
+ end
375
+ def test_create_index__fulltext
376
+ df = IndexDefinition.new
377
+ df.index_name = 'index'
378
+ df.fulltext = true
379
+ indices = flexmock('indices')
380
+ indices.should_receive(:store).and_return { |key, val|
381
+ assert_instance_of(FulltextIndex, val)
382
+ }
383
+ indices.should_receive(:odba_store_unsaved).times(1)
384
+ @cache.instance_variable_set('@indices', indices)
385
+ @storage.should_receive(:transaction).and_return { |block| block.call }
386
+ @storage.should_receive(:create_fulltext_index).times(1)
387
+ index = @cache.create_index(df, flexmock)
388
+ assert_instance_of(FulltextIndex, index)
389
+ @cache.instance_variable_set('@indices', {})
390
+ end
391
+ def test_create_index__condition
392
+ df = IndexDefinition.new
393
+ df.index_name = 'index'
394
+ df.resolve_search_term = { }
395
+ indices = flexmock('indices')
396
+ indices.should_receive(:store).and_return { |key, val|
397
+ assert_instance_of(ConditionIndex, val)
398
+ }
399
+ indices.should_receive(:odba_store_unsaved).times(1)
400
+ @cache.instance_variable_set('@indices', indices)
401
+ @storage.should_receive(:transaction).and_return { |block| block.call }
402
+ @storage.should_receive(:create_condition_index).times(1)
403
+ index = @cache.create_index(df, flexmock)
404
+ assert_instance_of(ConditionIndex, index)
405
+ @cache.instance_variable_set('@indices', {})
406
+ end
407
+ def prepare_fetch_collection(obj)
408
+ end
409
+ def prepare_store(store_array, &block)
410
+ store_array.each { |mock|
411
+ # store
412
+ mock.should_receive(:odba_id).and_return { || }
413
+ mock.should_receive(:odba_name).and_return { || }
414
+ mock.should_receive(:odba_notify_observers).and_return { |key, id1, id2|
415
+ assert_equal(:store, key)
416
+ }
417
+ mock.should_receive(:odba_isolated_dump).and_return { || }
418
+
419
+ # store_collection_elements
420
+ mock.should_receive(:odba_id).and_return { || }
421
+ mock.should_receive(:odba_collection).and_return {|| []}
422
+ mock.should_receive(:odba_id).and_return { || }
423
+
424
+ # store
425
+ mock.should_receive(:odba_prefetch?).and_return { || }
426
+
427
+ # store_object_connections
428
+ mock.should_receive(:odba_target_ids).and_return { || [] }
429
+
430
+ # update_indices
431
+ mock.should_receive(:odba_indexable?).and_return {}
432
+
433
+ # store_cache_entry
434
+ mock.should_receive(:odba_prefetch?).and_return { || }
435
+ mock.should_receive(:odba_collection).and_return { || [] }
436
+
437
+ @storage.should_receive(:restore_collection).and_return { [] }
438
+ if(block)
439
+ @storage.should_receive(:store, &block).and_return
440
+ else
441
+ @storage.should_receive(:store).and_return {
442
+ assert(true)
443
+ }
444
+ end
445
+ @storage.should_receive(:ensure_object_connections).and_return {|*args|}
446
+ }
447
+ end
448
+ def test_delete
449
+ delete_item = ODBAContainer.new
450
+ delete_item.odba_id = 1
451
+ origin_obj = ODBAContainer.new
452
+ origin_obj.odba_id = 2
453
+ origin_obj.odba_connection = delete_item
454
+ @cache.fetched.store(1, delete_item)
455
+ @storage.should_receive(:retrieve_connected_objects).and_return { |id|
456
+ [[2]]
457
+ }
458
+ prepare_fetch(2, origin_obj)
459
+ @storage.should_receive(:restore_collection).and_return { |*args|
460
+ []
461
+ }
462
+ @storage.should_receive(:store).and_return { |id, dump, name, prefetch, klass| }
463
+ @storage.should_receive(:ensure_object_connections).and_return { }
464
+ @storage.should_receive(:delete_persistable).and_return { |id| }
465
+ @marshal.should_receive(:dump).and_return { |ob| "foo"}
466
+ @cache.delete(delete_item)
467
+ assert_equal(1, @cache.fetched.size)
468
+ assert_equal(nil, origin_obj.odba_connection)
469
+ end
470
+ def prepare_delete(mock, name, id)
471
+ mock.should_receive(:odba_id).and_return { id }
472
+ mock.should_receive(:odba_name).and_return { name }
473
+ mock.should_receive(:odba_notify_observers).and_return { |key, id1, id2|
474
+ assert_equal(:delete, key)
475
+ }
476
+ @storage.should_receive(:retrieve_connected_objects).and_return { |id|
477
+ []
478
+ }
479
+ mock.should_receive(:origin_class?).and_return { true }
480
+ mock.should_receive(:odba_id).and_return { id }
481
+ @storage.should_receive(:delete_persistable).and_return { |id_arg|
482
+ assert_equal(id, id_arg)
483
+ }
484
+ @storage.should_receive(:delete_index_element).and_return { }
485
+ end
486
+ def prepare_bulk_restore(rows)
487
+ rows.each { |odba_mock|
488
+ ## according to recent changes, objects are extended with
489
+ # ODBA::Persistable after loading - this enables ad-hoc storing
490
+ # but messes up loads of tests
491
+ @marshal.should_receive(:load).and_return { |dump|
492
+ odba_mock.instance_variable_set('@odba_id', 2)
493
+ odba_mock.instance_variable_set('@odba_prefetch', true)
494
+ odba_mock
495
+ }
496
+ @storage.should_receive(:restore_collection).and_return {|*args|
497
+ []
498
+ }
499
+ }
500
+ end
501
+ def test_retrieve_from_index
502
+ foo = flexmock
503
+ index = flexmock("bar_index")
504
+ @cache.indices["index_name"] = index
505
+ index.should_receive(:fetch_ids).and_return { |search_term, meta|
506
+ assert_equal('search term', search_term)
507
+ assert_nil(meta)
508
+ [2]
509
+ }
510
+ @storage.should_receive(:bulk_restore).and_return {
511
+ [[2, 'dump']]
512
+ }
513
+ prepare_bulk_restore([foo])
514
+ @cache.retrieve_from_index("index_name", "search term")
515
+ end
516
+ def test_update_indices
517
+ index = flexmock("index")
518
+ bar = flexmock("bar")
519
+ bar.should_receive(:odba_indexable?).and_return { true }
520
+ @cache.indices = {
521
+ "foo" => index
522
+ }
523
+ index.should_receive(:update).and_return { |obj|
524
+ assert_equal(bar, obj)
525
+ }
526
+ @cache.update_indices(bar)
527
+ end
528
+ def test_delete_index_element
529
+ foo = flexmock("foo")
530
+ bar = flexmock("bar")
531
+ @cache.indices = {
532
+ "foo" => foo
533
+ }
534
+ foo.should_receive(:delete).with(bar).times(1).and_return {
535
+ assert(true)
536
+ }
537
+ @cache.delete_index_element(bar)
538
+ end
539
+ def test_drop_index
540
+ @storage.should_receive(:transaction).and_return { |block| block.call }
541
+ @storage.should_receive(:drop_index).and_return { |index_name|
542
+ assert_equal("foo_index", index_name)
543
+ }
544
+ index = flexmock("index")
545
+ index.should_receive(:delete).with(index)
546
+ prepare_delete(index, "foo", 2)
547
+ @cache.indices.store("foo_index", index)
548
+ @cache.drop_index("foo_index")
549
+ end
550
+ def test_drop_indices
551
+ @storage.should_receive(:transaction).and_return { |block| block.call}
552
+ @storage.should_receive(:drop_index).and_return {|index_name|
553
+ assert_equal("foo_index", index_name)
554
+ }
555
+ index = flexmock("index")
556
+ index.should_receive(:delete).with(index)
557
+ prepare_delete(index, "foo", 2)
558
+ @cache.indices.store("foo_index", index)
559
+ @cache.drop_indices
560
+ end
561
+ def test_fetch_collection_element
562
+ key_dump = Marshal.dump('foo')
563
+ @storage.should_receive(:collection_fetch).and_return { |odba_id, key|
564
+ assert_equal(12, odba_id)
565
+ assert_equal(key_dump, key)
566
+ 'val_dump'
567
+ }
568
+ @marshal.should_receive(:dump).and_return { |key|
569
+ assert_equal('foo', key)
570
+ key_dump
571
+ }
572
+ @marshal.should_receive(:load).and_return { |dump|
573
+ assert_equal('val_dump', dump)
574
+ 'val'
575
+ }
576
+ res = @cache.fetch_collection_element(12, 'foo')
577
+ assert_equal('val', res)
578
+ end
579
+ def test_transaction
580
+ o1 = Object.new
581
+ o1.instance_variable_set('@odba_id', 1)
582
+ o2 = Object.new
583
+ o3 = Object.new
584
+ o1.extend(ODBA::Persistable)
585
+ o2.extend(ODBA::Persistable)
586
+ o2.odba_name = 'name2'
587
+ o3.extend(ODBA::Persistable)
588
+ o4 = o1.odba_dup
589
+ @storage.should_receive(:transaction).and_return { |block| block.call }
590
+
591
+ ## store o1
592
+ @marshal.should_receive(:dump).times(3).and_return { |obj|
593
+ "dump%i" % obj.odba_id
594
+ }
595
+ next_id = 1
596
+ @storage.should_receive(:next_id).and_return { next_id += 1 }
597
+ @storage.should_receive(:store).with(1,'dump1',nil,nil,Object)\
598
+ .times(1).and_return { assert(true) }
599
+ @storage.should_receive(:ensure_object_connections)\
600
+ .with(1,[2]).times(1).and_return { assert(true) }
601
+
602
+ ## store o2
603
+ @storage.should_receive(:restore_collection).with(2)\
604
+ .times(1).and_return([])
605
+ @storage.should_receive(:store)\
606
+ .with(2,'dump2','name2',nil,Object)\
607
+ .times(1).and_return { assert(true) }
608
+ @storage.should_receive(:ensure_object_connections)\
609
+ .with(2,[3]).times(1).and_return { assert(true) }
610
+
611
+ ## store o3 and raise
612
+ @storage.should_receive(:restore_collection).with(3)\
613
+ .times(1).and_return([])
614
+ ## at this stage 1 and 2 (and 'name2') are stored:
615
+ @storage.should_receive(:store)\
616
+ .with(3,'dump3',nil,nil,Object)\
617
+ .times(1).and_return { raise "trigger rollback" }
618
+
619
+ ## rollback
620
+ @storage.should_receive(:restore).with(2)\
621
+ .times(1).and_return(nil)
622
+ @storage.should_receive(:restore).with(1)\
623
+ .times(1).and_return('dump1')
624
+ @storage.should_receive(:restore_collection).with(1)\
625
+ .times(2).and_return([])
626
+ @marshal.should_receive(:load).with('dump1')\
627
+ .times(1).and_return(o4)
628
+ @cache.fetched.store(1, ODBA::CacheEntry.new(o1))
629
+ assert_raises(RuntimeError) {
630
+ ODBA.transaction {
631
+ o2.instance_variable_set('@other', o3)
632
+ o1.instance_variable_set('@other', o2)
633
+ o1.odba_store
634
+ }
635
+ }
636
+ assert_equal(1, @cache.size)
637
+ assert_nil(o1.instance_variable_get('@other'))
638
+ end
639
+ def test_extent
640
+ o1 = flexmock('O1')
641
+ o1.should_receive(:odba_id).and_return(1)
642
+ o2 = flexmock('O2')
643
+ o2.should_receive(:odba_id).and_return(2)
644
+ clr = flexmock('Caller')
645
+ @storage.should_receive(:extent_ids).and_return([1,2])
646
+ @storage.should_receive(:restore_collection).and_return([])
647
+ @storage.should_receive(:bulk_restore).with([1,2])\
648
+ .and_return([[1, 'dump1'],[2,'dump2']])
649
+ @marshal.should_receive(:load).with('dump1')\
650
+ .times(1).and_return(o1)
651
+ @marshal.should_receive(:load).with('dump2')\
652
+ .times(1).and_return(o2)
653
+ assert_equal([o1, o2], @cache.extent(ODBAContainer, clr))
654
+ end
655
+ def test_fetch_collection
656
+ obj = flexmock('Object')
657
+ obj.should_receive(:odba_id).and_return(1)
658
+ restored = flexmock('Restored')
659
+ restored.should_receive(:odba_id).and_return(7)
660
+ i1 = flexmock('Item1')
661
+ i2 = flexmock('Item2')
662
+ i1.should_receive(:is_a?).with(Stub).and_return(false)
663
+ i2.should_receive(:is_a?).with(Stub).and_return(true)
664
+ i2.should_receive(:odba_id).and_return(7)
665
+ i2.should_receive(:odba_container=).with(obj).times(1)
666
+ @storage.should_receive(:restore_collection).with(1)\
667
+ .times(1).and_return([['keydump1','dump1'],['keydump2','dump2']])
668
+ @storage.should_receive(:restore_collection).with(7).times(1).and_return([])
669
+ @storage.should_receive(:bulk_restore).with([7]).and_return([[7,'inst']])
670
+ @marshal.should_receive(:load).with('keydump1').and_return(0)
671
+ @marshal.should_receive(:load).with('keydump2').and_return(1)
672
+ @marshal.should_receive(:load).with('dump1').and_return(i1)
673
+ @marshal.should_receive(:load).with('dump2').and_return(i2)
674
+ @marshal.should_receive(:load).with('inst').and_return(restored)
675
+ assert_equal([[0,i1],[1,i2]], @cache.fetch_collection(obj))
676
+ end
677
+ def test_include
678
+ assert(!@cache.include?(1))
679
+ @cache.fetched.store(1, 'foo')
680
+ assert(@cache.include?(1))
681
+ assert(!@cache.include?(2))
682
+ @cache.prefetched.store(2, 'bar')
683
+ assert(@cache.include?(2))
684
+ end
685
+ def test_index_keys
686
+ index = flexmock('Index')
687
+ @cache.indices.store('index', index)
688
+ index.should_receive(:keys).with(nil).times(1).and_return(['ABC'])
689
+ index.should_receive(:keys).with(2).times(1).and_return(['AB'])
690
+ assert_equal(['ABC'], @cache.index_keys('index'))
691
+ assert_equal(['AB'], @cache.index_keys('index', 2))
692
+ end
693
+ def test_setup
694
+ @storage.should_receive(:setup).times(1).and_return { assert(true)}
695
+ @storage.should_receive(:ensure_target_id_index)\
696
+ .with('index').times(1).and_return { assert(true)}
697
+ df1 = IndexDefinition.new
698
+ df1.index_name = 'deferred'
699
+ df2 = IndexDefinition.new
700
+ df2.index_name = 'index'
701
+ indices = flexmock('indices')
702
+ indices.should_receive(:each_key).and_return { |block|
703
+ block.call('index')
704
+ }
705
+ indices.should_receive(:include?).with('index')\
706
+ .times(1).and_return(true)
707
+ indices.should_receive(:include?).with('deferred')\
708
+ .times(1).and_return(false)
709
+ indices.should_receive(:store).and_return { |key, val|
710
+ assert_equal('deferred', key)
711
+ assert_instance_of(Index, val)
712
+ }
713
+ indices.should_receive(:odba_store_unsaved).times(1)
714
+ @cache.instance_variable_set('@indices', indices)
715
+ @storage.should_receive(:transaction).and_return { |block|
716
+ block.call
717
+ }
718
+ @storage.should_receive(:create_index).with('deferred')\
719
+ .times(1).and_return { assert(true) }
720
+ @cache.instance_variable_set('@deferred_indices', [df1, df2])
721
+ @cache.setup
722
+ @cache.instance_variable_set('@indices', {})
723
+ end
724
+ end
725
+ end