archipelago 0.2.2 → 0.2.3

Sign up to get free protection for your applications and to get access to all the features.
data/README CHANGED
@@ -13,27 +13,33 @@ Archipelago::Hashish:: A hash-like tool that provides transparent persistence.
13
13
  Archipelago::Treasure:: A distributed object database where the objects never leave the database, instead you do your operations upon references to the objects. It has support for serializably isolated transactions with optimistic concurrency control using Archipelago::Tranny or any transaction manager with similar semantics.
14
14
  Archipelago::Pirate:: A client tool to allocate Archipelago::Treasure::Chests for different keys and act like an almost normal local Hash for providing distributed and scaleable object database facilities to any ruby application.
15
15
 
16
- == Examples:
16
+ == Usage:
17
+
18
+ To use archipelago in the simplest and most obvious way, just run the script/services.rb script.
17
19
 
18
- To build a ruby gem from these sources do 'rake gem'. The gem will be
19
- placed within the pkg/ directory.
20
+ You can run this on any number of machines in your local network, and they will all communicate through their own
21
+ discovery services.
20
22
 
21
- To set up an Archipelago::Tranny::Manager do the following (from scripts/tranny.rb):
23
+ Then you instantiate an Archipelago::Pirate::Captain anywhere in the network. This instance can be used basically as
24
+ a normal Hash - most everything will be transparently hidden from you.
22
25
 
23
- :include:script/tranny.rb
26
+ Everything you put in this instance will be persistently stored in the network.
24
27
 
25
- To set up an Archipelago::Treasure::Chest do the following (from scripts/chest.rb):
28
+ == Examples:
26
29
 
27
- :include:script/chest.rb
30
+ To run a transaction manager and a database server, just do the following (from script/services.rb):
28
31
 
29
- To set up an Archipelago::Pirate::Captain do the following (from scripts/pirate.rb):
32
+ :include:script/services.rb
33
+
34
+ To set up an Archipelago::Pirate::Captain do the following (from script/pirate.rb):
30
35
 
31
36
  :include:script/pirate.rb
32
37
 
33
- To set up a test environment to play around with, run the following
38
+ Or you can run script/console, which starts an irb and loads script/pirate.rb.
39
+
40
+ So, to set up a test environment to play around with in a few simple steps, run the following
34
41
  commands in a few terminals:
35
42
 
36
- * script/tranny.rb /tmp/tranny
37
- * script/chest.rb /tmp/chest1
38
- * script/chest.rb /tmp/chest2
39
- * script/console
43
+ script/services.rb /tmp/services1
44
+ script/services.rb /tmp/services2
45
+ script/console
data/TODO CHANGED
@@ -6,6 +6,9 @@
6
6
  check whether the instance before the call differs from the instance after
7
7
  the call. Preferably without incurring performance lossage.
8
8
 
9
+ * Make Dubloon proxy targets able to provide a dirty_state method that decides
10
+ whether they are to be considered dirty, clean or auto-select.
11
+
9
12
  * Test the transaction recovery mechanism of Chest.
10
13
 
11
14
  * Make Archipelago::Treasure::Dubloons work after an Archipelago::Treasure::Chest
@@ -36,47 +36,58 @@ module Archipelago
36
36
  #
37
37
  # Adds a few threaded methods to the normal ruby collections.
38
38
  #
39
+ # The only method your class has to implement to use this module is <b>each(&block)</b>.
40
+ #
39
41
  # NB: Will work slightly different than the unthreaded ones in certain circumstances.
40
42
  #
41
43
  module ThreadedCollection
42
44
 
43
45
  #
44
- # Like each, except calls +block+ within a new thread.
46
+ # Like each, except calls +block+ or +callable+ within a new thread.
45
47
  #
46
- def t_each(&block)
48
+ def t_each(callable = nil, &block)
49
+ raise "You have to provide either callable or block" if callable.nil? && block.nil?
50
+
47
51
  threads = []
52
+
48
53
  self.each do |args|
49
54
  threads << Thread.new do
50
- yield(args)
55
+ call_helper(callable, args, &block)
51
56
  end
52
57
  end
58
+
53
59
  threads.each do |thread|
54
60
  thread.join
