transaction-simple 1.2.0 → 1.3.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,133 @@
1
+ require 'transaction/simple'
2
+
3
+ # A transaction group is an object wrapper that manages a group of objects
4
+ # as if they were a single object for the purpose of transaction
5
+ # management. All transactions for this group of objects should be
6
+ # performed against the transaction group object, not against individual
7
+ # objects in the group.
8
+ #
9
+ # == Transaction Group Usage
10
+ # require 'transaction/simple/group'
11
+ #
12
+ # x = "Hello, you."
13
+ # y = "And you, too."
14
+ #
15
+ # g = Transaction::Simple::Group.new(x, y)
16
+ # g.start_transaction(:first) # -> [ x, y ]
17
+ # g.transaction_open?(:first) # -> true
18
+ # x.transaction_open?(:first) # -> true
19
+ # y.transaction_open?(:first) # -> true
20
+ #
21
+ # x.gsub!(/you/, "world") # -> "Hello, world."
22
+ # y.gsub!(/you/, "me") # -> "And me, too."
23
+ #
24
+ # g.start_transaction(:second) # -> [ x, y ]
25
+ # x.gsub!(/world/, "HAL") # -> "Hello, HAL."
26
+ # y.gsub!(/me/, "Dave") # -> "And Dave, too."
27
+ # g.rewind_transaction(:second) # -> [ x, y ]
28
+ # x # -> "Hello, world."
29
+ # y # -> "And me, too."
30
+ #
31
+ # x.gsub!(/world/, "HAL") # -> "Hello, HAL."
32
+ # y.gsub!(/me/, "Dave") # -> "And Dave, too."
33
+ #
34
+ # g.commit_transaction(:second) # -> [ x, y ]
35
+ # x # -> "Hello, HAL."
36
+ # y # -> "And Dave, too."
37
+ #
38
+ # g.abort_transaction(:first) # -> [ x, y ]
39
+ # x = -> "Hello, you."
40
+ # y = -> "And you, too."
41
+ class Transaction::Simple::Group
42
+ # Creates a transaction group for the provided objects. If a block is
43
+ # provided, the transaction group object is yielded to the block; when
44
+ # the block is finished, the transaction group object will be cleared
45
+ # with #clear.
46
+ def initialize(*objects)
47
+ @objects = objects || []
48
+ @objects.freeze
49
+ @objects.each { |obj| obj.extend(Transaction::Simple) }
50
+
51
+ if block_given?
52
+ begin
53
+ yield self
54
+ ensure
55
+ self.clear
56
+ end
57
+ end
58
+ end
59
+
60
+ # Returns the objects that are covered by this transaction group.
61
+ attr_reader :objects
62
+
63
+ # Clears the object group. Removes references to the objects so that
64
+ # they can be garbage collected.
65
+ def clear
66
+ @objects = @objects.dup.clear
67
+ end
68
+
69
+ # Tests to see if all of the objects in the group have an open
70
+ # transaction. See Transaction::Simple#transaction_open? for more
71
+ # information.
72
+ def transaction_open?(name = nil)
73
+ @objects.inject(true) do |val, obj|
74
+ val = val and obj.transaction_open?(name)
75
+ end
76
+ end
77
+
78
+ # Returns the current name of the transaction for the group.
79
+ # Transactions not explicitly named are named +nil+.
80
+ def transaction_name
81
+ @objects[0].transaction_name
82
+ end
83
+
84
+ # Starts a transaction for the group. Stores the current object state.
85
+ # If a transaction name is specified, the transaction will be named.
86
+ # Transaction names must be unique. Transaction names of +nil+ will be
87
+ # treated as unnamed transactions.
88
+ def start_transaction(name = nil)
89
+ @objects.each { |obj| obj.start_transaction(name) }
90
+ end
91
+
92
+ # Rewinds the transaction. If +name+ is specified, then the intervening
93
+ # transactions will be aborted and the named transaction will be
94
+ # rewound. Otherwise, only the current transaction is rewound.
95
+ def rewind_transaction(name = nil)
96
+ @objects.each { |obj| obj.rewind_transaction(name) }
97
+ end
98
+
99
+ # Aborts the transaction. Resets the object state to what it was before
100
+ # the transaction was started and closes the transaction. If +name+ is
101
+ # specified, then the intervening transactions and the named transaction
102
+ # will be aborted. Otherwise, only the current transaction is aborted.
103
+ #
104
+ # If the current or named transaction has been started by a block
105
+ # (Transaction::Simple.start), then the execution of the block will be
106
+ # halted with +break+ +self+.
107
+ def abort_transaction(name = nil)
108
+ @objects.each { |obj| obj.abort_transaction(name) }
109
+ end
110
+
111
+ # If +name+ is +nil+ (default), the current transaction level is closed
112
+ # out and the changes are committed.
113
+ #
114
+ # If +name+ is specified and +name+ is in the list of named
115
+ # transactions, then all transactions are closed and committed until the
116
+ # named transaction is reached.
117
+ def commit_transaction(name = nil)
118
+ @objects.each { |obj| obj.commit_transaction(name) }
119
+ end
120
+
121
+ # Alternative method for calling the transaction methods. An optional
122
+ # name can be specified for named transaction support.
123
+ #
124
+ # #transaction(:start):: #start_transaction
125
+ # #transaction(:rewind):: #rewind_transaction
126
+ # #transaction(:abort):: #abort_transaction
127
+ # #transaction(:commit):: #commit_transaction
128
+ # #transaction(:name):: #transaction_name
129
+ # #transaction:: #transaction_open?
130
+ def transaction(action = nil, name = nil)
131
+ @objects.each { |obj| obj.transaction(action, name) }
132
+ end
133
+ end
@@ -0,0 +1,52 @@
1
+ require 'transaction/simple'
2
+ require 'thread'
3
+
4
+ class Transaction::TransactionThreadError < StandardError
5
+ end
6
+
7
+ # = Transaction::Simple::ThreadSafe
8
+ # Thread-safe simple object transaction support for Ruby.
9
+ # Transaction::Simple::ThreadSafe is used in the same way as
10
+ # Transaction::Simple. Transaction::Simple::ThreadSafe uses a Mutex object
11
+ # to ensure atomicity at the cost of performance in threaded applications.
12
+ #
13
+ # Transaction::Simple::ThreadSafe will not wait to obtain a lock; if the
14
+ # lock cannot be obtained immediately, a
15
+ # Transaction::TransactionThreadError will be raised.
16
+ #
17
+ # Thanks to Mauricio Fern�ndez for help with getting this part working.
18
+ #
19
+ # Threadsafe transactions can be used in any place that normal
20
+ # transactions would. The main difference would be in setup:
21
+ #
22
+ # require 'transaction/simple/threadsafe'
23
+ #
24
+ # x = "Hello, you."
25
+ # x.extend(Transaction::Simple::ThreadSafe) # Threadsafe
26
+ #
27
+ # y = "Hello, you."
28
+ # y.extend(Transaction::Simple) # Not threadsafe
29
+ module Transaction::Simple::ThreadSafe
30
+ include Transaction::Simple
31
+
32
+ SKIP_TRANSACTION_VARS = Transaction::Simple::SKIP_TRANSACTION_VARS.dup #:nodoc:
33
+ SKIP_TRANSACTION_VARS << "@__transaction_mutex__"
34
+
35
+ Transaction::Simple.instance_methods(false) do |meth|
36
+ next if meth == "transaction"
37
+ arg = "(name = nil)" unless meth == "transaction_name"
38
+ module_eval <<-EOS
39
+ def #{meth}#{arg}
40
+ if (@__transaction_mutex__ ||= Mutex.new).try_lock
41
+ result = super
42
+ @__transaction_mutex__.unlock
43
+ return result
44
+ else
45
+ raise TransactionThreadError, Messages[:cannot_obtain_transaction_lock] % meth
46
+ end
47
+ ensure
48
+ @__transaction_mutex__.unlock
49
+ end
50
+ EOS
51
+ end
52
+ end
@@ -0,0 +1,23 @@
1
+ require 'transaction/simple/threadsafe'
2
+
3
+ # A transaction group is an object wrapper that manages a group of objects
4
+ # as if they were a single object for the purpose of transaction
5
+ # management. All transactions for this group of objects should be
6
+ # performed against the transaction group object, not against individual
7
+ # objects in the group. This is the threadsafe version of a transaction
8
+ # group.
9
+ class Transaction::Simple::ThreadSafe::Group < Transaction::Simple::Group
10
+ def initialize(*objects)
11
+ @objects = objects || []
12
+ @objects.freeze
13
+ @objects.each { |obj| obj.extend(Transaction::Simple::ThreadSafe) }
14
+
15
+ if block_given?
16
+ begin
17
+ yield self
18
+ ensure
19
+ self.clear
20
+ end
21
+ end
22
+ end
23
+ end
@@ -120,7 +120,6 @@ class Test__Transaction_Simple < Test::Unit::TestCase #:nodoc:
120
120
  end
