odba 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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