55
61
  end
56
62
  end
57
63
 
58
64
  #
59
- # Like collect, except calls +block+ within a new thread.
65
+ # Like collect, except calls +block+ or +callable+ within a new thread.
60
66
  #
61
- def t_collect(&block)
67
+ def t_collect(callable = nil, &block)
68
+ raise "You have to provide either callable or block" if callable.nil? && block.nil?
69
+
62
70
  result = []
63
71
  result.extend(Synchronized)
64
72
  self.t_each do |args|
73
+ new_value = call_helper(callable, args, &block)
65
74
  result.synchronize do
66
- result << yield(args)
75
+ result << new_value
67
76
  end
68
77
  end
69
78
  return result
70
79
  end
71
80
 
72
81
  #
73
- # Like select, except calls +block+ within a new thread.
82
+ # Like select, except calls +block+ or +callable+ within a new thread.
74
83
  #
75
- def t_select(&block)
84
+ def t_select(callable = nil, &block)
85
+ raise "You have to provide either callable or block" if callable.nil? && block.nil?
86
+
76
87
  result = []
77
88
  result.extend(Synchronized)
78
89
  self.t_each do |args|
79
- matches = yield(args)
90
+ matches = call_helper(callable, args, &block)
80
91
  result.synchronize do
81
92
  result << args
82
93
  end if matches
@@ -85,13 +96,15 @@ module Archipelago
85
96
  end
86
97
 
87
98
  #
88
- # Like reject, except calls +block+ within a new thread.
99
+ # Like reject, except calls +block+ or +callable+ within a new thread.
89
100
  #
90
- def t_reject(&block)
101
+ def t_reject(callable = nil, &block)
102
+ raise "You have to provide either callable or block" if callable.nil? && block.nil?
103
+
91
104
  result = []
92
105
  result.extend(Synchronized)
93
106
  self.t_each do |args|
94
- matches = yield(args)
107
+ matches = call_helper(callable, args, &block)
95
108
  result.synchronize do
96
109
  result << args
97
110
  end unless matches
@@ -99,6 +112,20 @@ module Archipelago
99
112
  return result
100
113
  end
101
114
 
115
+ private
116
+
117
+ def call_helper(o = nil, args = nil, &block)
118
+ if o
119
+ if Array === args
120
+ return o.call(*args)
121
+ else
122
+ return o.call(args)
123
+ end
124
+ else
125
+ return yield(args)
126
+ end
127
+ end
128
+
102
129
  end
103
130
 
104
131
 
@@ -209,11 +209,7 @@ module Archipelago
209
209
  #
210
210
  def method_missing(meth, *args, &block)
211
211
  if @attributes.respond_to?(meth)
212
- if block
213
- @attributes.send(meth, *args, &block)
214
- else
215
- @attributes.send(meth, *args)
216
- end
212
+ @attributes.send(meth, *args, &block)
217
213
  else
218
214
  super(*args)
219
215
  end
@@ -296,11 +292,7 @@ module Archipelago
296
292
  def method_missing(meth, *args, &block)
297
293
  if @hash.respond_to?(meth)
298
294
  synchronize do
299
- if block
300
- @hash.send(meth, *args, &block)
301
- else
302
- @hash.send(meth, *args)
303
- end
295
+ @hash.send(meth, *args, &block)
304
296
  end
305
297
  else
306
298
  super(meth, *args, &block)
@@ -62,8 +62,19 @@ module Archipelago
62
62
  return Marshal.load(@content_db[Marshal.dump(key)])
63
63
  end
64
64
  #
65
+ # Returns true if this BerkeleyHashish include +key+.
66
+ #
67
+ def include?(key)
68
+ @content.include?(key) || !@content_db[Marshal.dump(key)].nil?
69
+ end
70
+ #
65
71
  # Simply get the value for the +key+.
66
72
  #
73
+ # Will call <b>value.load_hook</b> and send it
74
+ # a block that does the actuall insertion of the value
75
+ # into the live hash if <b>value.respond_to?(:load_hook)</b>
76
+ # if the value didnt exist in the live hash yet.
77
+ #
67
78
  def [](key)
68
79
  @lock.synchronize_on(key) do
69
80
 