121
121
 
122
122
  def test_block
123
- @value = VALUE.dup
124
123
  Transaction::Simple.start(@value) do |tv|
125
124
  assert_equal(true, tv.transaction_open?)
126
125
  assert_nothing_raised { tv.gsub!(/men/, 'women') }
@@ -144,7 +143,6 @@ class Test__Transaction_Simple < Test::Unit::TestCase #:nodoc:
144
143
  end
145
144
 
146
145
  def test_named_block
147
- @value = VALUE.dup
148
146
  Transaction::Simple.start_named(:first, @value) do |tv|
149
147
  assert_equal(true, tv.transaction_open?)
150
148
  assert_equal(true, tv.transaction_open?(:first))
@@ -233,7 +231,6 @@ class Test__Transaction_Simple < Test::Unit::TestCase #:nodoc:
233
231
  end
234
232
 
235
233
  def test_multilevel_block
236
- @value = VALUE.dup
237
234
  Transaction::Simple.start_named(:outer, @value) do |tv0|
238
235
  assert_equal(1, tv0.instance_variable_get(:@__transaction_level__))
239
236
  assert_equal(true, tv0.transaction_open?(:outer))
@@ -267,136 +264,14 @@ class Test__Transaction_Simple < Test::Unit::TestCase #:nodoc:
267
264
  assert_nothing_raised { @value.abort_transaction }