@@ -94,7 +105,8 @@ module Archipelago
94
105
  end
95
106
  #
96
107
  # Stores whatever is under +key+ if it is not the same as
97
- # whats in the persistent db.
108
+ # whats in the persistent db - if the +key+ actually has
109
+ # a representation in the db.
98
110
  #
99
111
  # Will call <b>value.save_hook(old_value)</b> and send
100
112
  # it a block that does the actual saving if
@@ -105,11 +117,13 @@ module Archipelago
105
117
  @lock.synchronize_on(key) do
106
118
 
107
119
  serialized_key = Marshal.dump(key)
120
+
108
121
  value = @content[key]
109
122
  serialized_value = Marshal.dump(value)
110
-
111
- write_to_db(key, serialized_key, serialized_value, value) if @content_db[serialized_key] != serialized_value
123
+ old_serialized_value = @content_db[serialized_key]
112
124
 
125
+ write_to_db(key, serialized_key, serialized_value, value) if old_serialized_value && old_serialized_value != serialized_value
126
+
113
127
  end
114
128
  end
115
129
  #
@@ -198,13 +212,23 @@ module Archipelago
198
212
  # Read +key+ from db and if it is found
199
213
  # put it in the cache Hash.
200
214
  #
215
+ # Will call <b>value.load_hook</b> and send it
216
+ # a block that does the actuall insertion of the value
217
+ # into the live hash if <b>value.respond_to?(:load_hook)</b>.
218
+ #
201
219
  def get_from_db(key)
202
220
  serialized_key = Marshal.dump(key)
203
221
  serialized_value = @content_db[serialized_key]
204
222
  return nil unless serialized_value
205
223
 
206
224
  value = Marshal.load(serialized_value)
207
- @content[key] = value
225
+ if value.respond_to?(:load_hook)
226
+ value.load_hook do
227
+ @content[key] = value
228
+ end
229
+ else
230
+ @content[key] = value
231
+ end
208
232
  return value
209
233
  end
210
234
  end
@@ -242,15 +266,21 @@ module Archipelago
242
266
  return db
243
267
  end
244
268
  #
245
- # Closes databases opened by this instance and removes the persistent files.
269
+ # Closes databases opened by this instance.
246
270
  #
247
- def unlink
271
+ def close!
248
272
  @berkeley_hashishes.each do |h|
249
273
  h.close!
250
274
  end
251
275
  @bdb_dbs.each do |d|
252
276
  d.close
253
277
  end
278
+ end
279
+ #
280
+ # Closes databases opened by this instance and removes the persistent files.
281
+ #
282
+ def unlink!
283
+ close!
254
284
  home = Pathname.new(@env.home)
255
285
  @env.close
256
286
  home.rmtree if home.exist?
@@ -51,6 +51,15 @@ module Archipelago
51
51
  end
52
52
  end
53
53
 
54
+ #
55
+ # Raised when the transaction block failed to commit the transaction in the end.
56
+ #
57
+ class CommitFailedException < RuntimeError
58
+ def initialize(pirate, transaction)
59
+ super("#{pirate} failed to commit #{transaction}")
60
+ end
61
+ end
62
+
54
63
  INITIAL_SERVICE_UPDATE_INTERVAL = 1
55
64
  MAXIMUM_SERVICE_UPDATE_INTERVAL = 60
56
65
  CHEST_DESCRIPTION = {
@@ -111,6 +120,13 @@ module Archipelago
111
120
  @yar_counter = 0
112
121
  end
113
122
 
123
+ #
124
+ # Returns true if this Captain includes the given +key+, optionally within a +transaction+.
125
+ #
126
+ def include?(key, transaction = nil)
127
+ responsible_chest(key)[:service].include?(key, transaction || @transaction)
128
+ end
129
+
114
130
  #
115
131
  # Get a value from the distributed database network using a +key+,
116
132
  # optionally within a +transaction+.
@@ -157,6 +173,13 @@ module Archipelago
157
173
  return rval
158
174
  end
159
175
 
176
+ #
177
+ # Returns our active transaction, if any.
178
+ #
179
+ def active_transaction
180
+ @transaction
181
+ end
182
+
160
183
  #
161
184
  # Execute +block+ within a transaction.
162
185
  #
@@ -171,11 +194,10 @@ module Archipelago
171
194
  @transaction = @trannies.values.first[:service].begin
172
195
  begin
173
196
  yield(@transaction)
174
- @transaction.commit! if @transaction.state == :active
197
+ raise CommitFailedException.new(self, @transaction) unless @transaction.commit! == :commited
175
198
  rescue Exception => e
176
199
  @transaction.abort! unless @transaction.state == :aborted
177
- puts e
178
- pp e.backtrace
200
+ raise e
179
201
  ensure
180
202
  @transaction = nil
181
203
  end
@@ -254,7 +276,7 @@ module Archipelago
254
276
  def responsible_chest(key)
255
277
  raise NoRemoteDatabaseAvailableException.new(self) if @chests.empty?
256
278
 
257
- key_id = Digest::SHA1.new(Marshal.dump(key)).to_s
279
+ key_id = Digest::SHA1.hexdigest(Marshal.dump(key))
258
280
  sorted_chest_ids = @chests.keys.sort
259
281
  sorted_chest_ids.each do |id|
260
282
  return @chests[id] if id > key_id
@@ -218,6 +218,12 @@ module Archipelago
218
218
  end
219
219
  end
220
220
  #
221
+ # Implemented to allow comparison between proxies.
222
+ #
223
+ def ==(o)
224
+ eql?(o)
225
+ end
226
+ #
221
227
  # Forwards everything to our Transaction and remembers
222
228
  # returnvalue if necessary.
223
229
  #
@@ -94,6 +94,19 @@ module Archipelago
94
94
  #
95
95
  # A proxy to something in the chest.
96
96
  #
97
+ # This will do a very efficient masquerade as the object it proxies. When asked
98
+ # for its class or any other attribute it will forward the query to the proxied object.
99
+ #
100
+ # It can also be a part of a transaction, either because it was fetched from the chest
101
+ # within a transaction, or because it is the return value of a Dubloon#join call.
102
+ #
103
+ # In this case all forwarded methods will also be within the same transaction, and
104
+ # any change to the proxied object will be inside that transaction.
105
+ #
106
+ # If the proxied object itself needs to handle transaction semantics it can implement
107
+ # the <b>with_transaction(transaction, &block)</b> method, which will wrap the
108
+ # method call itself within the home Chest of the proxied object.
109
+ #
97
110
  class Dubloon
98
111
  #
99
112
  # Remove all methods so that we look like our target.
@@ -328,6 +341,20 @@ module Archipelago
328
341
  end
329
342
  end
330
343
 
344
+ #
345
+ # Returns true if this Chest includes the given +key+, optionally within a +transaction+.
346
+ #
347
+ def include?(key, transaction = nil)
348
+ join!(transaction)
349
+
350
+ if transaction
351
+ return @snapshot_by_transaction[transaction].include?(key)
352
+ else
353
+ return @db.include?(key)
354
+ end
355
+
356
+ end
357
+
331
358
  #
332
359
  # Return the contents of this chest using a given +key+ and +transaction+.
333
360
  #
@@ -402,6 +429,10 @@ module Archipelago
402
429
  # Call an instance +method+ on whatever this chest holds at +key+
403
430
  # with any +transaction+ and +args+.
404
431
  #
432
+ # If a +transaction+ is provided <b>and</b> the value for the +key+
433
+ # <b>respond_to?(:with_transaction)</b> then the actual method call
434
+ # will be wrapped within the block sent to <b>with_transaction(transaction, &block)</b>.
435
+ #
405
436
  def call_instance_method(key, method, transaction, *arguments, &block)
406
437
  if transaction
407
438
  return call_with_transaction(key, method, transaction, *arguments, &block)
@@ -602,7 +633,10 @@ module Archipelago
602
633
  end
603
634
 
604
635
  #
605
- # Call a method within a transaction.
636
+ # Call a +method+ within a +transaction+.
637
+ #
638
+ # If the object we want to run the +method+ on <b>respond_to?(:with_transaction)</b>
639
+ # then we will execute the actual +method+ within a block sent to the <b>with_transaction</b> method.
606
640
  #
607
641
  def call_with_transaction(key, method, transaction, *arguments, &block)
608
642
  assert_transaction(transaction)
@@ -617,7 +651,15 @@ module Archipelago
617
651
  raise UnknownObjectException.new(self, key, transaction) unless instance
618
652
 
619
653
  begin
620
- return execute(instance, method, *arguments, &block)
654
+ if instance.respond_to?(:with_transaction)
655
+ return_value = nil
656
+ instance.with_transaction(transaction) do
657
+ return_value = instance.send(method, *arguments, &block)
658
+ end
659
+ return return_value
660
+ else
661
+ return instance.send(method, *arguments, &block)
662
+ end
621
663
  ensure
622
664
  #
623
665
  # Make sure we remember when this object was last changed according
@@ -630,17 +672,6 @@ module Archipelago
630
672
  end
631
673
  end
632
674
 
633
- #
634
- # Execute +m+ with arguments +a+ and block +b+ on +o+.
635
- #
636
- def execute(o, m, *a, &b)
637
- if b
638
- return o.send(m, *a, &b)
639
- else
640
- return o.send(m, *a)
641
- end
642
- end
643
-
644
675
  #
645
676
  # Call a method outside any transaction (ie inside a transaction of its own).
646
677
  #
@@ -650,7 +681,7 @@ module Archipelago
650
681
  raise UnknownObjectException(self, key) unless instance
651
682
 
652
683
  begin
653
- return execute(instance, method, *arguments, &block)
684
+ return instance.send(method, *arguments, &block)
654
685
  ensure
655
686
  @db.store_if_changed(key)
656
687
  end
@@ -1,6 +1,22 @@
1
1
 
2
2
  require File.join(File.dirname(__FILE__), 'test_helper')
3
3
 
4
+ class HashCollector
5
+ attr_accessor :epa
6
+ def call(k,v)
7
+ @epa ||= {}
8
+ @epa[k] = v
9
+ end
10
+ end
11
+
12
+ class ArrayCollector
13
+ attr_accessor :epa
14
+ def call(e)
15
+ @epa ||= []
16
+ @epa << e
17
+ end
18
+ end
19
+
4
20
  class CurrentTest < Test::Unit::TestCase
5
21
 
6
22
  def test_synchronized
@@ -24,6 +40,24 @@ class CurrentTest < Test::Unit::TestCase
24
40
  end
25
41
  end
26
42
 
43
+ def test_lock_on
44
+ t = true
45
+
46
+ a = "hej"
47
+ a.extend(Archipelago::Current::Synchronized)
48
+ a.lock_on("epa")
49
+ Thread.new do
50
+ a.lock_on("epa")
51
+ t = false
52
+ end
53
+ Thread.pass
54
+ assert(t)
55
+ a.unlock_on("epa")
56
+ assert_within(0.5) do
57
+ !t
58
+ end
59
+ end
60
+
27
61
  def test_threaded_collection
28
62
  a = Array(10)
29
63
  a.extend(Archipelago::Current::ThreadedCollection)
@@ -51,6 +85,16 @@ class CurrentTest < Test::Unit::TestCase
51
85
  b = {}
52
86
  a.t_each do |k,v| b[k] = v end
53
87
  assert_equal(b,a)
88
+
89
+ c = HashCollector.new
90
+ a.t_each(c)
91
+ assert_equal(a, c.epa)
92
+
93
+ a = [1,2,3,4,5,6,7]
94
+ a.extend(Archipelago::Current::ThreadedCollection)
95
+ c = ArrayCollector.new
96
+ a.t_each(c)
97
+ assert_equal(a.sort, c.epa.sort)
54
98
  end
55
99
 
56
100
  end
data/tests/pirate_test.rb CHANGED
@@ -25,14 +25,36 @@ class PirateTest < Test::Unit::TestCase
25
25
  def teardown
26
26
  @p.stop!
27
27
  @c.stop!
28
- @c.persistence_provider.unlink
28
+ @c.persistence_provider.unlink!
29
29
  @c2.stop!
30
- @c2.persistence_provider.unlink
30
+ @c2.persistence_provider.unlink!
31
31
  @tm.stop!
32
- @tm.persistence_provider.unlink
32
+ @tm.persistence_provider.unlink!
33
33
  DRb.stop_service
34
34
  end
35
35
 
36
+ def test_include
37
+ assert(!@p.include?("blabla"))
38
+ @p["blabla"] = "kissobjas"
39
+ assert(@p.include?("blabla"))
40
+ t = @tm.begin
41
+ assert(!@p.include?("hehu"))
42
+ assert(!@p.include?("hehu", t))
43
+ @p["hehu", t] = "brunte"
44
+ assert(!@p.include?("hehu"))
45
+ assert(@p.include?("hehu", t))
46
+ t.commit!
47
+ assert(@p.include?("hehu"))
48
+ t = @tm.begin
49
+ assert(!@p.include?("hehu2"))
50
+ assert(!@p.include?("hehu2", t))
51
+ @p["hehu2", t] = "brunte"
52
+ assert(!@p.include?("hehu2"))
53
+ assert(@p.include?("hehu2", t))
54
+ t.abort!
55
+ assert(!@p.include?("hehu2"))
56
+ end
57
+
36
58
  def test_each
37
59
  @p["oj"] = "bla"
38
60
  @p["brunt"] = "ja"
@@ -89,6 +111,40 @@ class PirateTest < Test::Unit::TestCase
89
111
  assert_equal(s1, s2)
90
112
  end
91
113
 
114
+ def test_transaction
115
+ p2 = Archipelago::Pirate::Captain.new(:chest_description => {:class => "TestChest"},
116
+ :tranny_description => {:class => "TestManager"})
117
+ assert_within(10) do
118
+ p2.chests.keys.sort == [@c.service_id, @c2.service_id].sort
119
+ end
120
+ assert_within(10) do
121
+ p2.trannies.keys == [@tm.service_id]
122
+ end
123
+ trans = nil
124
+ assert_raise(Archipelago::Pirate::CommitFailedException) do
125
+ @p.transaction do |trans|
126
+ assert_equal(trans.transaction_id, @p.active_transaction.transaction_id)
127
+ @p["hehu"] = "haha"
128
+ assert(!p2.include?("hehu"))
129
+ assert(!p2.include?("hehu", nil))
130
+ assert_equal(nil, p2.active_transaction)
131
+ trans2 = nil
132
+ p2.transaction do |trans2|
133
+ assert_equal(trans2.transaction_id, p2.active_transaction.transaction_id)
134
+ p2["hehu"] = "hoj"
135
+ assert_equal("haha", @p["hehu"])
136
+ assert_equal("hoj", @p["hehu", trans2])
137
+ end
138
+ assert_equal(:commited, trans2.state)
139
+ assert_equal(:active, trans.state)
140
+ assert_equal("hoj", p2["hehu"])
141
+ assert_equal("haha", p2["hehu", trans])
142
+ end
143
+ end
144
+ assert_equal(:aborted, trans.state)
145
+ assert_equal("hoj", @p["hehu"])
146
+ end
147
+
92
148
  def test_write_read_transaction
93
149
  $T = true
94
150
  @p["hej"] = "haha"
data/tests/tranny_test.rb CHANGED
@@ -9,7 +9,7 @@ class TrannyTest < Test::Unit::TestCase
9
9
  end
10
10
 
11
11
  def teardown
12
- @tm.persistence_provider.unlink
12
+ @tm.persistence_provider.unlink!
13
13
  DRb.stop_service
14
14
  end
15
15
 
@@ -9,7 +9,7 @@ class TreasureBenchmark < Test::Unit::TestCase
9
9
  end
10
10
 
11
11
  def teardown
12
- @c.persistence_provider.unlink
12
+ @c.persistence_provider.unlink!
13
13
  DRb.stop_service
14
14
  end
15
15
 
@@ -7,6 +7,17 @@ class A < String
7
7
  yield
8
8
  $BURKMAT2 += 1
9
9
  end
10
+ def load_hook(&block)
11
+ $BURKMAT3 += 1
12
+ yield
13
+ $BURKMAT4 += 1
14
+ end
15
+ def with_transaction(transaction, &block)
16
+ $BURKMAT7 = transaction
17
+ $BURKMAT5 += 1
18
+ yield
19
+ $BURKMAT6 += 1
20
+ end
10
21
  end
11
22
 
12
23
  class TreasureTest < Test::Unit::TestCase
@@ -18,15 +29,29 @@ class TreasureTest < Test::Unit::TestCase
18
29
  @tm = TestManager.new(:persistence_provider => Archipelago::Hashish::BerkeleyHashishProvider.new(Pathname.new(__FILE__).parent.join("tranny1.db")))
19
30
  $BURKMAT = 0
20
31
  $BURKMAT2 = 0
32
+ $BURKMAT3 = 0
33
+ $BURKMAT4 = 0
34
+ $BURKMAT5 = 0
35
+ $BURKMAT6 = 0
36
+ $BURKMAT7 = nil
21
37
  end
22
38
 
23
39
  def teardown
24
- @c.persistence_provider.unlink
25
- @c2.persistence_provider.unlink
26
- @tm.persistence_provider.unlink
40
+ @c.persistence_provider.unlink!
41
+ @c2.persistence_provider.unlink!
42
+ @tm.persistence_provider.unlink!
27
43
  DRb.stop_service
28
44
  end
29
45
 
46
+ def test_with_transaction
47
+ t = @tm.begin
48
+ @c["hehu"] = A.new("kiss")
49
+ @c["hehu", t].upcase!
50
+ assert_equal(1, $BURKMAT5)
51
+ assert_equal(1, $BURKMAT6)
52
+ assert(!$BURKMAT7.nil?)
53
+ end
54
+
30
55
  def test_each
31
56
  @c["oj"] = "bla"
32
57
  @c["brunt"] = "ja"
@@ -37,6 +62,37 @@ class TreasureTest < Test::Unit::TestCase
37
62
  assert_equal({"oj" => "bla", "brunt" => "ja"}, h)
38
63
  end
39
64
 
65
+ def test_include
66
+ assert(!@c.include?("blabla"))
67
+ @c["blabla"] = "kissobjas"
68
+ assert(@c.include?("blabla"))
69
+ t = @tm.begin
70
+ assert(!@c.include?("hehu"))
71
+ assert(!@c.include?("hehu", t))
72
+ @c["hehu", t] = "brunte"
73
+ assert(!@c.include?("hehu"))
74
+ assert(@c.include?("hehu", t))
75
+ t.commit!
76
+ assert(@c.include?("hehu"))
77
+ t = @tm.begin
78
+ assert(!@c.include?("hehu2"))
79
+ assert(!@c.include?("hehu2", t))
80
+ @c["hehu2", t] = "brunte"
81
+ assert(!@c.include?("hehu2"))
82
+ assert(@c.include?("hehu2", t))
83
+ t.abort!
84
+ assert(!@c.include?("hehu2"))
85
+ end
86
+
87
+ def test_around_load
88
+ @c["brunis"] = A.new("hirr")
89
+ @c.persistence_provider.close!
90
+ @c = TestChest.new(:persistence_provider => Archipelago::Hashish::BerkeleyHashishProvider.new(Pathname.new(__FILE__).parent.join("chest.db")))
91
+ a = @c["brunis"]
92
+ assert_equal(1, $BURKMAT3)
93
+ assert_equal(1, $BURKMAT4)
94
+ end
95
+
40
96
  def test_around_save
41
97
  s = A.new("hehu")
42
98
  @c["oj"] = s
@@ -61,7 +117,9 @@ class TreasureTest < Test::Unit::TestCase
61
117
 
62
118
  def test_store_load_update
63
119
  s = "hehu"
120
+ assert(!@c.include?("oj"))
64
121
  @c["oj"] = "hehu"
122
+ assert(@c.include?("oj"))
65
123
  oj1 = @c["oj"]
66
124
  assert_equal(oj1, @c["oj"])
67
125
  assert_equal("hehu", oj1)
metadata CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.8.11
3
3
  specification_version: 1
4
4
  name: archipelago
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.2.2
7
- date: 2006-11-28 00:00:00 +01:00
6
+ version: 0.2.3
7
+ date: 2006-11-29 00:00:00 +01:00
8
8
  summary: A set of tools for distributed computing in ruby.
9
9
  require_paths:
10
10
  - lib