268
265
  assert_equal(@orig, @value)
269
266
  end
270
- end
271
267
 
272
- class Test__Transaction_Simple_ThreadSafe < Test::Unit::TestCase #:nodoc:
273
- VALUE = "Now is the time for all good men to come to the aid of their country."
274
-
275
- def setup
268
+ def test_instance_var
276
269
  @value = VALUE.dup
277
- @value.extend(Transaction::Simple::ThreadSafe)
278
- end
279
-
280
- def test_extended
281
- assert_respond_to(@value, :start_transaction)
282
- end
283
-
284
- def test_started
285
- assert_equal(false, @value.transaction_open?)
286
- assert_nothing_raised { @value.start_transaction }
287
- assert_equal(true, @value.transaction_open?)
288
- end
289
-
290
- def test_rewind
291
- assert_equal(false, @value.transaction_open?)
292
- assert_raises(Transaction::TransactionError) { @value.rewind_transaction }
293
- assert_nothing_raised { @value.start_transaction }
294
- assert_equal(true, @value.transaction_open?)
295
- assert_nothing_raised { @value.gsub!(/men/, 'women') }
296
- assert_not_equal(VALUE, @value)
297
- assert_nothing_raised { @value.rewind_transaction }
298
- assert_equal(true, @value.transaction_open?)
299
- assert_equal(VALUE, @value)
300
- end
301
-
302
- def test_abort
303
- assert_equal(false, @value.transaction_open?)
304
- assert_raises(Transaction::TransactionError) { @value.abort_transaction }
305
- assert_nothing_raised { @value.start_transaction }
306
- assert_equal(true, @value.transaction_open?)
307
- assert_nothing_raised { @value.gsub!(/men/, 'women') }
308
- assert_not_equal(VALUE, @value)
309
- assert_nothing_raised { @value.abort_transaction }
310
- assert_equal(false, @value.transaction_open?)
311
- assert_equal(VALUE, @value)
312
- end
313
-
314
- def test_commit
315
- assert_equal(false, @value.transaction_open?)
316
- assert_raises(Transaction::TransactionError) { @value.commit_transaction }
317
- assert_nothing_raised { @value.start_transaction }
318
- assert_equal(true, @value.transaction_open?)
319
- assert_nothing_raised { @value.gsub!(/men/, 'women') }
320
- assert_not_equal(VALUE, @value)
321
- assert_equal(true, @value.transaction_open?)
322
- assert_nothing_raised { @value.commit_transaction }
323
- assert_equal(false, @value.transaction_open?)
324
- assert_not_equal(VALUE, @value)
325
- end
326
-
327
- def test_multilevel
328
- assert_equal(false, @value.transaction_open?)
329
- assert_nothing_raised { @value.start_transaction }
330
- assert_equal(true, @value.transaction_open?)
331
- assert_nothing_raised { @value.gsub!(/men/, 'women') }
332
- assert_equal(VALUE.gsub(/men/, 'women'), @value)
333
- assert_equal(true, @value.transaction_open?)
334
- assert_nothing_raised { @value.start_transaction }
335
- assert_nothing_raised { @value.gsub!(/country/, 'nation-state') }
336
- assert_nothing_raised { @value.commit_transaction }
337
- assert_equal(VALUE.gsub(/men/, 'women').gsub(/country/, 'nation-state'), @value)
338
- assert_equal(true, @value.transaction_open?)
339
- assert_nothing_raised { @value.abort_transaction }
340
- assert_equal(VALUE, @value)
341
- end
342
-
343
- def test_multilevel_named
344
- assert_equal(false, @value.transaction_open?)
345
- assert_raises(Transaction::TransactionError) { @value.transaction_name }
346
- assert_nothing_raised { @value.start_transaction(:first) } # 1
347
- assert_raises(Transaction::TransactionError) { @value.start_transaction(:first) }
348
- assert_equal(true, @value.transaction_open?)
349
- assert_equal(true, @value.transaction_open?(:first))
350
- assert_equal(:first, @value.transaction_name)
351
- assert_nothing_raised { @value.start_transaction } # 2
352
- assert_not_equal(:first, @value.transaction_name)
353
- assert_equal(nil, @value.transaction_name)
354
- assert_raises(Transaction::TransactionError) { @value.abort_transaction(:second) }
355
- assert_nothing_raised { @value.abort_transaction(:first) }
356
- assert_equal(false, @value.transaction_open?)
357
- assert_nothing_raised do
358
- @value.start_transaction(:first)
359
- @value.gsub!(/men/, 'women')
360
- @value.start_transaction(:second)
361
- @value.gsub!(/women/, 'people')
362
- @value.start_transaction
363
- @value.gsub!(/people/, 'sentients')
364
- end
365
- assert_nothing_raised { @value.abort_transaction(:second) }
366
- assert_equal(true, @value.transaction_open?(:first))
367
- assert_equal(VALUE.gsub(/men/, 'women'), @value)
368
- assert_nothing_raised do
369
- @value.start_transaction(:second)
370
- @value.gsub!(/women/, 'people')
371
- @value.start_transaction
372
- @value.gsub!(/people/, 'sentients')
373
- end
374
- assert_raises(Transaction::TransactionError) { @value.rewind_transaction(:foo) }
375
- assert_nothing_raised { @value.rewind_transaction(:second) }
376
- assert_equal(VALUE.gsub(/men/, 'women'), @value)
377
- assert_nothing_raised do
378
- @value.gsub!(/women/, 'people')
379
- @value.start_transaction
380
- @value.gsub!(/people/, 'sentients')
381
- end
382
- assert_raises(Transaction::TransactionError) { @value.commit_transaction(:foo) }
383
- assert_nothing_raised { @value.commit_transaction(:first) }
384
- assert_equal(VALUE.gsub(/men/, 'sentients'), @value)
385
- assert_equal(false, @value.transaction_open?)
386
- end
387
-
388
- def test_array
389
- assert_nothing_raised do
390
- @orig = ["first", "second", "third"]
391
- @value = ["first", "second", "third"]
392
- @value.extend(Transaction::Simple::ThreadSafe)
393
- end
394
- assert_equal(@orig, @value)
395
- assert_nothing_raised { @value.start_transaction }
270
+ @value.extend(Transaction::Simple)
271
+ @value.start_transaction
396
272
  assert_equal(true, @value.transaction_open?)
397
- assert_nothing_raised { @value[1].gsub!(/second/, "fourth") }
398
- assert_not_equal(@orig, @value)
399
- assert_nothing_raised { @value.abort_transaction }
400
- assert_equal(@orig, @value)
273
+ @value.instance_variable_set("@foo", "bar")
274
+ @value.rewind_transaction
275
+ assert_nil(@value.instance_variable_get("@foo"))
401
276
  end
402
277
  end
@@ -0,0 +1,44 @@
1
+ $LOAD_PATH.unshift("#{File.dirname(__FILE__)}/../lib") if __FILE__ == $0
2
+
3
+ require 'transaction/simple/group'
4
+ require 'test/unit'
5
+ class Test__Transaction_Simple_Group < Test::Unit::TestCase #:nodoc:
6
+ VALUE1 = "Hello, you."
7
+ VALUE2 = "And you, too."
8
+
9
+ def setup
10
+ @x = VALUE1.dup
11
+ @y = VALUE2.dup
12
+ end
13
+
14
+ def test_group
15
+ group = Transaction::Simple::Group.new(@x, @y)
16
+
17
+ assert_nothing_raised { group.start_transaction(:first) }
18
+ assert_equal(true, group.transaction_open?(:first))
19
+ assert_equal(true, @x.transaction_open?(:first))
20
+ assert_equal(true, @y.transaction_open?(:first))
21
+
22
+ assert_equal("Hello, world.", @x.gsub!(/you/, "world"))
23
+ assert_equal("And me, too.", @y.gsub!(/you/, "me"))
24
+
25
+ assert_nothing_raised { group.start_transaction(:second) }
26
+ assert_equal("Hello, HAL.", @x.gsub!(/world/, "HAL"))
27
+ assert_equal("And Dave, too.", @y.gsub!(/me/, "Dave"))
28
+
29
+ assert_nothing_raised { group.rewind_transaction(:second) }
30
+ assert_equal("Hello, world.", @x)
31
+ assert_equal("And me, too.", @y)
32
+
33
+ assert_equal("Hello, HAL.", @x.gsub!(/world/, "HAL"))
34
+ assert_equal("And Dave, too.", @y.gsub!(/me/, "Dave"))
35
+
36
+ assert_nothing_raised { group.commit_transaction(:second) }
37
+ assert_equal("Hello, HAL.", @x)
38
+ assert_equal("And Dave, too.", @y)
39
+
40
+ assert_nothing_raised { group.abort_transaction(:first) }
41
+ assert_equal("Hello, you.", @x)
42
+ assert_equal("And you, too.", @y)
43
+ end
44
+ end
@@ -0,0 +1,135 @@
1
+ $LOAD_PATH.unshift("#{File.dirname(__FILE__)}/../lib") if __FILE__ == $0
2
+
3
+ require 'transaction/simple/threadsafe'
4
+ require 'test/unit'
5
+ class Test__Transaction_Simple_ThreadSafe < Test::Unit::TestCase #:nodoc:
6
+ VALUE = "Now is the time for all good men to come to the aid of their country."
7
+
8
+ def setup
9
+ @value = VALUE.dup
10
+ @value.extend(Transaction::Simple::ThreadSafe)
11
+ end
12
+
13
+ def test_extended
14
+ assert_respond_to(@value, :start_transaction)
15
+ end
16
+
17
+ def test_started
18
+ assert_equal(false, @value.transaction_open?)
19
+ assert_nothing_raised { @value.start_transaction }
20
+ assert_equal(true, @value.transaction_open?)
21
+ end
22
+
23
+ def test_rewind
24
+ assert_equal(false, @value.transaction_open?)
25
+ assert_raises(Transaction::TransactionError) { @value.rewind_transaction }
26
+ assert_nothing_raised { @value.start_transaction }
27
+ assert_equal(true, @value.transaction_open?)
28
+ assert_nothing_raised { @value.gsub!(/men/, 'women') }
29
+ assert_not_equal(VALUE, @value)
30
+ assert_nothing_raised { @value.rewind_transaction }
31
+ assert_equal(true, @value.transaction_open?)
32
+ assert_equal(VALUE, @value)
33
+ end
34
+
35
+ def test_abort
36
+ assert_equal(false, @value.transaction_open?)
37
+ assert_raises(Transaction::TransactionError) { @value.abort_transaction }
38
+ assert_nothing_raised { @value.start_transaction }
39
+ assert_equal(true, @value.transaction_open?)
40
+ assert_nothing_raised { @value.gsub!(/men/, 'women') }
41
+ assert_not_equal(VALUE, @value)
42
+ assert_nothing_raised { @value.abort_transaction }
43
+ assert_equal(false, @value.transaction_open?)
44
+ assert_equal(VALUE, @value)
45
+ end
46
+
47
+ def test_commit
48
+ assert_equal(false, @value.transaction_open?)
49
+ assert_raises(Transaction::TransactionError) { @value.commit_transaction }
50
+ assert_nothing_raised { @value.start_transaction }
51
+ assert_equal(true, @value.transaction_open?)
52
+ assert_nothing_raised { @value.gsub!(/men/, 'women') }
53
+ assert_not_equal(VALUE, @value)
54
+ assert_equal(true, @value.transaction_open?)
55
+ assert_nothing_raised { @value.commit_transaction }
56
+ assert_equal(false, @value.transaction_open?)
57
+ assert_not_equal(VALUE, @value)
58
+ end
59
+
60
+ def test_multilevel
61
+ assert_equal(false, @value.transaction_open?)
62
+ assert_nothing_raised { @value.start_transaction }
63
+ assert_equal(true, @value.transaction_open?)
64
+ assert_nothing_raised { @value.gsub!(/men/, 'women') }
65
+ assert_equal(VALUE.gsub(/men/, 'women'), @value)
66
+ assert_equal(true, @value.transaction_open?)
67
+ assert_nothing_raised { @value.start_transaction }
68
+ assert_nothing_raised { @value.gsub!(/country/, 'nation-state') }
69
+ assert_nothing_raised { @value.commit_transaction }
70
+ assert_equal(VALUE.gsub(/men/, 'women').gsub(/country/, 'nation-state'), @value)
71
+ assert_equal(true, @value.transaction_open?)
72
+ assert_nothing_raised { @value.abort_transaction }
73
+ assert_equal(VALUE, @value)
74
+ end
75
+
76
+ def test_multilevel_named
77
+ assert_equal(false, @value.transaction_open?)
78
+ assert_raises(Transaction::TransactionError) { @value.transaction_name }
79
+ assert_nothing_raised { @value.start_transaction(:first) } # 1
80
+ assert_raises(Transaction::TransactionError) { @value.start_transaction(:first) }
81
+ assert_equal(true, @value.transaction_open?)
82
+ assert_equal(true, @value.transaction_open?(:first))
83
+ assert_equal(:first, @value.transaction_name)
84
+ assert_nothing_raised { @value.start_transaction } # 2
85
+ assert_not_equal(:first, @value.transaction_name)
86
+ assert_equal(nil, @value.transaction_name)
87
+ assert_raises(Transaction::TransactionError) { @value.abort_transaction(:second) }
88
+ assert_nothing_raised { @value.abort_transaction(:first) }
89
+ assert_equal(false, @value.transaction_open?)
90
+ assert_nothing_raised do
91
+ @value.start_transaction(:first)
92
+ @value.gsub!(/men/, 'women')
93
+ @value.start_transaction(:second)
94
+ @value.gsub!(/women/, 'people')
95
+ @value.start_transaction
96
+ @value.gsub!(/people/, 'sentients')
97
+ end
98
+ assert_nothing_raised { @value.abort_transaction(:second) }
99
+ assert_equal(true, @value.transaction_open?(:first))
100
+ assert_equal(VALUE.gsub(/men/, 'women'), @value)
101
+ assert_nothing_raised do
102
+ @value.start_transaction(:second)
103
+ @value.gsub!(/women/, 'people')
104
+ @value.start_transaction
105
+ @value.gsub!(/people/, 'sentients')
106
+ end
107
+ assert_raises(Transaction::TransactionError) { @value.rewind_transaction(:foo) }
108
+ assert_nothing_raised { @value.rewind_transaction(:second) }
109
+ assert_equal(VALUE.gsub(/men/, 'women'), @value)
110
+ assert_nothing_raised do
111
+ @value.gsub!(/women/, 'people')
112
+ @value.start_transaction
113
+ @value.gsub!(/people/, 'sentients')
114
+ end
115
+ assert_raises(Transaction::TransactionError) { @value.commit_transaction(:foo) }
116
+ assert_nothing_raised { @value.commit_transaction(:first) }
117
+ assert_equal(VALUE.gsub(/men/, 'sentients'), @value)
118
+ assert_equal(false, @value.transaction_open?)
119
+ end
120
+
121
+ def test_array
122
+ assert_nothing_raised do
123
+ @orig = ["first", "second", "third"]
124
+ @value = ["first", "second", "third"]
125
+ @value.extend(Transaction::Simple::ThreadSafe)
126
+ end
127
+ assert_equal(@orig, @value)
128
+ assert_nothing_raised { @value.start_transaction }
129
+ assert_equal(true, @value.transaction_open?)
130
+ assert_nothing_raised { @value[1].gsub!(/second/, "fourth") }
131
+ assert_not_equal(@orig, @value)
132
+ assert_nothing_raised { @value.abort_transaction }
133
+ assert_equal(@orig, @value)
134
+ end
135
+ end